diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README b/README new file mode 100644 index 0000000..4579bd3 --- /dev/null +++ b/README @@ -0,0 +1,65 @@ +INTRODUCTION + +OBNC is a compiler for Niklaus Wirth's programming language Oberon. It translates Oberon modules into C code. The build command (obnc) invokes both the Oberon compiler (obnc-compile) and the host C compiler and sorts out all dependencies. + +OBNC follows POSIX standard. This implies that it should compile and run on a POSIX compatible operating system. + + +INSTALLATION + +1. Make sure you have Boehm-Demers-Weiser's garbage collector GC installed on your system. To use the basic library modules Input and XYplane you also need SDL (Simple DirectMedia Layer). On a Debian system you install these dependencies with the command + + apt install libgc-dev libsdl1.2-dev + +2. Compile OBNC with the command + + ./build + +By default OBNC is built to be installed in /usr/local. If you want to use installation directory D instead, add `--prefix=D' to the build command. For other build options, run `./build -h'. + +3. Optionally, run unit tests with the command + + ./test + +4. Install OBNC with the command + + ./install + +To undo the installation, run `./install u'. For other installation options, run `./build -h'. + + +COMMANDS + +bin/obnc + Oberon build tool + +bin/obnc-compile + Oberon-to-C compiler + +bin/obnc-path + Oberon module finder + +bin/obncdoc + Oberon documentation generator + + +DOCUMENTATION + +share/doc/obnc/oberon-report.html + Oberon language reference + +share/doc/obnc/obncdoc/ + Basic library modules + +share/man/man1/ + OBNC commands + + +LICENSE + +OBNC is released under the GNU General Public License, see file COPYING. + + +AUTHOR + +Karl Landstrom diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..78bc1ab --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.10.0 diff --git a/bin/micb b/bin/micb new file mode 100755 index 0000000..b1ccf74 --- /dev/null +++ b/bin/micb @@ -0,0 +1,279 @@ +#!/bin/sh + +#micb - MIASAP C Builder +# +#usage: micb MODULE.c +# +#Builds an executable with MODULE.c as entry point. Imported modules are compiled or recompiled as needed. For any module M, compiler, compiler flags, link flags and link libraries specific to M can be specified by setting the variables CC, CFLAGS, LDFLAGS and LDLIBS respectively in a file named M.env. + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +readonly selfDirPath="$(cd "$(dirname "$0")"; pwd -P)" +readonly micbIncludes="$selfDirPath/micb-includes" +readonly CC="${CC:-cc}" +readonly CFLAGS="${CFLAGS:-}" +readonly LDFLAGS="${LDFLAGS:-}" +readonly LDLIBS="${LDLIBS:-}" + +IncludeFiles() +{ + local filename="$1" + + local prefix="$(dirname "$filename")/" + prefix="${prefix#./}" + "$micbIncludes" < "$filename" | while read header; do echo "$prefix$header"; done +} + + +MapPut() +{ + local key="$1" + local value="$2" + local map="$3" + + if [ -z "$map" ]; then + echo "$key$(printf '\t')$value" + else + echo "$map" | \ + awk -v key="$key" -v value="$value" \ + 'BEGIN { FS = "\t"; keyFound = 0 } + $1 == key { print key"\t"value; keyFound = 1 } + $1 != key { print $0 } + END { if (! keyFound) { print key"\t"value } }' + fi +} + + +MapHas() +{ + local key="$1" + local map="$2" + + echo "$map" | grep -q "^$key$(printf '\t')" +} + + +MapAt() +{ + local key="$1" + local map="$2" + + echo "$map" | awk -v key="$key" 'BEGIN { FS = "\t" } $1 == key { print $2 }' +} + + +EnvValue() +{ + local ident="$1" + local envFile="$2" + + local quot="'" + local apos='"' + local value="$(awk -F "[$quot$apos=]+" -v ident="$ident" '$1 == ident { print $2 }' "$envFile")" + eval "value=\"$value\"" #expand embedded commands, like pkg-config + echo "$value" +} + + +Compile() +{ + local cFile="$1" + + local module="${cFile%.c}" + local moduleCC= + local moduleCFLAGS= + if [ -e "$module.env" ]; then + moduleCC="$(EnvValue CC "$module.env")" + moduleCFLAGS="$(EnvValue CFLAGS "$module.env")" + fi + if [ -z "$moduleCC" ]; then + moduleCC="$CC" + fi + local compileCommand="$moduleCC -c -o $module.o $CFLAGS $moduleCFLAGS $module.c" + compileCommand="$(echo "$compileCommand" | sed 's/ */ /g')" + echo "$compileCommand" + $compileCommand +} + + +UpdateObjectFile() +{ + local sourceFile="$1" + local newestFile="$2" + + local module="${sourceFile%.*}" + if [ "$sourceFile" = "$module.c" ]; then + if [ ! -e "$module.o" ] \ + || [ "$module.o" -ot "$newestFile" ] \ + || { [ -e "$module.env" ] && [ "$module.o" -ot "$module.env" ]; }; then + Compile "$sourceFile" + fi + fi +} + + +discoveredFiles="" #map with "filename" as key and "newest file in subgraph" as value + +Traverse() +{ + local filename="$1" + local nodePath="$2" #for detecting include cycles + local nodeHandler="$3" + + discoveredFiles="$(MapPut "$filename" "" "$discoveredFiles")" + + #traverse include files + local includeFile + local newestFileInSubgraph + local newestFile="$filename" + for includeFile in $(IncludeFiles "$filename"); do + if ! { echo "$nodePath" | grep -q -Fx "$includeFile"; }; then + if ! MapHas "$includeFile" "$discoveredFiles"; then + Traverse "$includeFile" "$nodePath\n$includeFile" "$nodeHandler" + fi + newestFileInSubgraph="$(MapAt "$includeFile" "$discoveredFiles")" + if [ "$newestFile" -ot "$newestFileInSubgraph" ]; then + newestFile="$newestFileInSubgraph" + fi + else + local cycle="$(echo "$nodePath" | tr '\n' ' ')$includeFile" + echo "$0: warning: include cycle found: $cycle" >&2 + fi + done + + discoveredFiles="$(MapPut "$filename" "$newestFile" "$discoveredFiles")" + + "$nodeHandler" "$filename" "$newestFile" + + #for a header file, also traverse the implementation file + local module="${filename%.*}" + if [ "${filename%.h}" != "$filename" ] && [ -e "$module.c" ] && ! MapHas "$module.c" "$discoveredFiles"; then + Traverse "$module.c" "$module.c" "$nodeHandler" + fi +} + + +NewestFile() +{ + local files="$1" + + local result="$(echo "$files" | head -n 1)" + for file in $files; do + if [ "$result" -ot "$file" ]; then + result="$file" + fi + done + echo "$result" +} + + +EnvFiles() +{ + local sourceFiles="$1" + + echo "$sourceFiles" \ + | while read srcFile; do + envFile="${srcFile%.*}.env" + if [ -e "$envFile" ]; then + echo "$envFile" + fi + done \ + | sort | uniq +} + + +OptionUnion() +{ + local ident="$1" + local envFiles="$2" + + echo "$envFiles" \ + | while read envFile; do + EnvValue "$ident" "$envFile" + done \ + | tr ' ' '\n' | sort | uniq | tr '\n' ' ' +} + + +Link() +{ + local objectFiles="$1" + local exeFile="$2" + + local objectFileArgs="$(echo "$objectFiles" | tr '\n' ' ')" + local sourceFiles="$(echo "$discoveredFiles" | awk 'BEGIN { FS = "\t" } { print $1 }')" + local envFiles="$(EnvFiles "$sourceFiles")" + local ldflags="$(OptionUnion LDFLAGS "$envFiles")" + local ldlibs="$(OptionUnion LDLIBS "$envFiles")" + + local linkCommand="$CC -o $exeFile $ldflags $LDFLAGS $objectFileArgs $ldlibs $LDLIBS" + linkCommand="$(echo "$linkCommand" | sed 's/ */ /g')" + echo "$linkCommand" + $linkCommand +} + + +Build() +{ + local cFile="$1" + + discoveredFiles="" + Traverse "$cFile" "$cFile" UpdateObjectFile + + local exeFile="${cFile%.c}" + local cFiles="$(echo "$discoveredFiles" | awk 'BEGIN { FS = "\t" } $1 ~ /\.c$/ { print $1 }')" + local objectFiles="$(echo "$cFiles" | sed 's/\.c$/.o/')" + local newestObjectFile="$(NewestFile "$objectFiles")" + + if [ ! -e "$exeFile" ] || [ "$exeFile" -ot "$newestObjectFile" ]; then + Link "$objectFiles" "$exeFile" + else + echo "$exeFile is up to date" + fi +} + + +Run() +{ + local syntaxError=false + + if [ "$#" = 1 ]; then + case $1 in + -*) syntaxError=true;; + *.c) + if [ -e "$1" ]; then + Build "$1" + else + echo "$0: no such file: $1" >&2 + false + fi;; + *) syntaxError=true + esac + else + syntaxError=true + fi + + if "$syntaxError"; then + echo "synopsis: $(basename "$0") MODULE.c" >&2 + false + fi +} + +Run "$@" diff --git a/bin/micb-includes b/bin/micb-includes new file mode 100755 index 0000000..fa5176c --- /dev/null +++ b/bin/micb-includes @@ -0,0 +1,61 @@ +#!/usr/bin/awk -f + +#micb-includes - MIASAP C Builder Include files +# +#usage: micb-includes +# +#Reads C code from stdin and writes include files to stdout. Only non-system header files are considered. + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +BEGIN { + insideComment = 0 +} + +(index($0, "/*") > 0) || (index($0, "*/") > 0) { + tail = $0 + insideString = 0 + if (insideComment) { + match(tail, /\*\//) + } else { + match(tail, /"|\/\*/) + } + while (RSTART > 0) { + delim = substr(tail, RSTART, RLENGTH) + if (delim == "\"") { + insideString = ! insideString + } else if (delim == "/*") { + insideComment = 1 + } else { + insideComment = 0 + } + tail = substr(tail, RSTART + RLENGTH) + if (insideString) { + match(tail, /"/) + } else if (insideComment) { + match(tail, /\*\//) + } else { + match(tail, /"|\/\*|\*\//) + } + } +} + +! insideComment && /^#include[ \t]+"/ { + split($0, fields, /"/) + print fields[2] +} diff --git a/bin/obnc b/bin/obnc new file mode 100755 index 0000000..3afb550 --- /dev/null +++ b/bin/obnc @@ -0,0 +1,495 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +export OBNC_PREFIX +export OBNC_LIBDIR + +readonly defaultPrefix="$(dirname "$(cd "$(dirname "$0")"; pwd -P)")" +readonly defaultLibDir=lib +readonly prefix="${OBNC_PREFIX:-$defaultPrefix}" +readonly libdir="${OBNC_LIBDIR:-$defaultLibDir}" + +readonly CC="${CC:-cc}" +readonly CFLAGS="${CFLAGS:-}" +readonly LDFLAGS="${LDFLAGS:-}" +readonly LDLIBS="${LDLIBS:-}" +readonly newline=' +' + +verbosity=0 #0 = quiet, 1 = progress, 2 = subcommands +discoveredFiles="" #map with "filename" as key and "symbol file changed" as value + +PathOfGeneratedFile() +{ + local file="$1" + local dirPath="$2" + + if [ -e "$dirPath/.obnc" ]; then + echo "$dirPath/.obnc/$file" + else + echo "$dirPath/$file" + fi +} + + +ImportedFiles() +{ + local obnFile="$1" + + local clientDir="$(dirname "$obnFile")" + local importedModules= + if [ ! -e "$obnFile" ]; then + local module="$(basename "${obnFile%.obn}")" + local impFile="$(PathOfGeneratedFile "$module.imp" "$(dirname "$obnFile")")" + if [ -e "$impFile" ]; then + importedModules="$(cat "$impFile")" + fi + else + importedModules="$(cd "$clientDir" && "$prefix/bin/obnc-compile" -l "$(basename "$obnFile")")" + fi + if [ -n "$importedModules" ]; then + ( + cd "$clientDir" + echo "$importedModules" \ + | while read -r module; do + if path="$("$prefix/bin/obnc-path" "$module" 2>/dev/null)"; then + modulePath="$path/$module.obn" + if [ "${path##/*}" = "" ]; then + echo "$modulePath" + else + modulePath="$clientDir/${modulePath#./}" + echo "${modulePath#./}" + fi + else + echo "obnc: error: Module imported by $obnFile not found: $module" >&2 + exit 1 + fi + done + ) + fi +} + + +MapPut() +{ + local key="$1" + local value="$2" + local map="$3" + + if [ -z "$map" ]; then + echo "$key$(printf '\t')$value" + else + echo "$map" | \ + awk -v key="$key" -v value="$value" \ + 'BEGIN { FS = "\t"; keyFound = 0 } + $1 == key { print key"\t"value; keyFound = 1 } + $1 != key { print $0 } + END { if (! keyFound) { print key"\t"value } }' + fi +} + + +MapHas() +{ + local key="$1" + local map="$2" + + echo "$map" | grep -q "^$key$(printf '\t')" +} + + +MapAt() +{ + local key="$1" + local map="$2" + + echo "$map" | awk -v key="$key" 'BEGIN { FS = "\t" } $1 == key { print $2 }' +} + + +RunCommand() +{ + if [ "$verbosity" = 2 ]; then + echo "$@" + fi + eval "$@" +} + + +CompileOberon() +{ + local module="$1" + local isEntryPoint="$2" + + #backup current symbol file + local symFile=".obnc/$module.sym" + if [ -e "$symFile" ]; then + cp "$symFile" "$symFile.bak" + else + mkdir -p .obnc + touch "$symFile.bak" + fi + + local entryPointOption="" + if "$isEntryPoint"; then + entryPointOption="-e" + fi + RunCommand "$prefix/bin/obnc-compile" "$entryPointOption" "$module.obn" +} + + +EnvValue() +{ + local ident="$1" + local envFile="$2" + + awk -F "[\"'=]+" -v ident="$ident" '$1 == ident { print $2 }' "$envFile" +} + + +CompileC() +{ + local module="$1" + + local cFile= + if [ -e "$module.c" ]; then + cFile="$module.c" + else + cFile=".obnc/$module.c" + fi + + local moduleCC= + local moduleCFLAGS= + if [ -e "$module.env" ]; then + moduleCC="$(EnvValue CC "$module.env")" + moduleCFLAGS="$(EnvValue CFLAGS "$module.env")" + fi + if [ -z "$moduleCC" ]; then + moduleCC="$CC" + fi + RunCommand "$moduleCC" -c -o ".obnc/$module.o" $CFLAGS $moduleCFLAGS "$cFile" +} + + +Compile() +{ + local module="$1" + local dir="$2" + local oberonCompilationNeeded="$3" + local isEntryPoint="$4" + + if [ "$verbosity" = 1 ]; then + echo "Compiling module $module" + elif [ "$verbosity" = 2 ]; then + printf "\nCompiling module %s:\n\n" "$module" + fi + + if [ "$verbosity" = 2 ] && [ "$dir" != "." ]; then + echo "cd $dir" + fi + cd "$dir" + + if "$oberonCompilationNeeded"; then + CompileOberon "$module" "$isEntryPoint" + fi + CompileC "$module" + + cd - >/dev/null +} + + +UpdateObjectFile() +{ + local obnFile="$1" + local importedSymFileChanged="$2" + local isEntryPoint="$3" + + local module="$(basename "$obnFile" .obn)" + local dir="$(dirname "$obnFile")" + + local symFile="$dir/.obnc/$module.sym" + local genCFile="$dir/.obnc/$module.c" + local hFile="$dir/.obnc/$module.h" + local dirFile="$dir/.obnc/$module.dir" + local dirName="$(basename "$(cd "$dir"; pwd)")" + local dirFileUpToDate=false + if [ -e "$dirFile" ] && [ "$(cat "$dirFile")" = "$dirName" ]; then + dirFileUpToDate=true + fi + local oberonCompilationNeeded=false + + if "$importedSymFileChanged" \ + || [ ! -e "$genCFile" ] || [ "$genCFile" -ot "$obnFile" ] \ + || { "$isEntryPoint" && [ -e "$symFile" ]; } \ + || { ! "$isEntryPoint" && { \ + [ ! -e "$symFile" ] || [ "$symFile" -ot "$obnFile" ] \ + || [ ! -e "$hFile" ] || [ "$hFile" -ot "$obnFile" ] \ + || ! "$dirFileUpToDate"; }; }; then + oberonCompilationNeeded=true + fi + + local objectFile="$dir/.obnc/$module.o" + local envFile="$dir/$module.env" + local nonGenCFile="$dir/$module.c" + local cCompilationNeeded=false + + if "$oberonCompilationNeeded" \ + || [ ! -e "$objectFile" ] \ + || { [ ! -e "$nonGenCFile" ] && [ "$objectFile" -ot "$genCFile" ]; } \ + || { [ -e "$nonGenCFile" ] && [ "$objectFile" -ot "$nonGenCFile" ]; } \ + || { [ -e "$envFile" ] && [ "$objectFile" -ot "$envFile" ]; }; then + cCompilationNeeded=true + fi + + if "$oberonCompilationNeeded" || "$cCompilationNeeded"; then + Compile "$module" "$dir" "$oberonCompilationNeeded" "$isEntryPoint" + fi + + if "$isEntryPoint"; then + rm -f "$dirFile" + elif ! "$dirFileUpToDate"; then + echo "$dirName" > "$dirFile" + fi +} + + +Traverse() +{ + local obnFile="$1" + local nodePath="$2" #for detecting import cycles + local isRoot="$3" + + discoveredFiles="$(MapPut "$obnFile" "" "$discoveredFiles")" + + #traverse imported files + local importedFiles + local importedFile + local importedSymFileChanged=false + importedFiles="$(ImportedFiles "$obnFile")" + for importedFile in $importedFiles; do + if ! { echo "$nodePath" | grep -q -Fx "$importedFile"; }; then + if ! MapHas "$importedFile" "$discoveredFiles"; then + Traverse "$importedFile" "$nodePath${newline}$importedFile" false + fi + if [ "$(MapAt "$importedFile" "$discoveredFiles")" = true ]; then + importedSymFileChanged=true + fi + else + local cycle="$(echo "$nodePath" | tr '\n' ' ') $importedFile" + echo "obnc: error: import cycle found: $cycle" >&2 + exit 1 + fi + done + + local symFileChanged=false + + if [ -e "$obnFile" ]; then + UpdateObjectFile "$obnFile" "$importedSymFileChanged" "$isRoot" + + #find out if the symbol file has changed + local dir="$(dirname "$obnFile")" + local module="$(basename "$obnFile" .obn)" + local symFile="$dir/.obnc/$module.sym" + if [ -e "$symFile.bak" ] && ! cmp -s "$symFile" "$symFile.bak"; then + symFileChanged=true + fi + rm -f "$symFile.bak" + fi + + discoveredFiles="$(MapPut "$obnFile" "$symFileChanged" "$discoveredFiles")" +} + + +ObjectFiles() +{ + local obnFiles="$1" + + echo "$obnFiles" | \ + while read -r file; do + objFile="$(PathOfGeneratedFile "$(basename "$file" .obn).o" "$(dirname "$file")")" + echo "${objFile#./}" + done +} + + +NewestFile() +{ + local files="$1" + + local result="$(echo "$files" | head -n 1)" + for file in $files; do + if [ "$result" -ot "$file" ]; then + result="$file" + fi + done + echo "$result" +} + + +EnvFiles() +{ + local objectFiles="$1" + + echo "$objectFiles" | \ + while read -r objFile; do + dir="$(dirname "$objFile")" + if [ "$(basename "$dir")" = .obnc ]; then + srcDir="$(dirname "$dir")" + else + srcDir="$dir" + fi + module="$(basename "$objFile" .o)" + envFile="$srcDir/$module.env"; + if [ -e "$envFile" ]; then + echo "$envFile" + fi + done +} + + +OptionUnion() +{ + local ident="$1" + local envFiles="$2" + + echo "$envFiles" \ + | while read -r envFile; do + EnvValue "$ident" "$envFile" + done \ + | tr ' ' '\n' | sort | uniq | tr '\n' ' ' +} + + +Link() +{ + local objectFiles="$1" + local exeFile="$2" + + local envFiles="$(EnvFiles "$objectFiles")" + local ldflags="$(OptionUnion LDFLAGS "$envFiles")" + local ldlibs="$(OptionUnion LDLIBS "$envFiles")" + local objectFileArgs="$(echo "$objectFiles" | tr '\n' ' ')" + + if [ "$verbosity" = 1 ]; then + echo "Linking modules" + elif [ "$verbosity" = 2 ]; then + printf "\nLinking modules:\n\n" + fi + RunCommand "$CC" -o "$exeFile" "$ldflags" "$LDFLAGS" "$objectFileArgs" "$ldlibs" "$LDLIBS" +} + + +Build() +{ + local obnFile="$1" + + discoveredFiles="" + Traverse "$obnFile" "$obnFile" true + + local exeFile="${obnFile%.obn}" + local obnFiles="$(echo "$discoveredFiles" | awk 'BEGIN { FS = "\t" } { print $1 }')" + local objectFiles="$(ObjectFiles "$obnFiles")${newline}$prefix/$libdir/obnc/OBNC.o" + local newestObjectFile="$(NewestFile "$objectFiles")" + + if [ ! -e "$exeFile" ] || [ "$exeFile" -ot "$newestObjectFile" ]; then + Link "$objectFiles" "$exeFile" + else + echo "$exeFile is up to date" + fi +} + + +PrintHelp() +{ + printf "obnc - build executable for Oberon module\n" + printf "\n" + printf "usage:\n" + printf "\tobnc [-v | -V] MODULE.obn\n" + printf "\tobnc (-h | -v)\n" + printf + printf "\t-h\tdisplay help and exit\n" + printf "\t-v\tlog compiled modules or display version and exit\n" + printf "\t-V\tlog compiler and linker commands\n" + +} + + +PrintVersion() +{ + "$prefix/bin/obnc-compile" -v +} + + +ExitInvalidCommand() +{ + echo "obnc: invalid command. Try 'obnc -h' for more information." >&2 + exit 1 +} + + +Run() +{ + local helpWanted=false + local vSet=false + local inputFile="" + local arg + + for arg in "$@"; do + case "$arg" in + -h) + helpWanted=true;; + -v) + vSet=true;; + -V) + verbosity=2;; + -*) + ExitInvalidCommand;; + *.obn) + if [ -z "$inputFile" ]; then + inputFile="$arg" + else + ExitInvalidCommand + fi;; + *) + ExitInvalidCommand + esac + done + + if "$helpWanted"; then + PrintHelp + elif [ -n "$inputFile" ]; then + if "$vSet"; then + verbosity=1 + fi + if [ -e "$inputFile" ]; then + Build "$inputFile" + else + echo "obnc: no such file: $inputFile" >&2 + exit 1 + fi + elif "$vSet"; then + PrintVersion + else + ExitInvalidCommand + fi +} + +Run "$@" diff --git a/bin/obnc-test b/bin/obnc-test new file mode 100755 index 0000000..f9e9ae3 --- /dev/null +++ b/bin/obnc-test @@ -0,0 +1,90 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +readonly selfDirPath="$(cd "$(dirname "$0")"; pwd -P)" +readonly packagePath="$(dirname "$selfDirPath")" + +EchoAndRun() +{ + echo "$@" + eval "$@" +} + + +CleanUp() +{ + find "$packagePath/tests/obnc" -name .obnc -type d | while read -r dir; do + rm -r "$dir" + done + find "$packagePath/tests/obnc" -name "*.obn" -type f | while read -r file; do + rm -f "${file%.obn}" + done +} + +CleanUp + +dir="$packagePath/tests/obnc/passing" +EchoAndRun cd "$dir" +for module in *.obn; do + if EchoAndRun "$packagePath/bin/obnc" "$module"; then + if ! EchoAndRun "./${module%.obn}"; then + printf "\nPositive test failed: %s\n\n" "$dir/$module">&2 + exit 1 + fi + else + printf "\nPositive test failed: %s\n\n" "$dir/$module" >&2 + exit 1 + fi +done + +dir="$packagePath/tests/obnc/failing-at-compile-time" +EchoAndRun cd "$dir" +for module in *.obn; do + if [ "$module" != A.obn ] && [ "$module" != B.obn ]; then + echo "$packagePath/bin/obnc-compile" "$module" + if "$packagePath/bin/obnc-compile" "$module" 2>/dev/null; then + printf "\nNegative test failed: %s\n\n" "$dir/$module" >&2 + exit 1 + elif [ "$?" -ne 1 ]; then + printf "\nNegative test crashed: %s\n\n" "$dir/$module" >&2 + exit 1 + fi + fi +done + +dir="$packagePath/tests/obnc/failing-at-runtime" +EchoAndRun cd "$dir" +for module in *.obn; do + if [ "$module" != A.obn ] && [ "$module" != B.obn ]; then + if EchoAndRun "$packagePath/bin/obnc" "$module"; then + echo "./${module%.obn}" + if ( "./${module%.obn}" || false ) >/dev/null 2>&1; then + printf "\nNegative test failed: %s\n\n" "$dir/$module" >&2 + exit 1 + fi + else + printf "\nNegative test failed: %s\n\n" "$dir/$module" >&2 + exit 1 + fi + fi +done + +CleanUp diff --git a/bin/obncdoc b/bin/obncdoc new file mode 100755 index 0000000..dd28f84 --- /dev/null +++ b/bin/obncdoc @@ -0,0 +1,225 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +readonly defaultPrefix="$(dirname "$(cd "$(dirname "$0")"; pwd -P)")" +readonly prefix="${OBNC_PREFIX:-$defaultPrefix}" + +readonly outputDir="obncdoc" +readonly indexFile="$outputDir/index.html" +readonly cssFile="$outputDir/style.css" +readonly libraryTitle="Library $(basename "$PWD")" + +readonly extractionCommand="$prefix/bin/obncdoc-extract" +readonly markupCommand="$prefix/bin/obncdoc-markup" + +readonly css='body { + font-family: sans-serif; + margin: 1em; +} + +pre { + font-family: inherit; + + -moz-tab-size: 5; + -o-tab-size: 5; + tab-size: 5; + + white-space: pre-wrap; + white-space: -moz-pre-wrap; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + word-wrap: break-word; +} + +pre em { + font-style: normal; + font-weight: bold; +} + +pre .comment { + color: #800000; +} + +pre .string { + color: #767676; +}' + + +PrintHtmlHeader() +{ + local title="$1" + + local cssHref="$(basename "$cssFile")" + echo " + + + + $title + + + " +} + + +PrintHtmlFooter() +{ + echo '' +} + + +CreateHtmlDefinition() +{ + local module="$1" + local txtDefFile="$2" + local outputFile="$3" + + PrintHtmlHeader "DEFINITION $module" > "$outputFile" + local indexHref="$(basename "$indexFile")" + { + echo "

$libraryTitle

" + echo "
"
+		"$markupCommand" < "$txtDefFile"
+		echo "
" + PrintHtmlFooter + } >> "$outputFile" +} + + +CreateDefinitions() +{ + local sourceFiles="$(find . -maxdepth 1 -name '*.obn' | sort)" + echo "$sourceFiles" | while read -r sourceFile; do + module="$(basename "$sourceFile" .obn)" + textOutputFile="$outputDir/$module.def" + htmlOutputFile="$textOutputFile.html" + if [ ! -f "$textOutputFile" ] || [ "$textOutputFile" -ot "$sourceFile" ]; then + "$extractionCommand" < "$sourceFile" > "$textOutputFile" + if [ "$(wc -l < "$textOutputFile")" -gt 2 ]; then + CreateHtmlDefinition "$module" "$textOutputFile" "$htmlOutputFile" + else + #delete empty definition + rm -f "$textOutputFile" + fi + fi + done +} + + +DeleteOrphanedDefinitions() +{ + for def in $outputDir/*.def; do + local module="$(basename "$def" .def)" + local source="$module.obn" + if [ ! -f "$source" ]; then + rm "$def" + rm -f "$def.html" + fi + done +} + + +CreateIndex() +{ + PrintHtmlHeader "$libraryTitle" > "$indexFile" + echo "

Library Index

" >> "$indexFile" + local definitions="$(find "$outputDir"/*.def)" + if [ -n "$definitions" ]; then + echo "
" >> "$indexFile"
+		echo "$definitions" | while read -r def; do
+			#list only non-empty definitions
+			if [ "$(wc -l < "$def")" -gt 2 ]; then
+				module="$(basename "$def" .def)"
+				echo "DEFINITION $module" >> "$indexFile"
+			fi
+		done
+		echo "
" >> "$indexFile" + else + echo "

(Empty)

" >> "$indexFile" + fi + PrintHtmlFooter >> "$indexFile" +} + + +PrintHelp() +{ + printf "obncdoc - extract exported features from Oberon modules\n" + printf "\n" + printf "usage:\n" + printf "\tobncdoc [-h | -v]\n" + printf "\n" + printf "\t-h\tdisplay help and exit\n" + printf "\t-v\tdisplay version and exit\n" + +} + + +PrintVersion() +{ + "$prefix/bin/obnc-compile" -v +} + + +ExitInvalidCommand() +{ + echo "obncdoc: invalid command. Try 'obncdoc -h' for more information." >&2 + exit 1 +} + + +Run() +{ + local helpWanted=false + local versionWanted=false + local arg= + + for arg in "$@"; do + case "$arg" in + -h) + helpWanted=true;; + -v) + versionWanted=true;; + *) + ExitInvalidCommand + esac + done + + if "$helpWanted"; then + PrintHelp + elif "$versionWanted"; then + PrintVersion + else + local sourceFiles="$(ls ./*.obn 2> /dev/null)" + if [ -n "$sourceFiles" ]; then + mkdir -p "$outputDir" + CreateDefinitions + DeleteOrphanedDefinitions + CreateIndex + if [ ! -f "$cssFile" ]; then + echo "$css" > "$cssFile" + fi + else + echo "$0: error: no oberon files" >&2 + exit 1 + fi + fi +} + +Run "$@" diff --git a/bin/obncdoc-extract b/bin/obncdoc-extract new file mode 100755 index 0000000..18903c9 --- /dev/null +++ b/bin/obncdoc-extract @@ -0,0 +1,272 @@ +#!/usr/bin/awk -f + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +#NOTE: nested comments and string literals containing comment delimiters are not accounted for + +function EndsInsideComment(line, startsInsideComment) +{ + return (startsInsideComment || (index(line, "(*") > 0)) && (index(line, "*)") == 0) +} + + +function EndsInsideRecord(line, parts) #local variables from second parameter +{ + split(line, parts, "\"|\\(\\*") + return (match(parts[1], "[^A-Za-z0-9]RECORD([^A-Za-z0-9]|$)") > 0) && (match(parts[1], "[^A-Za-z0-9]END[ \t]*;") == 0) +} + + +function RecordQualidents(line, i, regex, pos, done, matchedString) #local variables from second parameter +{ + regex = qualidentRegex"|\"|\\(\\*" + i = 1 + done = 0 + pos = match(line, regex) + while ((i <= 100) && ! done && (RSTART > 0)) { + matchedString = substr(line, pos, RLENGTH) + if (matchedString == "\"") { + pos += RLENGTH + pos += index(substr(line, pos), "\"") #skip string + pos++ + pos += match(substr(line, pos), regex) + } else if (matchedString == "(*") { + done = 1 + } else { + match(matchedString, identRegex) + publicImports[substr(matchedString, 1, RLENGTH)] = "" + pos += RLENGTH + pos += match(substr(line, pos), regex) - 1 + } + i++ + } +} + + +function WithoutExportMarks(line, left, right) #local variables from parameter two +{ + match(line, "[=:;(]|\\(\\*") + if (RSTART > 0) { + left = substr(line, 1, RSTART - 1) + right = substr(line, RSTART) + } else { + left = line + right = "" + } + gsub("\\*", "", left) + return left""right +} + + +function ExportedParts(line) +{ + line = WithoutExportMarks(line) + sub("\\(\\*([^*].*|$)", "", line) + sub("\\(\\*\\*", "(*", line) + return line +} + +BEGIN { + identRegex = "[A-Za-z][A-Za-z0-9]*" + qualidentRegex = identRegex"\\."identRegex + exportedIdentRegex = identRegex"[ \t]*\\*" + savedOutput = "" #output after import list + insideExportedRecord = 0 + exportedFieldFound = 0 + moduleIdent = "" + currentSection = "" + insideComment = 0 + insideExportedComment = 0 + exportedFeatureFound["CONST"] = 0 + exportedFeatureFound["TYPE"] = 0 + exportedFeatureFound["VAR"] = 0 + split("", aliasedImports) #initialize empty array + split("", publicImports) #initialize empty array +} + +! insideComment && ($1 == "MODULE") { + if (substr($2, length($2), 1) == ";") { + moduleIdent = substr($2, 1, length($2) - 1) + } else { + moduleIdent = $2 + } + line = $0 + sub(/MODULE/, "DEFINITION", line) + insideComment = EndsInsideComment(line, 0) + print ExportedParts(line) + next +} + +! insideComment && ($1 == "IMPORT") { + currentSection = "IMPORT" +} + +! insideComment && (currentSection == "IMPORT") { + #add aliased modules to aliasedImports + aliasedImportRegex = "[A-Za-z0-9]+[ \t]*:=[ \t]*[A-Za-z0-9]+|\\(\\*" + i = 1 + done = 0 + oldPos = 0 + newPos = match($0, aliasedImportRegex) + while ((i <= 100) && ! done && (newPos > oldPos)) { + str = substr($0, newPos, RLENGTH) + if (str == "(*") { + done = 1 + } else { + split(str, import, "[ \t:=]+") + aliasedImports[import[1]] = import[2] + oldPos = newPos + newPos += RLENGTH + newPos += match(substr($0, newPos), aliasedImportRegex) - 1 + } + i++ + } + + #are we on the last import line? + match($0, ";|\\(\\*") + if ((RSTART > 0) && (substr($0, RSTART, RLENGTH) == ";")) { + currentSection = "" + } + + next +} + +! insideComment && (currentSection != "PROCEDURE") && ($1 ~ "CONST|TYPE|VAR") { + currentSection = $1 + if (match($2$3, "^"exportedIdentRegex)) { + RecordQualidents($0) + insideComment = EndsInsideComment($0, 0) + insideExportedRecord = EndsInsideRecord($0) + savedOutput = savedOutput ExportedParts($0) "\n" + exportedFeatureFound[$1] = 1 + } + next +} + +! insideComment && (currentSection != "PROCEDURE") && insideExportedRecord && ($1 ~ "^END[ \t]*;") { + line = $0 + if (! exportedFieldFound) { + sub("^[ \t]*", " ", line) + } + insideExportedRecord = 0 + exportedFieldFound = 0 + savedOutput = savedOutput ExportedParts(line) "\n" + next +} + +! insideComment && ($1 == "PROCEDURE") && ($2 ~ "^[A-Za-z]") { + currentSection = "PROCEDURE" +} + +! insideComment && ($0 ~ "^[ \t]*(PROCEDURE[ \t]+)?"exportedIdentRegex) { + if (match(currentSection, "CONST|TYPE|VAR") && ! exportedFeatureFound[currentSection]) { + savedOutput = savedOutput "\n\t" currentSection "\n" + exportedFeatureFound[currentSection] = 1 + } + RecordQualidents($0) + if (insideExportedRecord) { + if (! exportedFieldFound) { + savedOutput = savedOutput "\n" + exportedFieldFound = 1 + } + } else { + insideExportedRecord = EndsInsideRecord($0) + } + insideComment = EndsInsideComment($0, 0) + if ($1 == "PROCEDURE") { + savedOutput = savedOutput "\n" + } + savedOutput = savedOutput ExportedParts($0) + if (! (insideExportedRecord && ! exportedFieldFound)) { + savedOutput = savedOutput "\n" + } + next +} + +! insideComment && ($1 == "END") && ($2$3 ~ "^"moduleIdent"\\.") { + insideComment = EndsInsideComment($0, 0) + if (savedOutput != "") { + savedOutput = savedOutput "\n" + } + savedOutput = savedOutput ExportedParts($0) "\n" + next +} + +! insideComment && ($1 ~ "^\\(\\*\\*?") { + insideComment = EndsInsideComment($0, 0) + if (match($1, "^\\(\\*\\*")) { + if (match(currentSection, "CONST|TYPE|VAR") && ! exportedFeatureFound[currentSection]) { + savedOutput = savedOutput "\n\t" currentSection "\n" + exportedFeatureFound[currentSection] = 1 + } + line = $0 + sub(/\(\*\*/, "(*", line) + if (insideComment) { + insideExportedComment = 1 + } + if (currentSection == "") { + print line + } else { + if (match(savedOutput, "\\*\\)[ \t\n]*$")) { + #add blank line between comments + savedOutput = savedOutput "\n" + } + savedOutput = savedOutput line "\n" + } + } + next +} + +insideComment { + if (insideExportedComment) { + if (currentSection == "") { + print $0 + } else { + savedOutput = savedOutput $0 "\n" + } + } + insideComment = EndsInsideComment($0, 1) + if (! insideComment) { + insideExportedComment = 0 + } +} + +END { + #print import list + n = 0 + for (key in publicImports) { + n++ + } + if (n > 0) { + printf "\n\tIMPORT" + i = 1 + for (key in publicImports) { + if (i > 1) { + printf "," + } + printf " %s", key + if (aliasedImports[key] != "") { + printf " := %s", aliasedImports[key] + } + i++ + } + print ";" + } + + print substr(savedOutput, 1, length(savedOutput) - 1) +} diff --git a/bin/obncdoc-index b/bin/obncdoc-index new file mode 100755 index 0000000..0da09a9 --- /dev/null +++ b/bin/obncdoc-index @@ -0,0 +1,43 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +echo ' + + + + Library Index + + + +

Library Index

+ +
'
+		
+for file in *; do
+	if [ -e "$file/index.html" ]; then
+		echo "Library $file"
+	fi
+done
+		
+echo '
+ +' + diff --git a/bin/obncdoc-markup b/bin/obncdoc-markup new file mode 100755 index 0000000..1a3082d --- /dev/null +++ b/bin/obncdoc-markup @@ -0,0 +1,85 @@ +#!/usr/bin/awk -f + +#markup definition file + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +BEGIN { + commentLevel = 0 +} + +{ + gsub(/&/, "\\&", $0) + gsub(//, "\\>", $0) +} + +(commentLevel == 0) && (($1 == "DEFINITION") || ($1 == "PROCEDURE")) { + split($2, parts, "[ (;]") + ident = parts[1] + sub($1"[ \t]+"ident, $1" "ident"", $0) +} + +((commentLevel == 0) && match($0, /"|\(\*/)) || ((commentLevel > 0) && match($0, /\(\*|\*\)/)) { + insideString = 0 + head = "" + tail = $0 + do { + delim = substr(tail, RSTART, RLENGTH) + head = head""substr(tail, 1, RSTART - 1) + if (delim == "\"") { + if (! insideString) { + head = head"\"" + insideString = 1 + } else { + head = head"\"" + insideString = 0 + } + } else if (delim == "(*") { + if (commentLevel == 0) { + head = head"(*" + } else { + head = head"(*" + } + commentLevel++ + } else if (delim == "*)") { + if (commentLevel == 1) { + head = head"*)" + } else { + head = head"*)" + } + commentLevel-- + } else { + print "obncdoc-markup: invalid match" > "/dev/stderr" + exit(1) + } + tail = substr(tail, RSTART + RLENGTH) + if (insideString) { + match(tail, /"/) + } else if (commentLevel > 0) { + match(tail, /\(\*|\*\)/) + } else { + match(tail, /"|\(\*|\*\)/) + } + } while (RSTART > 0) + $0 = head""tail +} + +{ + print $0 +} diff --git a/bin/obncdoc-markup-test b/bin/obncdoc-markup-test new file mode 100755 index 0000000..d14202b --- /dev/null +++ b/bin/obncdoc-markup-test @@ -0,0 +1,38 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +readonly selfDirPath="$(cd "$(dirname "$0")"; pwd -P)" +readonly packagePath="$(dirname "$selfDirPath")" + +readonly tmpdir="${TMPDIR:-/tmp}" + +readonly input="$packagePath/tests/obncdoc/Test.def" +readonly output="$tmpdir/Test.def.html.$$" +readonly expectedOutput="$packagePath/tests/obncdoc/Test.def.html" + +"$packagePath/bin/obncdoc-markup" <"$input" >"$output" + +if ! cmp -s "$output" "$expectedOutput"; then + echo "$(basename "$0") failed: files differ: $output $expectedOutput" >&2 + exit 1 +else + rm -f "$output" +fi diff --git a/build b/build new file mode 100755 index 0000000..77c55ab --- /dev/null +++ b/build @@ -0,0 +1,250 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +readonly selfDirPath="$(cd "$(dirname "$0")"; pwd -P)" +prefix="/usr/local" +libdir="lib" +useLongInt=false +useLongReal=false + +EchoAndRun() +{ + echo "$@" + eval "$@" +} + + +BuildCSource() +{ + EchoAndRun cd "$selfDirPath/src" + + if [ ! -e lex.yy.c ] || [ ! -e lex.yy.h ] || [ lex.yy.c -ot Oberon.l ] || [ lex.yy.h -ot Oberon.l ]; then + EchoAndRun lex --header-file=lex.yy.h Oberon.l + else + echo "lex.yy.c and lex.yy.h is up to date" + fi + + if [ ! -e y.tab.c ] || [ ! -e y.tab.h ] || [ y.tab.c -ot Oberon.y ]; then + local tmpdir="${TMPDIR:-/tmp}" + local bakFile="$tmpdir/y.tab.h.$$" + + if [ -e y.tab.h ]; then + cp -p y.tab.h "$bakFile" + trap "rm '$bakFile'" EXIT + fi + + EchoAndRun yacc -d -t Oberon.y + + #preserve timestamp of y.tab.h if it is unchanged + if cmp -s y.tab.h "$bakFile"; then + cp -p "$bakFile" y.tab.h + fi + else + echo "y.tab.c and y.tab.h is up to date" + fi + + cd "$selfDirPath" +} + + +Build() +{ + if [ -e "VERSION" ]; then + local version="$(cat VERSION)" + else + local version= + fi + + #generate configuration file for install script + if [ -e CONFIG ]; then + cp CONFIG CONFIG.bak + trap "rm $selfDirPath/CONFIG.bak" EXIT + fi + { + echo "prefix=$prefix" + echo "libdir=$libdir" + echo "useLongInt=$useLongInt" + echo "useLongReal=$useLongReal" + echo "version=$version" + } > CONFIG + + if ! { [ -e CONFIG.bak ] && cmp -s CONFIG CONFIG.bak; }; then + #generate configuration header files + { + echo "#ifndef CONFIG_H" + echo "#define CONFIG_H" + echo + printf "#define CONFIG_DEFAULT_PREFIX \"%s\"\n" "$prefix" + printf "#define CONFIG_DEFAULT_LIBDIR \"%s\"\n" "$libdir" + printf "#define CONFIG_VERSION \"%s\"\n" "$version" + echo + echo "const char *Config_Prefix(void);" + echo "const char *Config_LibDir(void);" + echo + echo "#endif" + } > src/Config.h + { + echo "#ifndef OBNC_CONFIG_H" + echo "#define OBNC_CONFIG_H" + echo + if "$useLongInt"; then + echo "#define OBNC_CONFIG_USE_LONG_INT" + echo + fi + if "$useLongReal"; then + echo "#define OBNC_CONFIG_USE_LONG_REAL" + echo + fi + echo "#endif" + } > lib/obnc/OBNCConfig.h + fi + + BuildCSource + + #build compiler + EchoAndRun cd "$selfDirPath/src" + env CFLAGS="${CFLAGS:-}" "$selfDirPath/bin/micb" obnc-compile.c + if [ ! -e "$selfDirPath/bin/obnc-compile" ] || [ "$selfDirPath/bin/obnc-compile" -ot obnc-compile ]; then + cp obnc-compile "$selfDirPath/bin" + fi + + #build core library module OBNC + EchoAndRun cd "$selfDirPath/lib/obnc" + "$selfDirPath/bin/micb" OBNCTest.c + + #build path finder + EchoAndRun cd "$selfDirPath/src" + env CFLAGS="${CFLAGS:-}" "$selfDirPath/bin/micb" obnc-path.c + if [ ! -e "$selfDirPath/bin/obnc-path" ] || [ "$selfDirPath/bin/obnc-path" -ot obnc-path ]; then + cp obnc-path "$selfDirPath/bin" + fi + + cd "$selfDirPath" +} + + +Clean() +{ + rm -f CONFIG + rm -f CONFIG.bak + + rm -f bin/obnc-compile + rm -f bin/obnc-path + + rm -f src/obnc-compile + rm -f src/obnc-path + rm -f src/?*Test + rm -f src/*.o + rm -f src/Config.h + + rm -f lib/obnc/?*Test + rm -f lib/obnc/*.o + rm -fr lib/obnc/.obnc + rm -f lib/obnc/OBNCConfig.h +} + + +CleanAll() +{ + Clean + rm -f src/lex.yy.[ch] + rm -f src/y.tab.[ch] +} + + +PrintHelp() +{ + echo "usage: " + printf "\tbuild [c-source | clean | clean-all] [--libdir=LIBDIR] [--prefix=PREFIX] [--use-long-int] [--use-long-real]\n" + printf "\tbuild -h\n" + echo + printf "\tc-source\tbuild only Yacc and Lex C source files\n" + printf "\tclean\t\tdelete all generated files except Yacc and Lex C files\n" + printf "\tclean-all\tdelete all generated files\n" + printf "\t--libdir\tlibrary installation directory instead of lib\n" + printf "\t--prefix\ttoplevel installation directory instead of /usr/local\n" + printf "\t--use-long-int\tuse \`long' type specifier for INTEGER and SET\n" + printf "\t--use-long-real\tuse \`long' type specifier for REAL\n" + printf "\t-h\t\tdisplay help and exit\n" +} + + +ExitInvalidCommand() +{ + echo "invalid command. Try 'build -h' for more information." >&2 + exit 1 +} + + +Run() +{ + local helpWanted=false + local action= + local arg= + + for arg in "$@"; do + case "$arg" in + c-source) + action=c-source;; + clean) + action=clean;; + clean-all) + action=clean-all;; + --libdir=*) + libdir="${arg#--libdir=}" + if [ "${libdir#*/}" != "$libdir" ]; then + echo "libdir must be a directory name, not a path: $prefix" >&2 + exit 1 + fi;; + --prefix=*) + prefix="${arg#--prefix=}" + if [ "${prefix#/}" = "$prefix" ]; then + echo "prefix must be an absolute path: $prefix" >&2 + exit 1 + fi;; + --use-long-int) + useLongInt=true;; + --use-long-real) + useLongReal=true;; + -h) + helpWanted=true;; + *) + ExitInvalidCommand + esac + done + + if "$helpWanted"; then + PrintHelp + else + case "$action" in + c-source) + BuildCSource;; + clean) + Clean;; + clean-all) + CleanAll;; + *) + Build + esac + fi +} + +Run "$@" diff --git a/install b/install new file mode 100755 index 0000000..32e87df --- /dev/null +++ b/install @@ -0,0 +1,198 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +readonly selfDirPath="$(cd "$(dirname "$0")"; pwd -P)" +readonly prefix="$(awk -F '=' '$1 == "prefix" { print $2; }' CONFIG)" +readonly libdir="$(awk -F '=' '$1 == "libdir" { print $2; }' CONFIG)" +destdir= + +readonly scripts="obnc obncdoc obncdoc-extract obncdoc-index obncdoc-markup" +readonly basicModules="Files In Input Math Out Strings XYplane" +readonly docFiles="oberon-report.html" +readonly man1Files="obnc.1 obnc-compile.1 obncdoc.1 obnc-path.1" + +EchoAndRun() +{ + echo "$@" + eval "$@" +} + + +Install() +{ + #install core files + EchoAndRun mkdir -p "$destdir$prefix/bin" + EchoAndRun cp "bin/obnc-compile" "$destdir$prefix/bin" + EchoAndRun cp "bin/obnc-path" "$destdir$prefix/bin" + local file= + for file in $scripts; do + EchoAndRun sed -e '"s|^\(readonly defaultPrefix=\).*$|\1'"'$prefix'"'|"' \ + -e '"s|^\(readonly defaultLibDir=\).*$|\1'"'$libdir'"'|"' \ + '"bin/'$file'"' \> '"'$destdir$prefix/bin/$file'"' + EchoAndRun chmod +x "$destdir$prefix/bin/$file" + done + EchoAndRun mkdir -p "$destdir$prefix/include/obnc" + EchoAndRun cp "lib/obnc/OBNCConfig.h" "$destdir$prefix/include/obnc" + EchoAndRun cp "lib/obnc/OBNC.h" "$destdir$prefix/include/obnc" + EchoAndRun mkdir -p "$destdir$prefix/$libdir/obnc" + EchoAndRun cp "lib/obnc/OBNC.o" "$destdir$prefix/$libdir/obnc" + EchoAndRun cp "lib/obnc/OBNC.env" "$destdir$prefix/$libdir/obnc" + + #install basic library + rm -rf "lib/obnc/.obnc" + local module= + for module in $basicModules; do + #allow installation to proceed even if some optional libraries (like SDL) are missing + if (cd "lib/obnc" && env OBNC_PREFIX="$destdir$prefix" CFLAGS="-I$destdir$prefix/include" "$destdir$prefix/bin/obnc" "${module}Test.obn"); then + EchoAndRun cp "lib/obnc/.obnc/$module.h" "$destdir$prefix/include/obnc" + EchoAndRun cp "lib/obnc/.obnc/$module.o" "$destdir$prefix/$libdir/obnc" + EchoAndRun cp "lib/obnc/.obnc/$module.sym" "$destdir$prefix/$libdir/obnc" + if [ -e "lib/obnc/.obnc/$module.imp" ]; then + EchoAndRun cp "lib/obnc/.obnc/$module.imp" "$destdir$prefix/$libdir/obnc" + fi + if [ -e "lib/obnc/$module.env" ]; then + EchoAndRun cp "lib/obnc/$module.env" "$destdir$prefix/$libdir/obnc" + fi + fi + done + + #install documentation + EchoAndRun mkdir -p "$destdir$prefix/share/doc/obnc" + for file in $docFiles; do + EchoAndRun cp "share/doc/obnc/$file" "$destdir$prefix/share/doc/obnc" + done + (cd "lib/obnc" && ../../bin/obncdoc) + EchoAndRun mkdir -p "$destdir$prefix/share/doc/obnc/obncdoc/obnc" + for file in "lib/obnc/obncdoc"/*; do + EchoAndRun cp "$file" "$destdir$prefix/share/doc/obnc/obncdoc/obnc" + done + EchoAndRun cd "$destdir$prefix/share/doc/obnc/obncdoc" + EchoAndRun "$selfDirPath/bin/obncdoc-index" \> index.html + EchoAndRun cp obnc/style.css . + cd - >/dev/null + + #install man pages + EchoAndRun mkdir -p "$destdir$prefix/share/man/man1" + for file in $man1Files; do + EchoAndRun cp "share/man/man1/$file" "$destdir$prefix/share/man/man1" + done +} + + +Uninstall() +{ + #delete executables + EchoAndRun rm -f "$destdir$prefix/bin/obnc-compile" + EchoAndRun rm -f "$destdir$prefix/bin/obnc-path" + local file= + for file in $scripts; do + EchoAndRun rm -f "$destdir$prefix/bin/$file" + done + + #delete library files + local module= + for module in $basicModules; do + EchoAndRun rm -f "$destdir$prefix/include/obnc/$module.h" + EchoAndRun rm -f "$destdir$prefix/$libdir/obnc/$module.o" + EchoAndRun rm -f "$destdir$prefix/$libdir/obnc/$module.sym" + EchoAndRun rm -f "$destdir$prefix/$libdir/obnc/$module.imp" + EchoAndRun rm -f "$destdir$prefix/$libdir/obnc/$module.env" + done + EchoAndRun rm -f "$destdir$prefix/include/obnc/OBNC.h" + EchoAndRun rm -f "$destdir$prefix/include/obnc/OBNCConfig.h" + EchoAndRun rm -f "$destdir$prefix/$libdir/obnc/OBNC.o" + EchoAndRun rm -f "$destdir$prefix/$libdir/obnc/OBNC.env" + + #delete documentation + for file in $docFiles; do + EchoAndRun rm -f "$destdir$prefix/share/doc/obnc/$file" + done + EchoAndRun rm -f "$destdir$prefix/share/doc/obnc/obncdoc/obnc/"* + EchoAndRun rm -f "$destdir$prefix/share/doc/obnc/obncdoc/index.html" + EchoAndRun rm -f "$destdir$prefix/share/doc/obnc/obncdoc/style.css" + + #delete man pages + for file in $man1Files; do + EchoAndRun rm -f "$destdir$prefix/share/man/man1/$file" + done +} + + +PrintHelp() +{ + echo "usage: " + printf "\tinstall [u] [--destdir=DESTDIR]\n" + printf "\tinstall -h\n" + echo + printf "\tu\t\tuninstall\n" + printf "\t--destdir\tspecify directory for staged installation\n" + printf "\t-h\t\tdisplay help and exit\n" +} + + +ExitInvalidCommand() +{ + echo "invalid command. Try 'install -h' for more information." >&2 + exit 1 +} + + +Run() +{ + local helpWanted=false + local uninstall=false + local arg= + + for arg in "$@"; do + case "$arg" in + u) + uninstall=true;; + --destdir=*) + destdir="${arg#--destdir=}";; + -h) + helpWanted=true;; + *) + ExitInvalidCommand + esac + done + + if "$helpWanted"; then + PrintHelp + else + if [ -e CONFIG ]; then + if [ "$prefix" != "${prefix#/}" ]; then + if "$uninstall"; then + Uninstall + else + Install + fi + else + printf "prefix must be an absolute path: %s\ninstallation aborted\n" "$prefix" >&2 + exit 1 + fi + else + printf "must first run build script\ninstallation aborted\n" >&2 + exit 1 + fi + fi +} + +Run "$@" diff --git a/lib/obnc/Files.c b/lib/obnc/Files.c new file mode 100644 index 0000000..280001c --- /dev/null +++ b/lib/obnc/Files.c @@ -0,0 +1,771 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include ".obnc/Files.h" +#include "../../src/Util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct Handle *File; + +struct Handle { + Files_Handle_ base; + FILE *file; + char *name; + int registered; +}; + +struct HeapHandle { + const OBNC_Td *td; + struct Handle fields; +}; + +const int Files_Handle_id; +const int *const Files_Handle_ids[1] = {&Files_Handle_id}; +const OBNC_Td Files_Handle_td = {Files_Handle_ids, 1}; + +const int Files_Rider_id; +const int *const Files_Rider_ids[1] = {&Files_Rider_id}; +const OBNC_Td Files_Rider_td = {Files_Rider_ids, 1}; + +static int FileExists(const char name[]) +{ + return access(name, F_OK ) != -1; +} + + +static void CheckTermination(const char s[], OBNC_LONGI int sLen) +{ + OBNC_LONGI int i; + + i = 0; + while ((i < sLen) && (s[i] != '\0')) { + i++; + } + OBNC_IT(i, sLen); +} + + +static File NewFile(FILE *file, const char name[], int registered) +{ + File result; + int nameLen; + + assert(file != NULL); + assert(name != NULL); + + OBNC_NEW(result, &Files_Handle_td, struct HeapHandle, OBNC_REGULAR_ALLOC); + if (result != NULL) { + result->file = file; + nameLen = strlen(name) + 1; + NEW_ARRAY(result->name, nameLen); + if (result->name != NULL) { + memcpy(result->name, name, nameLen); + result->registered = registered; + } else { + result = NULL; + } + } + return result; +} + + +Files_File_ Files_Old_(const char name[], OBNC_LONGI int nameLen) +{ + FILE *file; + File result; + + assert(name != NULL); + assert(nameLen >= 0); + CheckTermination(name, nameLen); + + file = fopen(name, "r+b"); + if (file == NULL) { + file = fopen(name, "rb"); + if ((file == NULL) && FileExists(name)) { + file = fopen(name, "ab"); + } + } + if (file != NULL) { + result = NewFile(file, name, 1); + } else { + result = NULL; + } + return (Files_File_) result; +} + + +Files_File_ Files_New_(const char name[], OBNC_LONGI int nameLen) +{ + FILE *file; + File result; + + assert(name != NULL); + assert(nameLen >= 0); + CheckTermination(name, nameLen); + + file = tmpfile(); + if (file != NULL) { + result = NewFile(file, name, 0); + } else { + result = NULL; + fprintf(stderr, "Files.New failed: %s\n", strerror(errno)); + } + return (Files_File_) result; +} + + +static void Copy(FILE *src, FILE *dst, const char dstName[], int *done) +{ + int ch; + + rewind(src); + ch = fgetc(src); + while (ch != EOF) { + ch = fputc(ch, dst); + if (ch != EOF) { + ch = fgetc(src); + } + } + *done = ! ferror(src) && ! ferror(dst); + if (ferror(src) || ferror(dst)) { + fprintf(stderr, "Files.Register failed: %s: %s\n", dstName, strerror(errno)); + } +} + + +void Files_Register_(Files_File_ file) +{ + File f; + FILE *new; + int done; + + OBNC_PT(file); + + f = (File) file; + if (! f->registered) { + new = fopen(f->name, "w+b"); + if (new != NULL) { + Copy(f->file, new, f->name, &done); + if (done) { + f->file = new; + f->registered = 1; + } + } else { + fprintf(stderr, "Files.Register failed: %s: %s\n", f->name, strerror(errno)); + } + } +} + + +void Files_Close_(Files_File_ file) +{ + File f; + int error; + + OBNC_PT(file); + + f = (File) file; + error = fflush(f->file); + if (error) { + fprintf(stderr, "Files.Close failed: %s: %s\n", f->name, strerror(errno)); + } +} + + +void Files_Purge_(Files_File_ file) +{ + File f; + int error; + + OBNC_PT(file); + + f = ((File) file); + error = fclose(f->file); + if (! error) { + if (f->registered) { + f->file = fopen(f->name, "w+b"); + } else { + f->file = tmpfile(); + } + if (f->file == NULL) { + fprintf(stderr, "Files.Purge failed: %s: %s\n", f->name, strerror(errno)); + } + } else { + fprintf(stderr, "Files.Purge failed: %s: %s\n", f->name, strerror(errno)); + } +} + + +void Files_Delete_(const char name[], OBNC_LONGI int nameLen, OBNC_LONGI int *res) +{ + assert(name != NULL); + assert(nameLen >= 0); + CheckTermination(name, nameLen); + + *res = unlink(name); + if (*res != 0) { + fprintf(stderr, "Files.Delete failed: %s: %s\n", name, strerror(errno)); + } +} + + +void Files_Rename_(const char old[], OBNC_LONGI int oldLen, const char new[], OBNC_LONGI int newLen, OBNC_LONGI int *res) +{ + assert(old != NULL); + assert(oldLen >= 0); + CheckTermination(old, oldLen); + assert(new != NULL); + assert(newLen >= 0); + CheckTermination(new, newLen); + + *res = rename(old, new); + if (*res != 0) { + fprintf(stderr, "Failed renaming file: %s: %s\n", old, strerror(errno)); + } +} + + +OBNC_LONGI int Files_Length_(Files_File_ file) +{ + File f; + long int result; + int error; + + OBNC_PT(file); + + f = (File) file; + result = 0; + error = fseek(f->file, 0, SEEK_END); + if (! error) { + result = ftell(f->file); + if (result < 0) { + fprintf(stderr, "Files.Length failed: %s: %s\n", f->name, strerror(errno)); + } else if (result > INT_MAX) { + fprintf(stderr, "Files.Length failed: %s: length exceeds maximum value of INTEGER (%d)\n", f->name, INT_MAX); + } + } else { + fprintf(stderr, "Files.Length failed: %s: %s\n", f->name, strerror(errno)); + } + return result; +} + + +void Files_GetDate_(Files_File_ file, OBNC_LONGI int *t, OBNC_LONGI int *d) +{ + File f; + struct stat statResult; + int error; + struct tm *td; + + OBNC_PT(file); + assert(t != NULL); + assert(d != NULL); + + f = (File) file; + if (f->registered) { + error = stat(f->name, &statResult); + if (! error) { + td = localtime(&(statResult.st_mtime)); + if (td != NULL) { + *t = (td->tm_hour << 12) | (td->tm_min << 6) | td->tm_sec; /*59 < 2^6*/ + *d = ((1900 + td->tm_year) << 9) | ((td->tm_mon + 1) << 5) | td->tm_mday; /*31 < 2^5 and 12 < 2^4*/ + } else { + fprintf(stderr, "Files.GetDate failed: %s: %s\n", f->name, strerror(errno)); + } + } else { + fprintf(stderr, "Files.GetDate failed: %s: %s\n", f->name, strerror(errno)); + } + } else { + *t = 0; + *d = 0; + fprintf(stderr, "Files.GetDate failed: %s: cannot get date of unregistered file\n", f->name); + } +} + + +void Files_Set_(Files_Rider_ *r, const OBNC_Td *rTD, Files_File_ f, OBNC_LONGI int pos) +{ + assert(r != NULL); + OBNC_PT(f); + assert(pos >= 0); + assert(pos <= Files_Length_(f)); + + r->eof_ = 0; + r->base_ = f; + r->pos_ = pos; +} + + +OBNC_LONGI int Files_Pos_(Files_Rider_ *r, const OBNC_Td *rTD) +{ + assert(r != NULL); + OBNC_PT(r->base_); + + return r->pos_; +} + + +Files_File_ Files_Base_(Files_Rider_ *r, const OBNC_Td *rTD) +{ + assert(r != NULL); + OBNC_PT(r->base_); + + return r->base_; +} + + +static void Position(const Files_Rider_ *r, FILE **fp) +{ + File f; + int error; + + f = (File) (r->base_); + *fp = f->file; + error = fseek(*fp, r->pos_, SEEK_SET); + if (error) { + fprintf(stderr, "Positioning rider failed: %s: %s\n", f->name, strerror(errno)); + *fp = NULL; + } +} + + +static const char *BaseName(const Files_Rider_ *r) +{ + return ((File) (r->base_))->name; +} + + +void Files_Read_(Files_Rider_ *r, const OBNC_Td *rTD, unsigned char *x) +{ + FILE *fp; + int ch; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(x != NULL); + + Position(r, &fp); + if (fp != NULL) { + ch = fgetc(fp); + if (ch != EOF) { + *x = ch; + r->pos_++; + } else { + if (feof(fp)) { + r->eof_ = 1; + } else if (ferror(fp)) { + fprintf(stderr, "Files.Read failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } + } +} + + +void Files_ReadInt_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGI int *i) +{ + FILE *fp; + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(i != NULL); + + Position(r, &fp); + if (fp != NULL) { + n = fread(i, sizeof (*i), 1, fp); + r->pos_ += n * sizeof (*i); + if (feof(fp)) { + r->eof_ = 1; + } else if (ferror(fp)) { + fprintf(stderr, "Files.ReadInt failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_ReadReal_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGR double *x) +{ + FILE *fp; + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(x != NULL); + + Position(r, &fp); + if (fp != NULL) { + n = fread(x, sizeof (*x), 1, fp); + r->pos_ += n * sizeof (*x); + if (feof(fp)) { + r->eof_ = 1; + } else if (ferror(fp)) { + fprintf(stderr, "Files.ReadReal failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +static int Mod(OBNC_LONGI int x, OBNC_LONGI int y) +{ + return (x >= 0)? x % y: (x % y + y) % y; +} + + +static int Div(OBNC_LONGI int x, OBNC_LONGI int y) +{ + return (x >= 0)? x / y: (x - Mod(x, y)) / y; +} + + +void Files_ReadNum_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGI int *x) +{ + FILE *fp; + OBNC_LONGI int s, n; + int ch; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(x != NULL); + + Position(r, &fp); + if (fp != NULL) { + s = 0; + n = 0; + ch = fgetc(fp); + while (ch >= 128) { + r->pos_++; + n += (ch - 128) << s; + s += 7; + ch = fgetc(fp); + } + if (ch != EOF) { + r->pos_++; + *x = n + ((Mod(ch, 64) - Div(ch, 64) * 64) << s); + } else { + if (feof(fp)) { + r->eof_ = 1; + } else if (ferror(fp)) { + fprintf(stderr, "Files.Read failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } + } +} + + +void Files_ReadString_(Files_Rider_ *r, const OBNC_Td *rTD, char s[], OBNC_LONGI int sLen) +{ + FILE *fp; + int ch, i; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(s != NULL); + assert(sLen >= 0); + + Position(r, &fp); + if (fp != NULL) { + ch = fgetc(fp); + i = 0; + while ((ch != EOF) && (ch != '\0') && (i < sLen - 1)) { + s[i] = ch; + ch = fgetc(fp); + i++; + } + if (ch != EOF) { + if (ch == '\0') { + s[i] = '\0'; + } else { /*string doesn't fit*/ + s[0] = '\0'; + } + r->pos_ += i + 1; + } else { + if (feof(fp)) { + r->eof_ = 1; + } else if (ferror(fp)) { + fprintf(stderr, "Files.ReadString failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } + } +} + + +void Files_ReadSet_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGI unsigned int *s) +{ + FILE *fp; + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(s != NULL); + + Position(r, &fp); + if (fp != NULL) { + n = fread(s, sizeof (*s), 1, fp); + r->pos_ += n * sizeof (*s); + if (feof(fp)) { + r->eof_ = 1; + } else if (ferror(fp)) { + fprintf(stderr, "Files.ReadSet failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_ReadBool_(Files_Rider_ *r, const OBNC_Td *rTD, int *b) +{ + FILE *fp; + int ch; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(b != NULL); + + Position(r, &fp); + if (fp != NULL) { + ch = fgetc(fp); + if (ch != EOF) { + *b = ch; + r->pos_++; + } else { + if (feof(fp)) { + r->eof_ = 1; + } if (ferror(fp)) { + fprintf(stderr, "Files.ReadBool failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } + } +} + + +static int Min(int a, int b) +{ + return (a < b)? a: b; +} + + +void Files_ReadBytes_(Files_Rider_ *r, const OBNC_Td *rTD, unsigned char buf[], OBNC_LONGI int bufLen, OBNC_LONGI int n) +{ + FILE *fp; + size_t nRead; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(buf != NULL); + assert(bufLen >= 0); + assert(n >= 0); + + Position(r, &fp); + if (fp != NULL) { + nRead = fread(buf, sizeof buf[0], Min(n, bufLen), fp); + r->pos_ += nRead * sizeof buf[0]; + r->res_ = n - nRead; + if (feof(fp)) { + r->eof_ = 1; + } else if (ferror(fp)) { + fprintf(stderr, "Files.ReadBytes failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_Write_(Files_Rider_ *r, const OBNC_Td *rTD, unsigned char x) +{ + FILE *fp; + int res; + + assert(r != NULL); + OBNC_PT(r->base_); + + Position(r, &fp); + if (fp != NULL) { + res = fputc(x, fp); + if (res != EOF) { + r->pos_++; + } else { + fprintf(stderr, "Files.Write failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_WriteInt_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGI int i) +{ + FILE *fp; + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + + Position(r, &fp); + if (fp != NULL) { + n = fwrite(&i, sizeof i, 1, fp); + r->pos_ += n * sizeof i; + if (ferror(fp)) { + fprintf(stderr, "Files.WriteInt failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_WriteReal_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGR double x) +{ + FILE *fp; + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + + Position(r, &fp); + if (fp != NULL) { + n = fwrite(&x, sizeof x, 1, fp); + r->pos_ += n * sizeof x; + if (ferror(fp)) { + fprintf(stderr, "Files.WriteReal failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_WriteNum_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGI int x) +{ + FILE *fp; + int i; + char buf[CHAR_BIT * sizeof x]; /* 10^x = 2^n implies x < n */ + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + + i = 0; + while ((x < -64) || (x > 63)) { + assert(i < LEN(buf)); + buf[i] = Mod(x, 128) + 128; + x = Div(x, 128); + i++; + } + assert(i < LEN(buf)); + buf[i] = Mod(x, 128); + + Position(r, &fp); + if (fp != NULL) { + n = fwrite(buf, sizeof buf[0], i + 1, fp); + r->pos_ += n * sizeof buf[0]; + if (ferror(fp)) { + fprintf(stderr, "Files.WriteNum failed: %s: %s\n", BaseName(r), strerror(errno)); + } + + } +} + + +void Files_WriteString_(Files_Rider_ *r, const OBNC_Td *rTD, const char s[], OBNC_LONGI int sLen) +{ + FILE *fp; + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(s != NULL); + assert(sLen >= 0); + CheckTermination(s, sLen); + + Position(r, &fp); + if (fp != NULL) { + n = fwrite(s, sizeof s[0], strlen(s) + 1, fp); + r->pos_ += n * sizeof s[0]; + if (ferror(fp)) { + fprintf(stderr, "Files.WriteString failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_WriteSet_(Files_Rider_ *r, const OBNC_Td *rTD, OBNC_LONGI unsigned int s) +{ + FILE *fp; + size_t n; + + assert(r != NULL); + OBNC_PT(r->base_); + + Position(r, &fp); + if (fp != NULL) { + n = fwrite(&s, sizeof s, 1, fp); + r->pos_ += n * sizeof s; + if (ferror(fp)) { + fprintf(stderr, "Files.WriteSet failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_WriteBool_(Files_Rider_ *r, const OBNC_Td *rTD, int b) +{ + FILE *fp; + int res; + + assert(r != NULL); + OBNC_PT(r->base_); + + Position(r, &fp); + if (fp != NULL) { + res = fputc(!! b, fp); + if (res != EOF) { + r->pos_++; + } else { + fprintf(stderr, "Files.WriteBool failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_WriteBytes_(Files_Rider_ *r, const OBNC_Td *rTD, unsigned char buf[], OBNC_LONGI int bufLen, OBNC_LONGI int n) +{ + FILE *fp; + int nWritten; + + assert(r != NULL); + OBNC_PT(r->base_); + assert(buf != NULL); + assert(bufLen >= 0); + assert(n >= 0); + assert(n <= bufLen); + + Position(r, &fp); + if (fp != NULL) { + nWritten = fwrite(buf, sizeof buf[0], n, fp); + r->pos_ += nWritten * sizeof buf[0]; + r->res_ = n - nWritten; + if (ferror(fp)) { + fprintf(stderr, "Files.WriteBytes failed: %s: %s\n", BaseName(r), strerror(errno)); + } + } +} + + +void Files_Init(void) +{ +} diff --git a/lib/obnc/Files.env b/lib/obnc/Files.env new file mode 100644 index 0000000..01a1bad --- /dev/null +++ b/lib/obnc/Files.env @@ -0,0 +1 @@ +LDLIBS="-lgc" diff --git a/lib/obnc/Files.obn b/lib/obnc/Files.obn new file mode 100644 index 0000000..0d64f37 --- /dev/null +++ b/lib/obnc/Files.obn @@ -0,0 +1,181 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Files; +(**Operations on files + +Implements the basic library module from "The Oakwood Guidelines for Oberon-2 Compiler Developers".*) + +(*implemented in C*) + + TYPE + File* = POINTER TO Handle; + + Handle = RECORD END; + + Rider* = RECORD + eof*: BOOLEAN; + res*: INTEGER; + base: File; + pos: INTEGER + END; + + PROCEDURE Old*(name: ARRAY OF CHAR): File; +(**Old(fn) searches the name fn in the directory and returns the corresponding file. If the name is not found, it returns NIL.*) + RETURN NIL + END Old; + + + PROCEDURE New*(name: ARRAY OF CHAR): File; +(**New(fn) creates and returns a new file. The name fn is remembered for the later use of the operation Register. The file is only entered into the directory when Register is called.*) + RETURN NIL + END New; + + + PROCEDURE Register*(f: File); +(**enters the file f into the directory together with the name provided in the operation New that created f. The file buffers are written back. Any existing mapping of this name to another file is overwritten.*) + END Register; + + + PROCEDURE Close*(f: File); +(**writes back the file buffers of f. The file is still accessible by its handle f and the riders positioned on it. If a file is not modified it is not necessary to close it.*) + END Close; + + + PROCEDURE Purge*(f: File); +(**resets the length of file f to 0*) + END Purge; + + + PROCEDURE Delete*(name: ARRAY OF CHAR; VAR res: INTEGER); +(**Delete(fn, res) removes the directory entry for the file fn without deleting the file. If res = 0 the file has been successfully deleted. If there are variables referring to the file while Delete is called, they can still be used.*) + END Delete; + + + PROCEDURE Rename*(old, new: ARRAY OF CHAR; VAR res: INTEGER); +(**Rename(oldfn, newfn, res) renames the directory entry oldfn to newfn. If res = 0 the file has been successfully renamed. If there are variables referring to the file while Rename is called, they can still be used.*) + END Rename; + + + PROCEDURE Length*(f: File): INTEGER; +(**returns the number of bytes in file f*) + RETURN 0 + END Length; + + + PROCEDURE GetDate*(f: File; VAR t, d: INTEGER); +(**returns the time t and date d of the last modification of file f. The encoding is: hour = t DIV 4096; minute = t DIV 64 MOD 64; second = t MOD 64; year = d DIV 512; month = d DIV 32 MOD 16; day = d MOD 32.*) + END GetDate; + + + PROCEDURE Set*(VAR r: Rider; f: File; pos: INTEGER); +(**sets the rider r to position pos in file f. The field r.eof is set to FALSE. The +operation requires that 0 <= pos <= Length(f)*) + END Set; + + + PROCEDURE Pos*(VAR r: Rider): INTEGER; +(**returns the position of the rider r*) + RETURN 0 + END Pos; + + + PROCEDURE Base*(VAR r: Rider): File; +(**returns the file to which the rider r has been set*) + RETURN NIL + END Base; + + PROCEDURE Read*(VAR r: Rider; VAR x: BYTE); +(**reads the next byte x from rider r and advances r accordingly*) + END Read; + + + PROCEDURE ReadInt*(VAR r: Rider; VAR i: INTEGER); +(**reads an integer i from rider r and advances r accordingly.*) + END ReadInt; + + + PROCEDURE ReadReal*(VAR r: Rider; VAR x: REAL); +(**reads a real number x from rider r and advances r accordingly.*) + END ReadReal; + + + PROCEDURE ReadNum*(VAR r: Rider; VAR i: INTEGER); +(**reads an integer i from rider r and advances r accordingly. The number i is compactly encoded*) + END ReadNum; + + + PROCEDURE ReadString*(VAR r: Rider; VAR s: ARRAY OF CHAR); +(**reads a sequence of characters (including the terminating 0X) from rider r and returns it in s. The rider is advanced accordingly. The actual parameter corresponding to s must be long enough to hold the character sequence plus the terminating 0X.*) + END ReadString; + + + PROCEDURE ReadSet*(VAR r: Rider; VAR s: SET); +(**reads a set s from rider r and advances r accordingly*) + END ReadSet; + + + PROCEDURE ReadBool*(VAR r: Rider; VAR b: BOOLEAN); +(**reads a Boolean value b from rider r and advances r accordingly*) + END ReadBool; + + + PROCEDURE ReadBytes*(VAR r: Rider; VAR buf: ARRAY OF BYTE; n: INTEGER); +(**reads n bytes into buffer buf starting at the rider position r. The rider is advanced accordingly. If less than n bytes could be read, r.res contains the number of requested but unread bytes.*) + END ReadBytes; + + + PROCEDURE Write*(VAR r: Rider; x: BYTE); +(**writes the byte x to rider r and advances r accordingly*) + END Write; + + + PROCEDURE WriteInt*(VAR r: Rider; i: INTEGER); +(**writes the integer i to rider r and advances r accordingly*) + END WriteInt; + + + PROCEDURE WriteReal*(VAR r: Rider; x: REAL); +(**writes the real number x to rider r and advances r accordingly*) + END WriteReal; + + + PROCEDURE WriteNum*(VAR r: Rider; i: INTEGER); +(**writes the integer i to rider r and advances r accordingly. The number i is compactly encoded.*) + END WriteNum; + + + PROCEDURE WriteString*(VAR r: Rider; s: ARRAY OF CHAR); +(**writes the sequence of characters s (including the terminating 0X) to rider r and advances r accordingly*) + END WriteString; + + + PROCEDURE WriteSet*(VAR r: Rider; s: SET); +(**writes the set s to rider r and advances r accordingly*) + END WriteSet; + + + PROCEDURE WriteBool*(VAR r: Rider; b: BOOLEAN); +(**writes the Boolean value b to rider r and advances r accordingly.*) + END WriteBool; + + + PROCEDURE WriteBytes*(VAR r: Rider; VAR buf: ARRAY OF BYTE; n: INTEGER); +(**writes the first n bytes from buf to rider r and advances r accordingly. r.res contains the number of bytes that could not be written (e.g., due to a disk full error).*) + END WriteBytes; + +END Files. diff --git a/lib/obnc/FilesTest.obn b/lib/obnc/FilesTest.obn new file mode 100644 index 0000000..7ea8a9a --- /dev/null +++ b/lib/obnc/FilesTest.obn @@ -0,0 +1,450 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE FilesTest; + + IMPORT Files; + + + PROCEDURE TestOld; + VAR f: Files.File; + BEGIN + f := Files.Old("FilesTest.obn"); + ASSERT(f # NIL); + ASSERT(f IS Files.File) + END TestOld; + + + PROCEDURE TestNew; + VAR f: Files.File; + BEGIN + f := Files.New("NewTest"); + ASSERT(f # NIL); + ASSERT(f IS Files.File) + END TestNew; + + + PROCEDURE TestRegister; + VAR f: Files.File; + r: Files.Rider; + res: INTEGER; + + PROCEDURE IsRider(VAR r: Files.Rider): BOOLEAN; + RETURN r IS Files.Rider + END IsRider; + BEGIN + f := Files.New("RegisterTest"); + ASSERT(f # NIL); + ASSERT(IsRider(r)); + Files.Register(f); + + f := Files.Old("RegisterTest"); + ASSERT(f # NIL); + Files.Delete("RegisterTest", res); + ASSERT(res = 0); + + f := Files.New("RegisterTest"); + ASSERT(f # NIL); + Files.Set(r, f, 0); + Files.Write(r, 37); + Files.Close(f); + Files.Register(f); + ASSERT(Files.Length(f) = 1); + + f := Files.Old("RegisterTest"); + ASSERT(f # NIL); + ASSERT(Files.Length(f) = 1); + Files.Delete("RegisterTest", res); + ASSERT(res = 0) + END TestRegister; + + + PROCEDURE TestClose; + VAR f: Files.File; + r: Files.Rider; + BEGIN + f := Files.New("CloseTest"); + ASSERT(f # NIL); + Files.Set(r, f, 0); + Files.Write(r, 65); + ASSERT(Files.Length(f) <= 1); + Files.Close(f); + ASSERT(Files.Length(f) = 1) + END TestClose; + + + PROCEDURE TestPurge; + VAR f: Files.File; + r: Files.Rider; + BEGIN + f := Files.New("PurgeTest"); + ASSERT(f # NIL); + Files.Set(r, f, 0); + Files.Write(r, 65); + Files.Purge(f); + ASSERT(Files.Length(f) = 0) + END TestPurge; + + + PROCEDURE TestDelete; + VAR f: Files.File; + res: INTEGER; + BEGIN + f := Files.New("DeleteTest"); + ASSERT(f # NIL); + Files.Register(f); + f := Files.Old("DeleteTest"); + ASSERT(f # NIL); + Files.Delete("DeleteTest", res); + ASSERT(res = 0); + f := Files.Old("DeleteTest"); + ASSERT(f = NIL) + END TestDelete; + + + PROCEDURE TestRename; + VAR f: Files.File; + res: INTEGER; + BEGIN + f := Files.New("RenameTest"); + ASSERT(f # NIL); + Files.Register(f); + f := Files.Old("RenameTest"); + ASSERT(f # NIL); + + Files.Rename("RenameTest", "RenameTest1", res); + ASSERT(res = 0); + + f := Files.Old("RenameTest"); + ASSERT(f = NIL); + f := Files.Old("RenameTest1"); + ASSERT(f # NIL); + + Files.Delete("RenameTest1", res); + ASSERT(res = 0) + END TestRename; + + + PROCEDURE TestLength; + VAR f: Files.File; + res: INTEGER; + BEGIN + f := Files.New("LengthTest"); + ASSERT(f # NIL); + Files.Register(f); + ASSERT(Files.Length(f) = 0); + Files.Delete("LengthTest", res); + ASSERT(res = 0) + END TestLength; + + + PROCEDURE TestDate; + VAR f: Files.File; + t, d: INTEGER; + hour, minute, second, year, month, day: INTEGER; + BEGIN + f := Files.Old("FilesTest.obn"); + ASSERT(f # NIL); + Files.GetDate(f, t, d); + hour := t DIV 4096; + ASSERT(hour >= 0); + ASSERT(hour < 24); + minute := t DIV 64 MOD 64; + ASSERT(minute >= 0); + ASSERT(minute < 60); + second := t MOD 64; + ASSERT(second >= 0); + ASSERT(second < 60); + year := d DIV 512; + ASSERT(year >= 0); + month := d DIV 32 MOD 16; + ASSERT(month >= 1); + ASSERT(month <= 12); + day := d MOD 32; + ASSERT(day >= 1); + ASSERT(day <= 31); + END TestDate; + + + PROCEDURE TestSet; + VAR f: Files.File; + r: Files.Rider; + BEGIN + f := Files.New("SetTest"); + ASSERT(f # NIL); + Files.Set(r, f, 0); + ASSERT(~r.eof) + END TestSet; + + + PROCEDURE TestPos; + VAR f: Files.File; + r: Files.Rider; + BEGIN + f := Files.New("PosTest"); + ASSERT(f # NIL); + Files.Set(r, f, 0); + ASSERT(Files.Pos(r) = 0) + END TestPos; + + + PROCEDURE TestBase; + VAR f: Files.File; + r: Files.Rider; + BEGIN + f := Files.New("BaseTest"); + ASSERT(f # NIL); + Files.Set(r, f, 0); + ASSERT(Files.Base(r) = f) + END TestBase; + + + PROCEDURE TestReadWrite; + VAR f: Files.File; + r: Files.Rider; + b: BYTE; + BEGIN + f := Files.New("ReadWriteTest"); + ASSERT(f # NIL); + Files.Set(r, f, 0); + Files.Write(r, 65); + Files.Close(f); + ASSERT(Files.Length(f) = 1); + Files.Set(r, f, 0); + Files.Read(r, b); + ASSERT(~r.eof); + ASSERT(b = 65); + ASSERT(Files.Pos(r) = 1) + END TestReadWrite; + + + PROCEDURE TestReadWriteInt; + VAR f: Files.File; + r: Files.Rider; + i: INTEGER; + BEGIN + f := Files.New("ReadWriteIntTest"); + ASSERT(f # NIL); + + Files.Set(r, f, 0); + Files.WriteInt(r, 100); + Files.WriteInt(r, -1000); + Files.Close(f); + + Files.Set(r, f, 0); + Files.ReadInt(r, i); + ASSERT(~r.eof); + ASSERT(i = 100); + Files.ReadInt(r, i); + ASSERT(~r.eof); + ASSERT(i = -1000); + Files.ReadInt(r, i); + ASSERT(r.eof) + END TestReadWriteInt; + + + PROCEDURE TestReadWriteReal; + VAR f: Files.File; + r: Files.Rider; + x: REAL; + BEGIN + f := Files.New("ReadWriteRealTest"); + ASSERT(f # NIL); + + Files.Set(r, f, 0); + Files.WriteReal(r, 3.14); + Files.WriteReal(r, -3.14); + Files.Close(f); + + Files.Set(r, f, 0); + Files.ReadReal(r, x); + ASSERT(~r.eof); + ASSERT(x = 3.14); + Files.ReadReal(r, x); + ASSERT(~r.eof); + ASSERT(x = -3.14); + Files.ReadReal(r, x); + ASSERT(r.eof) + END TestReadWriteReal; + + + PROCEDURE TestReadWriteNum; + VAR f: Files.File; + r: Files.Rider; + i: INTEGER; + BEGIN + f := Files.New("ReadWriteNumTest"); + ASSERT(f # NIL); + + Files.Set(r, f, 0); + Files.WriteNum(r, 100); + Files.WriteNum(r, -1000); + Files.Close(f); + + Files.Set(r, f, 0); + Files.ReadNum(r, i); + ASSERT(~r.eof); + ASSERT(i = 100); + Files.ReadNum(r, i); + ASSERT(~r.eof); + ASSERT(i = -1000); + Files.ReadNum(r, i); + ASSERT(r.eof) + END TestReadWriteNum; + + + PROCEDURE TestReadWriteString; + VAR f: Files.File; + r: Files.Rider; + s: ARRAY 32 OF CHAR; + BEGIN + f := Files.New("ReadWriteStringTest"); + ASSERT(f # NIL); + + Files.Set(r, f, 0); + Files.WriteString(r, "hello"); + Files.WriteString(r, "there"); + Files.WriteString(r, ""); + Files.Close(f); + + Files.Set(r, f, 0); + Files.ReadString(r, s); + ASSERT(~r.eof); + ASSERT(s = "hello"); + Files.ReadString(r, s); + ASSERT(~r.eof); + ASSERT(s = "there"); + Files.ReadString(r, s); + ASSERT(~r.eof); + ASSERT(s = ""); + Files.ReadString(r, s); + ASSERT(r.eof) + END TestReadWriteString; + + + PROCEDURE TestReadWriteSet; + VAR f: Files.File; + r: Files.Rider; + s: SET; + BEGIN + f := Files.New("ReadWriteSetTest"); + ASSERT(f # NIL); + + Files.Set(r, f, 0); + Files.WriteSet(r, {}); + Files.WriteSet(r, {0}); + Files.WriteSet(r, {0, 1}); + Files.Close(f); + + Files.Set(r, f, 0); + Files.ReadSet(r, s); + ASSERT(~r.eof); + ASSERT(s = {}); + Files.ReadSet(r, s); + ASSERT(~r.eof); + ASSERT(s = {0}); + Files.ReadSet(r, s); + ASSERT(~r.eof); + ASSERT(s = {0, 1}); + Files.ReadSet(r, s); + ASSERT(r.eof) + END TestReadWriteSet; + + + PROCEDURE TestReadWriteBool; + VAR f: Files.File; + r: Files.Rider; + b: BOOLEAN; + BEGIN + f := Files.New("ReadWriteBoolTest"); + ASSERT(f # NIL); + + Files.Set(r, f, 0); + Files.WriteBool(r, TRUE); + Files.WriteBool(r, FALSE); + Files.Close(f); + + Files.Set(r, f, 0); + Files.ReadBool(r, b); + ASSERT(~r.eof); + ASSERT(b); + Files.ReadBool(r, b); + ASSERT(~r.eof); + ASSERT(~b); + Files.ReadBool(r, b); + ASSERT(r.eof) + END TestReadWriteBool; + + + PROCEDURE TestReadWriteBytes; + VAR f: Files.File; + r: Files.Rider; + buf: ARRAY 4 OF BYTE; + i: INTEGER; + BEGIN + FOR i := 0 TO LEN(buf) - 1 DO buf[i] := i + 1 END; + + f := Files.New("ReadWriteBytesTest"); + ASSERT(f # NIL); + + Files.Set(r, f, 0); + Files.WriteBytes(r, buf, LEN(buf)); + Files.WriteBytes(r, buf, LEN(buf)); + Files.Close(f); + + FOR i := 0 TO LEN(buf) - 1 DO buf[i] := 0 END; + + Files.Set(r, f, 0); + + Files.ReadBytes(r, buf, LEN(buf)); + ASSERT(~r.eof); + FOR i := 0 TO LEN(buf) - 1 DO + ASSERT(buf[i] = i + 1) + END; + + Files.ReadBytes(r, buf, LEN(buf)); + ASSERT(~r.eof); + FOR i := 0 TO LEN(buf) - 1 DO + ASSERT(buf[i] = i + 1) + END; + + Files.ReadBytes(r, buf, LEN(buf)); + ASSERT(r.eof) + END TestReadWriteBytes; + +BEGIN + TestOld; + TestNew; + TestRegister; + TestClose; + TestPurge; + TestDelete; + TestRename; + TestLength; + TestDate; + TestSet; + TestPos; + TestBase; + TestReadWrite; + TestReadWriteInt; + TestReadWriteReal; + TestReadWriteNum; + TestReadWriteString; + TestReadWriteSet; + TestReadWriteBool; + TestReadWriteBytes +END FilesTest. diff --git a/lib/obnc/In.c b/lib/obnc/In.c new file mode 100644 index 0000000..baa7c0e --- /dev/null +++ b/lib/obnc/In.c @@ -0,0 +1,160 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include ".obnc/In.h" +#include "../../src/Util.h" +#include +#include +#include +#include +#include + +int In_Done_; +static int inputConsumed; + +void In_Open_(void) +{ + if (! inputConsumed) { + /*do nothing*/ + } else { + fprintf(stderr, "cannot rewind stdin\n"); + exit(EXIT_FAILURE); + } +} + + +void In_Char_(char *ch) +{ + int d; + + d = getchar(); + *ch = (char) d; + In_Done_ = d != EOF; + if (In_Done_) { + inputConsumed = 1; + } +} + + +void In_Int_(OBNC_LONGI int *i) +{ + int scanCount; + + scanCount = scanf("%" OBNC_INT_MOD "d", i); + In_Done_ = scanCount == 1; + if (In_Done_) { + inputConsumed = 1; + } +} + + +void In_Real_(OBNC_LONGR double *x) +{ + int scanCount; + + scanCount = scanf("%" OBNC_REAL_MOD_R "f", x); + In_Done_ = scanCount == 1; + if (In_Done_) { + inputConsumed = 1; + } +} + + +void In_String_(char str[], OBNC_LONGI int strLen) +{ + int n, ch, i; + + In_Done_ = 0; + n = 0; + do { + ch = getchar(); + n++; + } while (isspace(ch)); + if (ch == '"') { + i = 0; + ch = getchar(); + n++; + while ((ch != EOF) && (ch != '"') && (ch != '\n') && (i < strLen)) { + str[i] = ch; + i++; + ch = getchar(); + n++; + } + if ((ch == '"') && (i < strLen)) { + str[i] = '\0'; + In_Done_ = 1; + } else { + str[0] = '\0'; + } + } else { + if (ch != EOF) { + ch = ungetc(ch, stdin); + if (ch != EOF) { + n--; + } else { + fprintf(stderr, "ungetc failed while reading string\n"); + } + } + } + if (ch == EOF) { + n--; + } + if (n > 0) { + inputConsumed = 1; + } +} + + +void In_Name_(char name[], OBNC_LONGI int nameLen) +{ + int n, ch, i; + + In_Done_ = 0; + n = 0; + do { + ch = getchar(); + n++; + } while (isspace(ch)); + if (ch != EOF) { + i = 0; + while ((i < nameLen) && isgraph(ch)) { + name[i] = ch; + i++; + ch = getchar(); + n++; + } + if (i < nameLen) { + name[i] = '\0'; + In_Done_ = ! isgraph(ch); + } else { + name[0] = '\0'; + } + } + if (ch == EOF) { + n--; + } + if (n > 0) { + inputConsumed = 1; + } +} + + +void In_Init(void) +{ + In_Done_ = 0; + inputConsumed = 0; +} diff --git a/lib/obnc/In.obn b/lib/obnc/In.obn new file mode 100644 index 0000000..8c7bbc3 --- /dev/null +++ b/lib/obnc/In.obn @@ -0,0 +1,68 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE In; +(**Input from the standard input stream + +Implements the basic library module from "The Oakwood Guidelines for Oberon-2 Compiler Developers". All read operations except Char skips over preceding whitespace.*) + +(*implemented in C*) + + VAR + Done*: BOOLEAN; (**status of last operation*) + + PROCEDURE Open*; +(**included for compatibility with "The Oakwood Guidelines". On a typical Unix-like system, stdin cannot be rewinded. If Open is called when the file position is not at the beginning of stdin, the program aborts.*) + END Open; + + + PROCEDURE Char*(VAR ch: CHAR); +(**returns in ch the character at the current position*) + END Char; + + + PROCEDURE Int*(VAR i: INTEGER); +(**returns in i the integer constant at the current position according to the format + + integer = digit {digit} | digit {hexDigit} "H". + hexDigit = digit | "A" | "B" | "C" | "D" | "E" | "F". +*) + END Int; + + + PROCEDURE Real*(VAR x: REAL); +(**returns in x the real constant at the current position according to the format + + real = digit {digit} "." {digit} [ScaleFactor]. + ScaleFactor = "E" ["+" | "-"] digit {digit}. +*) + END Real; + + + PROCEDURE String*(VAR str: ARRAY OF CHAR); +(**returns in str the string at the current position according to the format + + string = """ {character} """ | digit {hexdigit} "X" . +*) + END String; + + + PROCEDURE Name*(VAR name: ARRAY OF CHAR); +(**Name(s) returns in s the sequence of graphical (non-whitspace) characters at the current position*) + END Name; + +END In. diff --git a/lib/obnc/InTest.obn b/lib/obnc/InTest.obn new file mode 100644 index 0000000..303366b --- /dev/null +++ b/lib/obnc/InTest.obn @@ -0,0 +1,52 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE InTest; + + IMPORT In; + + CONST + eps = 0.001; + + VAR + ch: CHAR; + n: INTEGER; + x: REAL; + s: ARRAY 12 OF CHAR; + +BEGIN + In.Char(ch); + ASSERT(In.Done); + ASSERT(ch = "a"); + + In.Int(n); + ASSERT(In.Done); + ASSERT(n = 37); + + In.Real(x); + ASSERT(In.Done); + ASSERT(x >= 3.14 - eps); + ASSERT(x <= 3.14 + eps); + + In.String(s); + ASSERT(In.Done); + ASSERT(s = "foo bar"); + + In.Name(s); + ASSERT(In.Done); + ASSERT(s = "foo/bar") +END InTest. diff --git a/lib/obnc/InTest.sh b/lib/obnc/InTest.sh new file mode 100755 index 0000000..5fc28fa --- /dev/null +++ b/lib/obnc/InTest.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -e + +input='a +37 +3.14 +"foo bar" +foo/bar' + +echo "$input" | ./InTest diff --git a/lib/obnc/Input.c b/lib/obnc/Input.c new file mode 100644 index 0000000..1898c5c --- /dev/null +++ b/lib/obnc/Input.c @@ -0,0 +1,179 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include ".obnc/Input.h" +#include "../../src/Util.h" +#include +#include +#include /*POSIX*/ +#include /*POSIX*/ +#include +#include +#include +#include + +OBNC_LONGI int Input_TimeUnit_ = (OBNC_LONGI int) CLOCKS_PER_SEC; + +static int IsAscii(const SDL_Event *event) +{ + Uint16 inputChar; + int isAsciiChar, result; + + if (event->type == SDL_KEYDOWN) { + inputChar = event->key.keysym.unicode; + isAsciiChar = (inputChar & 0xFF80) == 0; + result = (inputChar != '\0') && isAsciiChar; + } else { + result = 0; + } + return result; +} + + +OBNC_LONGI int Input_Available_(void) +{ + SDL_Event events[10]; + int count, result, i; + + result = 0; + if (SDL_GetVideoSurface() != NULL) { + SDL_PumpEvents(); + /*search the event queue for key down events*/ + count = SDL_PeepEvents(events, LEN(events), SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYDOWN)); + if (count >= 0) { + for (i = 0; i < count; i++) { + if (IsAscii(&(events[i]))) { + result++; + } + } + if (result == 0) { + /*remove events from the queue to allow new ones to enter*/ + count = SDL_PeepEvents(events, LEN(events), SDL_GETEVENT, -1); + for (i = 0; i < count; i++) { + if (events[i].type == SDL_QUIT) { + exit(EXIT_SUCCESS); + } + } + } + } else { + fprintf(stderr, "SDL_PeepEvents: %s\n", SDL_GetError()); + } + } else { + fprintf(stderr, "No display surface\n"); + } + return result; +} + + +static void ReadGUI(char *ch) +{ + SDL_Event event; + int done; + + do { + done = SDL_WaitEvent(&event); + } while (! done || ! IsAscii(&event)); + if (done) { + *ch = event.key.keysym.unicode; + } +} + + +static void ReadCLI(char *ch) +{ + struct termios savedState, newState; + int error, inputChar, isAscii; + + *ch = '\0'; + error = tcgetattr(STDIN_FILENO, &savedState); + if (! error) { + newState = savedState; + newState.c_lflag &= ~(ECHO | ICANON); + newState.c_cc[VMIN] = 1; + error = tcsetattr(STDIN_FILENO, TCSANOW, &newState); + if (! error) { + do { + inputChar = getchar(); + isAscii = (inputChar >= 0) && (inputChar < 128); + } while (! isAscii); + *ch = inputChar; + error = tcsetattr(STDIN_FILENO, TCSANOW, &savedState); + if (error) { + fprintf(stderr, "tcsetattr: %s\n", strerror(errno)); + } + } else { + fprintf(stderr, "tcsetattr: %s\n", strerror(errno)); + } + } else { + fprintf(stderr, "tcgetattr: %s\n", strerror(errno)); + } +} + + +void Input_Read_(char *ch) +{ + if (SDL_GetVideoSurface() != NULL) { + ReadGUI(ch); + } else { + ReadCLI(ch); + } +} + + +void Input_Mouse_(OBNC_LONGI unsigned int *keys, OBNC_LONGI int *x, OBNC_LONGI int *y) +{ + SDL_Surface *display; + int x0, y0; + Uint8 buttons; + int leftPressed, middlePressed, rightPressed; + + display = SDL_GetVideoSurface(); + if (display != NULL) { + SDL_PumpEvents(); + buttons = SDL_GetMouseState(&x0, &y0); + *x = x0; + *y = y0; + *y = display->h - 1 - *y; + + leftPressed = (buttons & SDL_BUTTON_LMASK) != 0; + middlePressed = (buttons & SDL_BUTTON_MMASK) != 0; + rightPressed = (buttons & SDL_BUTTON_RMASK) != 0; + + *keys = (OBNC_LONGI unsigned int) (leftPressed << 2) | (middlePressed << 1) | rightPressed; + } else { + fprintf(stderr, "No display surface\n"); + exit(EXIT_FAILURE); + } +} + + +void Input_SetMouseLimits_(OBNC_LONGI int w, OBNC_LONGI int h) +{ + fprintf(stderr, "Function Input_SetMouseLimits is not implemented\n"); +} + + +OBNC_LONGI int Input_Time_(void) +{ + return (OBNC_LONGI int) clock(); +} + + +void Input_Init(void) +{ + /*do nothing*/ +} diff --git a/lib/obnc/Input.env b/lib/obnc/Input.env new file mode 100644 index 0000000..73f78ce --- /dev/null +++ b/lib/obnc/Input.env @@ -0,0 +1 @@ +LDLIBS=-lSDL diff --git a/lib/obnc/Input.obn b/lib/obnc/Input.obn new file mode 100644 index 0000000..bf9bb58 --- /dev/null +++ b/lib/obnc/Input.obn @@ -0,0 +1,50 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Input; +(**Access to keyboard, mouse and clock + +Implements the basic library module from "The Oakwood Guidelines for Oberon-2 Compiler Developers".*) + +(*implemented in C*) + + VAR + TimeUnit*: INTEGER; (**clock ticks per second*) + + PROCEDURE Available*(): INTEGER; +(**returns the number of characters in the keyboard buffer*) + RETURN 0 (*dummy value*) + END Available; + + PROCEDURE Read*(VAR ch: CHAR); +(**returns (and removes) the next character from the keyboad buffer. If the buffer is empty, Read waits until a key is pressed.*) + END Read; + + PROCEDURE Mouse*(VAR keys: SET; VAR x, y: INTEGER); +(**returns the current mouse position (x, y) in pixels relative to the lower left corner of the screen. keys is the set of the currently pressed mouse keys (left = 2, middle = 1, right = 0).*) + END Mouse; + + PROCEDURE SetMouseLimits*(w, h: INTEGER); +(**defines the rectangle where the mouse moves (in pixels). Subsequent calls to the operation Mouse will return coordinates for x in the range 0 .. w - 1 and y in the range 0 .. h - 1.*) + END SetMouseLimits; + + PROCEDURE Time*(): INTEGER; +(**returns the time elapsed since system startup in units of size 1 / TimeUnit seconds*) + RETURN 0 (*dummy value*) + END Time; + +END Input. diff --git a/lib/obnc/InputTest.obn b/lib/obnc/InputTest.obn new file mode 100644 index 0000000..b1f9e00 --- /dev/null +++ b/lib/obnc/InputTest.obn @@ -0,0 +1,103 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE InputTest; + + IMPORT Input, Out, XYplane; +(* + PROCEDURE TestAvailable; + VAR n: INTEGER; ch: CHAR; + BEGIN + Out.String("Press any key ... "); + Out.Ln; + REPEAT + n := Input.Available() + UNTIL n # 0; + ASSERT(n > 0); + Out.String("OK"); + Out.Ln; + Input.Read(ch) + END TestAvailable; + + + PROCEDURE TestRead; + VAR ch: CHAR; + BEGIN + Out.String("Press space ... "); + Out.Ln; + Input.Read(ch); + ASSERT(ch = " "); + Out.String("OK"); + Out.Ln; + + Out.String("Press $ ... "); + Out.Ln; + Input.Read(ch); + ASSERT(ch = "$"); + Out.String("OK"); + Out.Ln + END TestRead; + + + PROCEDURE TestButton(button: INTEGER); + VAR buttonLabels: ARRAY 3, 8 OF CHAR; + buttons: SET; x, y: INTEGER; + BEGIN + buttonLabels[0] := "right"; + buttonLabels[1] := "middle"; + buttonLabels[2] := "left"; + Out.String("Press "); + Out.String(buttonLabels[button]); + Out.String(" mouse button ... "); + Out.Ln; + REPEAT + Input.Mouse(buttons, x, y); + UNTIL buttons # {}; + ASSERT(button IN buttons); + ASSERT(x >= 0); + ASSERT(y >= 0); + Out.String("OK"); + Out.Ln; + REPEAT (*wait until button has been released*) + Input.Mouse(buttons, x, y); + UNTIL buttons = {} + END TestButton; + + + PROCEDURE TestMouse; + BEGIN + TestButton(0); + TestButton(1); + TestButton(2) + END TestMouse; + + + PROCEDURE TestTime; + BEGIN + ASSERT(Input.TimeUnit > 0); + ASSERT(Input.Time() > 0) + END TestTime; + + +BEGIN + XYplane.Open; + TestAvailable; + TestRead; + TestMouse; + TestTime +*) +END InputTest. diff --git a/lib/obnc/InputTest.sh b/lib/obnc/InputTest.sh new file mode 100755 index 0000000..66e193c --- /dev/null +++ b/lib/obnc/InputTest.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -e + +if false; then + ./InputTest >/dev/null & + pid="$!" + wid="$(xdotool search --sync --onlyvisible --pid "$pid")" + + xdotool key --window "$wid" k + xdotool key --window "$wid" space + xdotool key --window "$wid" dollar + + #save mouse position (X, Y and SCREEN) + eval "$(xdotool getmouselocation --shell)" + + xdotool mousemove --window "$wid" 0 0 click 3 + xdotool mousemove --window "$wid" 0 0 click 2 + xdotool mousemove --window "$wid" 0 0 click 1 + + #restore mouse position + xdotool mousemove --screen $SCREEN $X $Y +fi diff --git a/lib/obnc/Math.c b/lib/obnc/Math.c new file mode 100644 index 0000000..83fb5c5 --- /dev/null +++ b/lib/obnc/Math.c @@ -0,0 +1,141 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include ".obnc/Math.h" +#include + +OBNC_LONGR double Math_sqrt_(OBNC_LONGR double x) +{ + return sqrt(x); +} + + +OBNC_LONGR double Math_power_(OBNC_LONGR double base, OBNC_LONGR double exp) +{ + return pow(base, exp); +} + + +OBNC_LONGR double Math_exp_(OBNC_LONGR double x) +{ + return exp(x); +} + + +OBNC_LONGR double Math_ln_(OBNC_LONGR double x) +{ + return log(x); +} + + +OBNC_LONGR double Math_log_(OBNC_LONGR double x, OBNC_LONGR double base) +{ + return log(x) / log(base); +} + + +OBNC_LONGR double Math_round_(OBNC_LONGR double x) +{ + return floor(x + 0.5); +} + + +OBNC_LONGR double Math_sin_(OBNC_LONGR double x) +{ + return sin(x); +} + + +OBNC_LONGR double Math_cos_(OBNC_LONGR double x) +{ + return cos(x); +} + + +OBNC_LONGR double Math_tan_(OBNC_LONGR double x) +{ + return tan(x); +} + + +OBNC_LONGR double Math_arcsin_(OBNC_LONGR double x) +{ + return asin(x); +} + + +OBNC_LONGR double Math_arccos_(OBNC_LONGR double x) +{ + return acos(x); +} + + +OBNC_LONGR double Math_arctan_(OBNC_LONGR double x) +{ + return atan(x); +} + + +OBNC_LONGR double Math_arctan2_(OBNC_LONGR double y, OBNC_LONGR double x) +{ + return atan2(y, x); +} + + +OBNC_LONGR double Math_sinh_(OBNC_LONGR double x) +{ + return sinh(x); +} + + +OBNC_LONGR double Math_cosh_(OBNC_LONGR double x) +{ + return cosh(x); +} + + +OBNC_LONGR double Math_tanh_(OBNC_LONGR double x) +{ + return tanh(x); +} + + +OBNC_LONGR double Math_arcsinh_(OBNC_LONGR double x) +{ + /*asinh is not included in ANSI C math.h*/ + return log(x + sqrt(x * x + 1)); +} + + +OBNC_LONGR double Math_arccosh_(OBNC_LONGR double x) +{ + /*acosh is not included in ANSI C math.h*/ + return log(x + sqrt(x * x - 1)); +} + + +OBNC_LONGR double Math_arctanh_(OBNC_LONGR double x) +{ + /*atanh is not included in ANSI C math.h*/ + return (log(1 + x) - log(1 - x)) / 2; +} + + +void Math_Init(void) +{ + /*do nothing*/ +} diff --git a/lib/obnc/Math.env b/lib/obnc/Math.env new file mode 100644 index 0000000..fc7fd98 --- /dev/null +++ b/lib/obnc/Math.env @@ -0,0 +1 @@ +LDLIBS=-lm diff --git a/lib/obnc/Math.obn b/lib/obnc/Math.obn new file mode 100644 index 0000000..d9d0c8a --- /dev/null +++ b/lib/obnc/Math.obn @@ -0,0 +1,143 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Math; +(**General purpose mathematical functions + +Implements the basic library module from "The Oakwood Guidelines for Oberon-2 Compiler Developers".*) + +(*implemented in C*) + + CONST + pi* = 3.14159265358979; + e* = 2.71828182845905; + dummy = 0.0; + + PROCEDURE sqrt*(x: REAL): REAL; +(**returns the square root of x, where x must be positive*) + RETURN dummy + END sqrt; + + + PROCEDURE power*(base, exp: REAL): REAL; +(**returns base raised to exp*) + RETURN dummy + END power; + + + PROCEDURE exp*(x: REAL): REAL; +(**returns the constant e raised to x*) + RETURN dummy + END exp; + + + PROCEDURE ln*(x: REAL): REAL; +(**returns the natural logarithm of x with base e*) + RETURN dummy + END ln; + + + PROCEDURE log*(x, base: REAL): REAL; +(**log(x, b) returns the logarithm of x with base b*) + RETURN dummy + END log; + + + PROCEDURE round*(x: REAL): REAL; +(**returns x rounded to the nearest integer. If the fraction part of x is in range 0.0 to 0.5 then the result is the largest integer not greater than x, otherwise the result is x rounded up to the next highest whole number. Note that integer values cannot always be exactly represented in REAL format.*) + RETURN dummy + END round; + + + PROCEDURE sin*(x: REAL): REAL; +(**returns the sine of a radian value x*) + RETURN dummy + END sin; + + + PROCEDURE cos*(x: REAL): REAL; +(**returns the cosine of a radian value x*) + RETURN dummy + END cos; + + + PROCEDURE tan*(x: REAL): REAL; +(**returns the tangent of a radian value x*) + RETURN dummy + END tan; + + + PROCEDURE arcsin*(x: REAL): REAL; +(**returns the inverse sine of x in radians, where -1 <= x <= 1*) + RETURN dummy + END arcsin; + + + PROCEDURE arccos*(x: REAL): REAL; +(**returns the inverse cosine of x in radians, where -1 <= x <= 1*) + RETURN dummy + END arccos; + + + PROCEDURE arctan*(x: REAL): REAL; +(**returns the inverse tangent of x in radians, where -1 <= x <= 1*) + RETURN dummy + END arctan; + + + PROCEDURE arctan2*(y, x: REAL): REAL; +(**returns the inverse tangent in radians of y/x based on the signs of both values to determine the correct quadrant.*) + RETURN dummy + END arctan2; + + + PROCEDURE sinh*(x: REAL): REAL; +(**returns the hyperbolic sine of x*) + RETURN dummy + END sinh; + + + PROCEDURE cosh*(x: REAL): REAL; +(**returns the hyperbolic cosine of x*) + RETURN dummy + END cosh; + + + PROCEDURE tanh*(x: REAL): REAL; +(**returns the hyperbolic tangent of x*) + RETURN dummy + END tanh; + + + PROCEDURE arcsinh*(x: REAL): REAL; +(**returns the inverse hyperbolic sine of x*) + RETURN dummy + END arcsinh; + + + PROCEDURE arccosh*(x: REAL): REAL; +(**returns the inverse hyperbolic cosine of x*) + RETURN dummy + END arccosh; + + + PROCEDURE arctanh*(x: REAL): REAL; +(**returns the inverse hyperbolic tangent of x*) + RETURN dummy + END arctanh; + +END Math. diff --git a/lib/obnc/MathTest.obn b/lib/obnc/MathTest.obn new file mode 100644 index 0000000..4512ae2 --- /dev/null +++ b/lib/obnc/MathTest.obn @@ -0,0 +1,94 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE MathTest; + + IMPORT Math; + + CONST + eps = 0.01; + +BEGIN + ASSERT(ABS(Math.sqrt(1.0) - 1.0) < eps); + ASSERT(ABS(Math.sqrt(4.0) - 2.0) < eps); + + ASSERT(ABS(Math.power(0.0, 1.0) - 0.0) < eps); + ASSERT(ABS(Math.power(1.0, 0.0) - 1.0) < eps); + ASSERT(ABS(Math.power(2.0, 3.0) - 8.0) < eps); + ASSERT(ABS(Math.power(2.0, -3.0) - 1.0 / 8.0) < eps); + + ASSERT(ABS(Math.exp(0.0) - 1.0) < eps); + ASSERT(ABS(Math.exp(1.0) - Math.e) < eps); + ASSERT(ABS(Math.exp(2.0) - Math.e * Math.e) < eps); + ASSERT(ABS(Math.exp(-2.0) - 1.0 / Math.e / Math.e) < eps); + + ASSERT(ABS(Math.ln(1.0) - 0.0) < eps); + ASSERT(ABS(Math.ln(Math.e) - 1.0) < eps); + ASSERT(ABS(Math.ln(Math.e * Math.e) - 2.0) < eps); + + ASSERT(ABS(Math.log(1.0, 2.0) - 0.0) < eps); + ASSERT(ABS(Math.log(Math.e, Math.e) - 1.0) < eps); + ASSERT(ABS(Math.log(100.0, 10.0) - 2.0) < eps); + + ASSERT(ABS(Math.round(0.4) - 0.0) < eps); + ASSERT(ABS(Math.round(-0.4) - 0.0) < eps); + ASSERT(ABS(Math.round(0.6) - 1.0) < eps); + ASSERT(ABS(Math.round(-0.6) - (-1.0)) < eps); + + ASSERT(ABS(Math.sin(0.0) - 0.0) < eps); + ASSERT(ABS(Math.sin(Math.pi / 6.0) - 0.5) < eps); + ASSERT(ABS(Math.sin(Math.pi / 2.0) - 1.0) < eps); + + ASSERT(ABS(Math.cos(0.0) - 1.0) < eps); + ASSERT(ABS(Math.cos(Math.pi / 3.0) - 0.5) < eps); + ASSERT(ABS(Math.cos(Math.pi / 2.0) - 0.0) < eps); + + ASSERT(ABS(Math.tan(0.0) - 0.0) < eps); + ASSERT(ABS(Math.tan(Math.pi / 4.0) - 1.0) < eps); + + ASSERT(ABS(Math.arcsin(0.0) - 0.0) < eps); + ASSERT(ABS(Math.arcsin(0.5) - Math.pi / 6.0) < eps); + ASSERT(ABS(Math.arcsin(1.0) - Math.pi / 2.0) < eps); + + ASSERT(ABS(Math.arccos(1.0) - 0.0) < eps); + ASSERT(ABS(Math.arccos(0.5) - Math.pi / 3.0) < eps); + ASSERT(ABS(Math.arccos(0.0) - Math.pi / 2.0) < eps); + + ASSERT(ABS(Math.arctan(0.0) - 0.0) < eps); + ASSERT(ABS(Math.arctan(1.0) - Math.pi / 4.0) < eps); + + ASSERT(ABS(Math.arctan2(0.0, 2.0) - 0.0) < eps); + ASSERT(ABS(Math.arctan2(2.0, 2.0) - Math.pi / 4.0) < eps); + + ASSERT(ABS(Math.sinh(0.0) - 0.0) < eps); + ASSERT(ABS(Math.sinh(1.0) - (Math.e - 1.0 / Math.e) / 2.0) < eps); + + ASSERT(ABS(Math.cosh(0.0) - 1.0) < eps); + ASSERT(ABS(Math.cosh(1.0) - (Math.e + 1.0 / Math.e) / 2.0) < eps); + + ASSERT(ABS(Math.tanh(0.0) - 0.0) < eps); + ASSERT(ABS(Math.tanh(1.0) - (Math.e - 1.0 / Math.e) / (Math.e + 1.0 / Math.e)) < eps); + + ASSERT(ABS(Math.arcsinh(0.0) - 0.0) < eps); + ASSERT(ABS(Math.arcsinh((Math.e - 1.0 / Math.e) / 2.0) - 1.0) < eps); + + ASSERT(ABS(Math.arccosh(1.0) - 0.0) < eps); + ASSERT(ABS(Math.arccosh((Math.e + 1.0 / Math.e) / 2.0) - 1.0) < eps); + + ASSERT(ABS(Math.arctanh(0.0) - 0.0) < eps); + ASSERT(ABS(Math.arctanh((Math.e - 1.0 / Math.e) / (Math.e + 1.0 / Math.e)) - 1.0) < eps) +END MathTest. diff --git a/lib/obnc/OBNC.c b/lib/obnc/OBNC.c new file mode 100644 index 0000000..ce4e0c7 --- /dev/null +++ b/lib/obnc/OBNC.c @@ -0,0 +1,181 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "OBNC.h" +#include +#include +#include +#include + +int OBNC_argc; +char **OBNC_argv; + +void OBNC_Initialize(int argc, char *argv[]) +{ + OBNC_argc = argc; + OBNC_argv = argv; + GC_INIT(); +} + + +long double OBNC_Abs(long double x) +{ + return (x >= 0)? x: -x; +} + + +OBNC_LONGI int OBNC_Ror(OBNC_LONGI int x, OBNC_LONGI int n) +{ + return ((OBNC_LONGI unsigned int) x >> n) | ((OBNC_LONGI unsigned int) x << ((sizeof (OBNC_LONGI int) << 3) - n)); +} + + +OBNC_LONGI int OBNC_Floor(long double x) +{ + return (OBNC_LONGI int) ((x >= 0.0)? x: x - 0.5); +} + + +void *OBNC_Allocate(size_t size, int kind) +{ + void *result = NULL; + + switch (kind) { + case OBNC_REGULAR_ALLOC: + result = GC_MALLOC(size); /*initializes memory to zero like calloc*/ + break; + case OBNC_ATOMIC_ALLOC: + result = GC_MALLOC_ATOMIC(size); + if (result != NULL) { + memset(result, 0, size); + } + break; + case OBNC_ATOMIC_NOINIT_ALLOC: + result = GC_MALLOC_ATOMIC(size); /*no initialization*/ + break; + default: + assert(0); + } + return result; +} + + +void OBNC_Pack(OBNC_LONGR double *x, OBNC_LONGI int n) +{ + /*TODO: implement Pack for long double*/ + + *x = ldexp(*x, n); +} + + +void OBNC_Unpk(OBNC_LONGR double *x, OBNC_LONGI int *n) +{ + /*TODO: implement Unpk for long double*/ + +#ifdef OBNC_CONFIG_USE_LONG_INT + int t; + + *x = frexp(*x, &t); + *n = t; + *x += *x; + (*n)--; +#else + *x = frexp(*x, n); + *x += *x; + (*n)--; +#endif +} + + +int OBNC_Cmp(const char s[], OBNC_LONGI int sLen, const char t[], OBNC_LONGI int tLen, const char file[], int line) +{ + int minLen, i; + + minLen = (sLen < tLen)? sLen: tLen; + i = 0; + while ((i < minLen) && (s[i] != '\0') && (s[i] == t[i])) { + i++; + } + if ((i < 0) || (i >= minLen)) { + OBNC_Abort(OBNC_ORDER_RELATION_EXCEPTION, file, line); + } + + return (unsigned char) s[i] - (unsigned char) t[i]; +} + + +OBNC_LONGI int OBNC_Div(OBNC_LONGI int x, OBNC_LONGI int y) +{ + return (x >= 0)? x / y: (x - OBNC_Mod(x, y)) / y; +} + + +OBNC_LONGI int OBNC_Mod(OBNC_LONGI int x, OBNC_LONGI int y) +{ + return (x >= 0)? x % y: ((x % y) + y) % y; +} + + +OBNC_LONGI int OBNC_Range(OBNC_LONGI int m, OBNC_LONGI int n) +{ + return (m <= n) + ? (((OBNC_LONGI unsigned int) -2) << n) ^ (((OBNC_LONGI unsigned int) -1) << m) + : 0x0u; +} + + +int OBNC_It1(OBNC_LONGI int i, OBNC_LONGI int n, const char file[], int line) +{ + if ((i < 0) || (i >= n)) { + OBNC_Abort(OBNC_ARRAY_INDEX_EXCEPTION, file, line); + } + return i; +} + + +void OBNC_Abort(int exception, const char file[], int line) +{ + switch (exception) { + case OBNC_ARRAY_ASSIGNMENT_EXCEPTION: + fprintf(stderr, "exception: destination array too short for assignment (%s:%d)\n", file, line); + break; + case OBNC_ARRAY_INDEX_EXCEPTION: + fprintf(stderr, "exception: array index out of bounds (%s:%d)\n", file, line); + break; + case OBNC_ORDER_RELATION_EXCEPTION: + fprintf(stderr, "exception: non-terminated character array in order relation (%s:%d)\n", file, line); + break; + case OBNC_POINTER_DEREFERENCE_EXCEPTION: + fprintf(stderr, "exception: nil pointer dereference (%s:%d)\n", file, line); + break; + case OBNC_PROCEDURE_CALL_EXCEPTION: + fprintf(stderr, "exception: nil procedure variable call (%s:%d)\n", file, line); + break; + case OBNC_RECORD_ASSIGNMENT_EXCEPTION: + fprintf(stderr, "exception: source in assignment is not an extension of target (%s:%d)\n", file, line); + break; + case OBNC_TYPE_GUARD_EXCEPTION: + fprintf(stderr, "exception: type guard failure (%s:%d)\n", file, line); + break; + case OBNC_CASE_EXP_MATCH_EXCEPTION: + fprintf(stderr, "exception: unmatched expression in case statement (%s:%d)\n", file, line); + break; + default: + assert(0); + } + abort(); +} diff --git a/lib/obnc/OBNC.env b/lib/obnc/OBNC.env new file mode 100644 index 0000000..93029f4 --- /dev/null +++ b/lib/obnc/OBNC.env @@ -0,0 +1 @@ +LDLIBS="-lgc -lm" diff --git a/lib/obnc/OBNC.h b/lib/obnc/OBNC.h new file mode 100644 index 0000000..06d807d --- /dev/null +++ b/lib/obnc/OBNC.h @@ -0,0 +1,221 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef OBNC_H +#define OBNC_H + +#include "OBNCConfig.h" +#include +#include +#include +#include +#include +#include + +/*Run-time exceptions*/ + +#define OBNC_ARRAY_ASSIGNMENT_EXCEPTION 0 +#define OBNC_ARRAY_INDEX_EXCEPTION 1 +#define OBNC_ORDER_RELATION_EXCEPTION 2 +#define OBNC_POINTER_DEREFERENCE_EXCEPTION 3 +#define OBNC_PROCEDURE_CALL_EXCEPTION 4 +#define OBNC_RECORD_ASSIGNMENT_EXCEPTION 5 +#define OBNC_TYPE_GUARD_EXCEPTION 6 +#define OBNC_CASE_EXP_MATCH_EXCEPTION 7 + +/*Memory allocation kinds*/ + +#define OBNC_REGULAR_ALLOC 0 +#define OBNC_ATOMIC_ALLOC 1 +#define OBNC_ATOMIC_NOINIT_ALLOC 2 + +/*Size of type INTEGER and SET*/ + +#ifdef OBNC_CONFIG_USE_LONG_INT + #define OBNC_LONGI long + #define OBNC_INT_MOD "l" +#else + #define OBNC_LONGI + #define OBNC_INT_MOD "" +#endif + +/*Size of type REAL*/ + +#ifdef OBNC_CONFIG_USE_LONG_REAL + #define OBNC_LONGR long + #define OBNC_REAL_MOD_R "L" + #define OBNC_REAL_MOD_W "L" +#else + #define OBNC_LONGR + #define OBNC_REAL_MOD_R "l" + #define OBNC_REAL_MOD_W "" +#endif + +/*Predefined function procedures*/ + +#ifdef OBNC_CONFIG_USE_LONG_INT + #define OBNC_ABS_INT(x) labs(x) +#else + #define OBNC_ABS_INT(x) abs(x) +#endif +#ifdef OBNC_CONFIG_USE_LONG_REAL + #define OBNC_ABS_FLT(x) OBNC_Abs(x) +#else + #define OBNC_ABS_FLT(x) fabs(x) +#endif +#define OBNC_ODD(x) (((x) & 1) == 1) +#define OBNC_LSL(x, n) ((OBNC_LONGI int) (x) << (n)) +#define OBNC_ASR(x, n) ((OBNC_LONGI int) (x) >> (n)) +#define OBNC_ROR(x, n) OBNC_Ror(x, n) + +/*Type conversions*/ + +#ifdef OBNC_CONFIG_USE_LONG_REAL + #define OBNC_FLOOR(x) OBNC_Floor(x) +#else + #define OBNC_FLOOR(x) ((OBNC_LONGI int) floor(x)) +#endif +#define OBNC_FLT(x) ((OBNC_LONGR double) (x)) +#define OBNC_ORD(x) ((OBNC_LONGI int) (x)) +#define OBNC_CHR(x) ((char) (x)) + +/*Predefined proper procedures*/ + +#define OBNC_INC(v) (v)++ +#define OBNC_INC_N(v, n) (v) += (n) + +#define OBNC_DEC(v) (v)-- +#define OBNC_DEC_N(v, n) (v) -= (n) + +#define OBNC_INCL(v, x) (v) |= (1 << (x)) +#define OBNC_EXCL(v, x) (v) &= ~(1 << (x)) + +#define OBNC_NEW(v, vtd, vHeapType, allocKind) \ + { \ + vHeapType *p = OBNC_Allocate(sizeof *p, (allocKind)); \ + if (p != NULL) { \ + p->td = (vtd); \ + (v) = &p->fields; \ + } else { \ + (v) = NULL; \ + }\ + } + +#define OBNC_NEW_ANON(v, allocKind) (v) = OBNC_Allocate(sizeof *(v), (allocKind)) + +#define OBNC_ASSERT(b, oberonFile, line) \ + if (! (b)) { \ + fprintf(stderr, "Assertion failed at line %d in file %s\n", (line), (oberonFile)); \ + abort(); \ + } + +#define OBNC_PACK(x, n) OBNC_Pack(&(x), n) +#define OBNC_UNPK(x, n) OBNC_Unpk(&(x), &(n)) + +/*Type descriptor accessor*/ + +#define OBNC_TD(ptr, heapType) (*(const OBNC_Td **) ((char *) (ptr) - offsetof (heapType, fields))) + +/*Operators*/ + +#define OBNC_CMP(str1, len1, str2, len2) OBNC_Cmp((str1), (len1), (str2), (len2), __FILE__, __LINE__) + +#define OBNC_IS(td, typeID, extLevel) (((extLevel) < (td)->nids) && ((td)->ids[extLevel] == (typeID))) + +#define OBNC_DIV(x, y) OBNC_Div(x, y) + +#define OBNC_MOD(x, y) OBNC_Mod(x, y) + +#define OBNC_RANGE(m, n) OBNC_Range(m, n) + +#define OBNC_IN(x, A) ((((OBNC_LONGI unsigned int) 1) << (x)) & (A)) + +/*Structured assignments*/ + +#define OBNC_COPY_ARRAY(src, dst, n) memcpy(dst, src, (n) * sizeof (src)[0]) + +/*Traps*/ + +#define OBNC_IT(index, length) \ + (((OBNC_LONGI unsigned int) (index) < (OBNC_LONGI int) (length)) \ + ? (index) \ + : (OBNC_Abort(OBNC_ARRAY_INDEX_EXCEPTION, __FILE__, __LINE__), (index))) + +#define OBNC_IT1(index, length) (OBNC_It1((index), (length), __FILE__, __LINE__)) + +#define OBNC_RTT(recPtr, td, typeID, extLevel) \ + (OBNC_IS((td), (typeID), (extLevel)) \ + ? (recPtr) \ + : (OBNC_Abort(OBNC_TYPE_GUARD_EXCEPTION, __FILE__, __LINE__), (recPtr))) + +#define OBNC_PTT(ptrPtr, td, typeID, extLevel) \ + (((*(ptrPtr) != NULL) && OBNC_IS((td), (typeID), (extLevel))) \ + ? (ptrPtr) \ + : (OBNC_Abort(OBNC_TYPE_GUARD_EXCEPTION, __FILE__, __LINE__), (ptrPtr))) + +#define OBNC_AAT(sourceLen, targetLen) \ + if (sourceLen > targetLen) { \ + OBNC_Abort(OBNC_ARRAY_ASSIGNMENT_EXCEPTION, __FILE__, __LINE__); \ + } + +#define OBNC_RAT(srcTD, dstTD) \ + if (! (((srcTD)->nids >= (dstTD)->nids) \ + && ((srcTD)->ids[(dstTD)->nids - 1] == (dstTD)->ids[(dstTD)->nids - 1]))) { \ + OBNC_Abort(OBNC_RECORD_ASSIGNMENT_EXCEPTION, __FILE__, __LINE__); \ + } + +#define OBNC_PT(ptr) (((ptr) != NULL)? (ptr): (OBNC_Abort(OBNC_POINTER_DEREFERENCE_EXCEPTION, __FILE__, __LINE__), (ptr))) + +#define OBNC_PCT(ptr) (((ptr) != NULL)? (ptr): (OBNC_Abort(OBNC_PROCEDURE_CALL_EXCEPTION, __FILE__, __LINE__), (ptr))) + +#define OBNC_CT OBNC_Abort(OBNC_CASE_EXP_MATCH_EXCEPTION, __FILE__, __LINE__) + +typedef struct { + const int *const *ids; /*basetype IDs*/ + const int nids; /*length of ids*/ +} OBNC_Td; + +extern int OBNC_argc; +extern char **OBNC_argv; + +void OBNC_Initialize(int argc, char *argv[]); + +long double OBNC_Abs(long double x); + +OBNC_LONGI int OBNC_Ror(OBNC_LONGI int x, OBNC_LONGI int n); + +OBNC_LONGI int OBNC_Floor(long double x); + +void *OBNC_Allocate(size_t size, int kind); + +void OBNC_Pack(OBNC_LONGR double *x, OBNC_LONGI int n); + +void OBNC_Unpk(OBNC_LONGR double *x, OBNC_LONGI int *n); + +int OBNC_Cmp(const char s[], OBNC_LONGI int sLen, const char t[], OBNC_LONGI int tLen, const char file[], int line); + +OBNC_LONGI int OBNC_Div(OBNC_LONGI int x, OBNC_LONGI int y); + +OBNC_LONGI int OBNC_Mod(OBNC_LONGI int x, OBNC_LONGI int y); + +OBNC_LONGI int OBNC_Range(OBNC_LONGI int m, OBNC_LONGI int n); + +int OBNC_It1(OBNC_LONGI int index, OBNC_LONGI int length, const char file[], int line); + +void OBNC_Abort(int exception, const char file[], int line); + +#endif diff --git a/lib/obnc/OBNCTest.c b/lib/obnc/OBNCTest.c new file mode 100644 index 0000000..01df673 --- /dev/null +++ b/lib/obnc/OBNCTest.c @@ -0,0 +1,229 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "OBNC.h" +#include "../../src/Util.h" +#include +#include +#include + +#define INTEGER_BITS (sizeof (OBNC_LONGI int) * CHAR_BIT) + +static void TestABS(void) +{ + assert(OBNC_ABS_INT(-1) == 1); + assert(OBNC_ABS_INT(0) == 0); + assert(OBNC_ABS_INT(1) == 1); + + assert(OBNC_ABS_FLT(-1.0) == 1.0); + assert(OBNC_ABS_FLT(0.0) == 0.0); + assert(OBNC_ABS_FLT(1.0) == 1.0); +} + + +static void TestODD(void) +{ + assert(! OBNC_ODD(-2)); + assert(OBNC_ODD(-1)); + assert(! OBNC_ODD(0)); + assert(OBNC_ODD(1)); + assert(! OBNC_ODD(2)); +} + + +static void TestLSL(void) +{ + assert(OBNC_LSL(0, 0) == 0); + assert(OBNC_LSL(0, 1) == 0); + assert(OBNC_LSL(1, 0) == 1); + assert(OBNC_LSL(1, 1) == 2); +} + + +static void TestASR(void) +{ + assert(OBNC_ASR(0, 0) == 0); + assert(OBNC_ASR(0, 1) == 0); + assert(OBNC_ASR(1, 0) == 1); + assert(OBNC_ASR(1, 1) == 0); + assert(OBNC_ASR(~0, 1) == ~0); +} + + +static void TestROR(void) +{ + assert(OBNC_ROR(0, 1) == 0); + assert(OBNC_ROR(2, 1) == 1); + assert(OBNC_ROR(1, 2) == (OBNC_LONGI int) 1 << (INTEGER_BITS - 2)); +} + + +static void TestFLOOR(void) +{ + assert(OBNC_FLOOR(-1.5) == -2); + assert(OBNC_FLOOR(0.0) == 0); + assert(OBNC_FLOOR(1.5) == 1); +} + + +static void TestFLT(void) +{ + assert(OBNC_FLT(-1) == -1.0); + assert(OBNC_FLT(0) == 0.0); + assert(OBNC_FLT(1) == 1.0); +} + + +static void TestORD(void) +{ + assert(OBNC_ORD('\0') == 0); + assert(OBNC_ORD('\1') == 1); +} + + +static void TestCHR(void) +{ + assert(OBNC_CHR(0) == '\0'); + assert(OBNC_CHR(1) == '\1'); +} + + +static void TestINC(void) +{ + int x; + + x = 0; + OBNC_INC(x); + assert(x == 1); + + x = 0; + OBNC_INC_N(x, 10); + assert(x == 10); +} + + +static void TestDEC(void) +{ + int x; + + x = 0; + OBNC_DEC(x); + assert(x == -1); + + x = 0; + OBNC_DEC_N(x, 10); + assert(x == -10); +} + + +static void TestINCL(void) +{ + int A; + + A = 0; + OBNC_INCL(A, 0); + assert(A == 1); +} + + +static void TestEXCL(void) +{ + int A; + + A = 1; + OBNC_EXCL(A, 0); + assert(A == 0); +} + + +static void TestNEW(void) +{ + struct { int *typeID; int x; } *v; + + OBNC_NEW_ANON(v, OBNC_ATOMIC_NOINIT_ALLOC); + assert(v != NULL); + v->x = 1; + assert(v->x == 1); +} + + +static void TestASSERT(void) +{ + OBNC_ASSERT(1, "", -1); + /*can't test failure here*/ +} + + +static void TestPACK(void) +{ + const double eps = 0.01; + OBNC_LONGR double x; + + x = 1.0; + OBNC_PACK(x, 2); + assert(OBNC_ABS_FLT(x - 4.0) < eps); +} + + +static void TestUNPK(void) +{ + OBNC_LONGR double x; + OBNC_LONGI int n; + + x = 4.0; + OBNC_UNPK(x, n); + assert(x >= 1.0); + assert(x < 2.0); + assert(n == 2); +} + + +static void TestCMP(void) +{ + char s[4]; + + strcpy(s, "foo"); + assert(OBNC_CMP(s, LEN(s), "foo", LEN("foo")) == 0); + assert(OBNC_CMP(s, LEN(s), "fool", LEN("fool")) < 0); + assert(OBNC_CMP(s, LEN(s), "fo", LEN("fo")) > 0); +} + + +int main(void) +{ + Util_Init(); + TestABS(); + TestODD(); + TestLSL(); + TestASR(); + TestROR(); + TestFLOOR(); + TestFLT(); + TestORD(); + TestCHR(); + TestINC(); + TestDEC(); + TestINCL(); + TestEXCL(); + TestNEW(); + TestASSERT(); + TestPACK(); + TestUNPK(); + TestCMP(); + + return 0; +} diff --git a/lib/obnc/Out.c b/lib/obnc/Out.c new file mode 100644 index 0000000..00bc1f9 --- /dev/null +++ b/lib/obnc/Out.c @@ -0,0 +1,64 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include ".obnc/Out.h" +#include +#include + +void Out_Open_(void) +{ + /*do nothing*/ +} + + +void Out_Char_(char ch) +{ + putchar(ch); +} + + +void Out_String_(const char s[], OBNC_LONGI int sLen) +{ + int charCount; + + charCount = printf("%.*s", (int) sLen, s); + OBNC_IT(charCount, sLen); /*trap if not null-terminated*/ +} + + +void Out_Int_(OBNC_LONGI int i, OBNC_LONGI int n) +{ + printf("%*" OBNC_INT_MOD "d", (int) n, i); +} + + +void Out_Real_(OBNC_LONGR double x, OBNC_LONGI int n) +{ + printf("%*" OBNC_REAL_MOD_W "E", (int) n, x); +} + + +void Out_Ln_(void) +{ + putchar('\n'); +} + + +void Out_Init(void) +{ + /*do nothing*/ +} diff --git a/lib/obnc/Out.obn b/lib/obnc/Out.obn new file mode 100644 index 0000000..a5dff2f --- /dev/null +++ b/lib/obnc/Out.obn @@ -0,0 +1,54 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Out; +(**Output to the standard output stream + +Implements the basic library module from "The Oakwood Guidelines for Oberon-2 Compiler Developers".*) + +(*implemented in C*) + + PROCEDURE Open*; +(**does nothing (included for compatibility with "The Oakwood Guidelines")*) + END Open; + + + PROCEDURE Char*(ch: CHAR); +(**writes the character ch to the end of the output stream*) + END Char; + + + PROCEDURE String*(s: ARRAY OF CHAR); +(**writes the null-terminated character sequence s to the end of the output stream (without 0X).*) + END String; + + + PROCEDURE Int*(i, n: INTEGER); +(**writes the integer i to the end of the output stream. If the textual representation of i requires m characters, i is right adjusted in a field of Max(n, m) characters padded with blanks at the left end. A plus sign is not written.*) + END Int; + + + PROCEDURE Real*(x: REAL; n: INTEGER); +(**writes the real number x to the end of the output stream using an exponential form. If the textual representation of x requires m characters (including a two-digit signed exponent), x is right adjusted in a field of Max(n, m) characters padded with blanks at the left end. A plus sign of the mantissa is not written.*) + END Real; + + + PROCEDURE Ln*; +(**writes an end-of-line symbol to the end of the output stream*) + END Ln; + +END Out. diff --git a/lib/obnc/OutTest.obn b/lib/obnc/OutTest.obn new file mode 100644 index 0000000..a730058 --- /dev/null +++ b/lib/obnc/OutTest.obn @@ -0,0 +1,36 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE OutTest; + + IMPORT Out; + +BEGIN + Out.Char("a"); Out.Ln; + Out.String("abc"); Out.Ln; + Out.Int(-1, 0); Out.Ln; + Out.Int(-1, 3); Out.Ln; + Out.Int(0, 0); Out.Ln; + Out.Int(1, 0); Out.Ln; + Out.Int(37, 0); Out.Ln; + Out.Real(-1.0, 0); Out.Ln; + Out.Real(0.0, 0); Out.Ln; + Out.Real(0.0, 14); Out.Ln; + Out.Real(1.0, 0); Out.Ln; + Out.Real(37.0, 0); Out.Ln; + Out.Real(0.37, 0); Out.Ln +END OutTest. diff --git a/lib/obnc/OutTest.sh b/lib/obnc/OutTest.sh new file mode 100755 index 0000000..1af3523 --- /dev/null +++ b/lib/obnc/OutTest.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -e + +expectedOutput="a +abc +-1 + -1 +0 +1 +37 +-1.000000E+00 +0.000000E+00 + 0.000000E+00 +1.000000E+00 +3.700000E+01 +3.700000E-01" + +./OutTest | while IFS= read line; do + expectedLine="$(echo "$expectedOutput" | head -n 1)" + if [ "$line" != "$expectedLine" ]; then + echo "test failed: output: \"$line\", expected output: \"$expectedLine\"" >&2 + exit 1 + fi + expectedOutput="$(echo "$expectedOutput" | tail -n +2)" +done diff --git a/lib/obnc/Strings.obn b/lib/obnc/Strings.obn new file mode 100644 index 0000000..f993dcb --- /dev/null +++ b/lib/obnc/Strings.obn @@ -0,0 +1,206 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Strings; +(**Operations on strings + +Implements the basic library module from "The Oakwood Guidelines for Oberon-2 Compiler Developers". All character arrays are assumed to contain 0X as a terminator and positions start at 0.*) + + PROCEDURE Length*(s: ARRAY OF CHAR): INTEGER; +(**Length(s) returns the number of characters in s up to and excluding the first 0X.*) + VAR i: INTEGER; + BEGIN + i := 0; + WHILE s[i] # 0X DO + INC(i) + END + RETURN i + END Length; + + + PROCEDURE Min2(a, b: INTEGER): INTEGER; (*minumum of two values*) + VAR result: INTEGER; + BEGIN + IF a < b THEN + result := a + ELSE + result := b + END + RETURN result + END Min2; + + + PROCEDURE Min3(a, b, c: INTEGER): INTEGER; (*minimum of three values*) + VAR result: INTEGER; + BEGIN + result := a; + IF b < result THEN + result := b + END; + IF c < result THEN + result := c + END + RETURN result + END Min3; + + + PROCEDURE Insert*(source: ARRAY OF CHAR; pos: INTEGER; VAR dest: ARRAY OF CHAR); +(**Insert(src, pos, dst) inserts the string src into the string dst at position pos (0 <= pos <= Length(dst)). If pos = Length(dst), src is appended to dst. If the size of dst is not large enough to hold the result of the operation, the result is truncated so that dst is always terminated with a 0X.*) + VAR sourceLength, destLength, newLength: INTEGER; + i, lim: INTEGER; + BEGIN + destLength := Length(dest); + ASSERT(pos >= 0); + ASSERT(pos <= destLength); + + sourceLength := Length(source); + newLength := Min2(destLength + sourceLength, LEN(dest) - 1); + + (*make room for source in dest*) + dest[newLength] := 0X; + FOR i := newLength - 1 TO pos + sourceLength BY -1 DO + dest[i] := dest[i - sourceLength] + END; + + (*copy source to dest*) + lim := Min2(pos + sourceLength - 1, newLength - 1); + FOR i := pos TO lim DO + dest[i] := source[i - pos]; + END + END Insert; + + + PROCEDURE Append*(extra: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR); +(**Append(s, dst) has the same effect as Insert(s, Length(dst), dst).*) + VAR destLength, newLength: INTEGER; + i: INTEGER; + BEGIN + destLength := Length(dest); + newLength := Min2(destLength + Length(extra), LEN(dest) - 1); + + FOR i := destLength TO newLength - 1 DO + dest[i] := extra[i - destLength] + END; + dest[newLength] := 0X + END Append; + + + PROCEDURE Delete*(VAR s: ARRAY OF CHAR; pos, n: INTEGER); +(**Delete(s, pos, n) deletes n characters from s starting at position pos (0 <= pos <= Length(s)). If n > Length(s) - pos, the new length of s is pos.*) + VAR length, n1, i: INTEGER; + BEGIN + length := Length(s); + ASSERT(pos >= 0); + ASSERT(pos <= length); + ASSERT(n >= 0); + + n1 := Min2(n, length - pos); (*actual number of characters to delete*) + FOR i := pos TO length - n1 DO + s[i] := s[i + n1] + END + END Delete; + + + PROCEDURE Replace*(source: ARRAY OF CHAR; pos: INTEGER; VAR dest: ARRAY OF CHAR); +(**Replace(src, pos, dst) has the same effect as Delete(dst, pos, Length(src)) followed by an Insert(src, pos, dst).*) + VAR destLength, n, i: INTEGER; + BEGIN + destLength := Length(dest); + ASSERT(pos >= 0); + ASSERT(pos <= destLength); + + n := Min2(Length(source), LEN(dest) - 1 - pos); (*actual number of characters to replace*) + + (*replace characters*) + FOR i := 0 TO n - 1 DO + dest[pos + i] := source[i] + END; + + IF pos + n > destLength THEN + dest[pos + n] := 0X + END + END Replace; + + + PROCEDURE Extract*(source: ARRAY OF CHAR; pos, n: INTEGER; VAR dest: ARRAY OF CHAR); +(**Extract(src, pos, n, dst) extracts a substring dst with n characters from position pos (0 <= pos <= Length(src)) in src. If n > Length(src) - pos, dst is only the part of src from pos to the end of src, i.e. Length(src) -1. If the size of dst is not large enough to hold the result of the operation, the result is truncated so that dst is always terminated with a 0X.*) + VAR sourceLength, n1, i: INTEGER; + BEGIN + sourceLength := Length(source); + ASSERT(pos >= 0); + ASSERT(pos <= sourceLength); + + n1 := Min3(n, sourceLength - pos, LEN(dest) - 1); (*actual number of characters to extract*) + FOR i := 0 TO n1 - 1 DO + dest[i] := source[pos + i] + END; + dest[n1] := 0X + END Extract; + + + PROCEDURE Pos*(pattern, s: ARRAY OF CHAR; pos: INTEGER): INTEGER; +(**Pos(pat, s, pos) returns the position of the first occurrence of pat in s. Searching starts at position pos (0 <= pos <= Length(s)). If pat is not found, -1 is returned.*) + VAR result, patLength, sLength: INTEGER; + + PROCEDURE StartsWith(pattern: ARRAY OF CHAR; patLength: INTEGER; s: ARRAY OF CHAR; pos: INTEGER): BOOLEAN; + VAR i: INTEGER; + BEGIN + i := 0; + WHILE (i < patLength) & (pattern[i] = s[pos + i]) DO + INC(i) + END + RETURN i = patLength + END StartsWith; + + BEGIN + sLength := Length(s); + ASSERT(pos >= 0); + ASSERT(pos <= sLength); + result := -1; + IF pattern # "" THEN + patLength := Length(pattern); + DEC(pos); + REPEAT + INC(pos); + WHILE (pos + patLength <= sLength) & (s[pos] # pattern[0]) DO + INC(pos) + END + UNTIL (pos + patLength > sLength) OR StartsWith(pattern, patLength, s, pos); + IF pos + patLength <= sLength THEN + result := pos; + END + END; + ASSERT(result >= -1); + ASSERT(result < sLength) + RETURN result + END Pos; + + + PROCEDURE Cap*(VAR s: ARRAY OF CHAR); +(**Cap(s) replaces each lower case letter within s by its upper case equivalent.*) + VAR i: INTEGER; + BEGIN + i := 0; + WHILE s[i] # 0X DO + IF (s[i] >= "a") & (s[i] <= "z") THEN + s[i] := CHR(ORD("A") + ORD(s[i]) - ORD("a")); + END; + INC(i) + END + END Cap; + +END Strings. diff --git a/lib/obnc/StringsTest.obn b/lib/obnc/StringsTest.obn new file mode 100644 index 0000000..4f0cf4d --- /dev/null +++ b/lib/obnc/StringsTest.obn @@ -0,0 +1,114 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE StringsTest; + + IMPORT Out, Strings; + + VAR + shortStr: ARRAY 4 OF CHAR; + s: ARRAY 14 OF CHAR; + +BEGIN + (*test Length*) + ASSERT(Strings.Length("") = 0); + shortStr := ""; + ASSERT(Strings.Length(shortStr) = 0); + shortStr := 22X; + ASSERT(Strings.Length(shortStr) = 1); + shortStr := "123"; + ASSERT(Strings.Length(shortStr) = 3); + + (*test Insert*) + s := "cde"; + Strings.Insert("ab", 0, s); + ASSERT(s = "abcde"); + s := "adef"; + Strings.Insert("bc", 1, s); + ASSERT(s = "abcdef"); + shortStr := "ade"; + Strings.Insert("bc", 1, shortStr); + ASSERT(shortStr = "abc"); + shortStr := "aef"; + Strings.Insert("bcd", 1, shortStr); + ASSERT(shortStr = "abc"); + s := "foo bar"; + Strings.Insert(" baz", Strings.Length(s), s); + ASSERT(s = "foo bar baz"); + Strings.Insert(" qux qux qux qux qux", Strings.Length(s), s); + ASSERT(s = "foo bar baz q"); + + (*test Append*) + s := ""; + Strings.Append("foo", s); + ASSERT(s = "foo"); + Strings.Append(" bar", s); + ASSERT(s = "foo bar"); + Strings.Append(" baz qux", s); + ASSERT(s = "foo bar baz q"); + + (*test Delete*) + s := "foo bar baz"; + Strings.Delete(s, 11, 4); + ASSERT(s = "foo bar baz"); + Strings.Delete(s, 7, 4); + ASSERT(s = "foo bar"); + Strings.Delete(s, 0, 4); + ASSERT(s = "bar"); + Strings.Delete(s, 1, 10); + ASSERT(s = "b"); + Strings.Delete(s, 0, 0); + ASSERT(s = "b"); + Strings.Delete(s, 0, 1); + ASSERT(s = ""); + + (*test Replace*) + s := "foo bar baz"; + Strings.Replace("qux", 4, s); + ASSERT(s = "foo qux baz"); + s := "foo bar"; + Strings.Replace("qux qux qux", 5, s); + ASSERT(s = "foo bqux qux "); + s := "foo"; + Strings.Replace(" bar", 3, s); + ASSERT(s = "foo bar"); + + (*test Extract*) + Strings.Extract("foo bar", 4, 3, shortStr); + ASSERT(shortStr = "bar"); + Strings.Extract("foo bar", 4, 10, shortStr); + ASSERT(shortStr = "bar"); + Strings.Extract("foo bar", 0, 6, shortStr); + ASSERT(shortStr = "foo"); + Strings.Extract("foo bar", 7, 4, shortStr); + ASSERT(shortStr = ""); + + (*test Pos*) + ASSERT(Strings.Pos("", "", 0) = -1); + ASSERT(Strings.Pos("", "foo", 0) = -1); + ASSERT(Strings.Pos("foo", "foo", 0) = 0); + ASSERT(Strings.Pos("foo", "fool", 0) = 0); + ASSERT(Strings.Pos("ool", "fool", 0) = 1); + ASSERT(Strings.Pos("oo", "fool", 0) = 1); + ASSERT(Strings.Pos("fools", "fool", 0) = -1); + ASSERT(Strings.Pos("ol", "fool", 1) = 2); + + (*test Cap*) + s := "foo Bar BAZ"; + Strings.Cap(s); + ASSERT(s = "FOO BAR BAZ") +END StringsTest. diff --git a/lib/obnc/XYplane.c b/lib/obnc/XYplane.c new file mode 100644 index 0000000..e6a84e9 --- /dev/null +++ b/lib/obnc/XYplane.c @@ -0,0 +1,167 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include ".obnc/XYplane.h" +#include ".obnc/Input.h" +#include "../../src/Util.h" +#include +#include /*SDL 1.2*/ +#include +#include + +static const Uint8 colorPerMode[2] = {0x0, 0xff}; + +OBNC_LONGI int XYplane_X_ = 0; +OBNC_LONGI int XYplane_Y_ = 0; +OBNC_LONGI int XYplane_W_ = 0; +OBNC_LONGI int XYplane_H_ = 0; + +void XYplane_Open_(void) +{ + SDL_Surface *screen; + int error; + + screen = SDL_GetVideoSurface(); + if (screen != NULL) { + SDL_Quit(); + } + error = SDL_Init(SDL_INIT_VIDEO); + if (! error) { + atexit(SDL_Quit); + SDL_WM_SetCaption("XYplane", NULL); + SDL_EnableUNICODE(1); + screen = SDL_SetVideoMode(640, 480, 8, SDL_HWSURFACE | SDL_DOUBLEBUF); + if (screen != NULL) { + XYplane_W_ = screen->w; + XYplane_H_ = screen->h; + } else { + fprintf(stderr, "Could not set the video mode: %s\n", SDL_GetError()); + } + } else { + fprintf(stderr, "Could not initialize the drawing plane: %s\n", SDL_GetError()); + } +} + + +void XYplane_Clear_(void) +{ + SDL_Surface *screen; + int error; + + screen = SDL_GetVideoSurface(); + if (screen != NULL) { + error = SDL_FillRect(screen, NULL, colorPerMode[XYplane_erase_]); + if (error) { + fprintf(stderr, "Could not clear the drawing plane: %s\n", SDL_GetError()); + } + } else { + fprintf(stderr, "No drawing plane to clear\n"); + } +} + + +static int WithinBounds(int x, int y, const SDL_Surface *screen) +{ + return (x >= 0) && (x < screen->w) && (y >= 0) && (y < screen->h); +} + + +static Uint8 *PixelPtr(int x, int y, const SDL_Surface *screen) +{ + return ((Uint8 *) screen->pixels) + (screen->h - 1- y) * screen->w + x; +} + + +void XYplane_Dot_(OBNC_LONGI int x, OBNC_LONGI int y, OBNC_LONGI int mode) +{ + SDL_Surface *screen; + int error; + + assert(mode >= 0); + assert(mode < LEN(colorPerMode)); + + screen = SDL_GetVideoSurface(); + if (screen != NULL) { + if (WithinBounds(x, y, screen)) { + if (SDL_MUSTLOCK(screen)) { + error = SDL_LockSurface(screen); + if (error) { + fprintf(stderr, "Could not dot on the plane: %s\n", SDL_GetError()); + } + } + *PixelPtr(x, y, screen) = colorPerMode[mode]; + if (SDL_MUSTLOCK(screen)) { + SDL_UnlockSurface(screen); + } + } + } else { + fprintf(stderr, "No drawing plane to dot on\n"); + } +} + + +int XYplane_IsDot_(OBNC_LONGI int x, OBNC_LONGI int y) +{ + SDL_Surface *screen; + int result, error; + + result = 0; + screen = SDL_GetVideoSurface(); + if (screen != NULL) { + if (WithinBounds(x, y, screen)) { + if (SDL_MUSTLOCK(screen)) { + error = SDL_LockSurface(screen); + if (error) { + fprintf(stderr, "Could not access the drawing plane: %s\n", SDL_GetError()); + } + } + result = *(PixelPtr(x, y, screen)) == colorPerMode[XYplane_draw_]; + if (SDL_MUSTLOCK(screen)) { + SDL_UnlockSurface(screen); + } + } + } else { + fprintf(stderr, "No drawing plane to inspect\n"); + } + return result; +} + + +char XYplane_Key_(void) +{ + char result; + SDL_Surface *screen; + + result = '\0'; + screen = SDL_GetVideoSurface(); + if (screen != NULL) { + SDL_Flip(screen); + if (Input_Available_() > 0) { + Input_Read_(&result); + } + SDL_Delay(10); + } else { + fprintf(stderr, "No drawing plane available\n"); + } + return result; +} + + +void XYplane_Init(void) +{ + /*do nothing*/ +} diff --git a/lib/obnc/XYplane.env b/lib/obnc/XYplane.env new file mode 100644 index 0000000..73f78ce --- /dev/null +++ b/lib/obnc/XYplane.env @@ -0,0 +1 @@ +LDLIBS=-lSDL diff --git a/lib/obnc/XYplane.obn b/lib/obnc/XYplane.obn new file mode 100644 index 0000000..6393270 --- /dev/null +++ b/lib/obnc/XYplane.obn @@ -0,0 +1,63 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE XYplane; +(**Basic facilities for graphics programming + +Implements the basic library module from "The Oakwood Guidelines for Oberon-2 Compiler Developers". + +The drawing plane is repainted when Key is invoked. A typical event loop is + + REPEAT UpdateDrawingPlane UNTIL XYplane.Key() = "q" +*) + +(*implemented in C*) + + IMPORT Input; + + CONST + (**drawing modes*) + draw* = 1; + erase* = 0; + + VAR + X*, Y*: INTEGER; (**X = 0 and Y = 0. Included for compatibility with The Oakwood Guidlines.*) + W*, H*: INTEGER; (**width and height of the drawing plane in pixels*) + + PROCEDURE Open*; +(**initializes the drawing plane*) + END Open; + + PROCEDURE Clear*; +(**erases all pixels in the drawing plane*) + END Clear; + + PROCEDURE Dot*(x, y, mode: INTEGER); +(**Dot(x, y, m) draws or erases the pixel at the coordinates (x, y) relative to the lower left corner of the plane. If m = draw the pixel is drawn, if m = erase the pixel is erased.*) + END Dot; + + PROCEDURE IsDot*(x, y: INTEGER): BOOLEAN; +(**returns TRUE if the pixel at the coordinates (x, y) relative to the lower left corner of the screen is drawn, otherwise it returns FALSE*) + RETURN FALSE (*dummy value*) + END IsDot; + + PROCEDURE Key*(): CHAR; +(**reads the keyboard. If a key was pressed prior to invocation, its character value is returned, otherwise the result is 0X.*) + RETURN CHR(0) (*dummy value*) + END Key; + +END XYplane. diff --git a/lib/obnc/XYplaneTest.obn b/lib/obnc/XYplaneTest.obn new file mode 100644 index 0000000..68e14b0 --- /dev/null +++ b/lib/obnc/XYplaneTest.obn @@ -0,0 +1,77 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE XYplaneTest; + + IMPORT XYplane; +(* + PROCEDURE TestPosition(x, y: INTEGER); + VAR withinBounds: BOOLEAN; + BEGIN + withinBounds := (x >= 0) & (x < XYplane.W) + & (y >= 0) & (y < XYplane.H); + XYplane.Clear; + XYplane.Dot(x, y, XYplane.draw); + ASSERT(withinBounds & XYplane.IsDot(x, y) + OR ~withinBounds & ~XYplane.IsDot(x, y)); + XYplane.Dot(x, y, XYplane.erase); + ASSERT(~XYplane.IsDot(x, y)) + END TestPosition; + + + PROCEDURE Run; + VAR x, y, w, h: INTEGER; + BEGIN + x := XYplane.X; + y := XYplane.Y; + w := XYplane.W; + h := XYplane.H; + + XYplane.Open; + + XYplane.Open; (*reopening test*) + + (*corners*) + + TestPosition(x, y); + TestPosition(x, y + h - 1); + TestPosition(x + w - 1, y + h - 1); + TestPosition(x, y + h - 1); + + (*just outside the corners*) + + TestPosition(x - 1, y); + TestPosition(x - 1, y - 1); + TestPosition(x, y - 1); + + TestPosition(x - 1, y + h - 1); + TestPosition(x - 1, y + h); + TestPosition(x, y + h); + + TestPosition(x + w - 1, y + h); + TestPosition(x + w, y + h); + TestPosition(x + w, y + h - 1); + + TestPosition(x + w, y); + TestPosition(x + w, y - 1); + TestPosition(x + w - 1, y - 1); + END Run; + +BEGIN + Run +*) +END XYplaneTest. diff --git a/share/doc/obnc/oberon-report.html b/share/doc/obnc/oberon-report.html new file mode 100644 index 0000000..a2743f3 --- /dev/null +++ b/share/doc/obnc/oberon-report.html @@ -0,0 +1,1672 @@ + + + + + + + The Programming Language Oberon + + + + +
+

The Programming Language Oberon

+ +

Revision 1.10.2013 / 3.5.2016

+ +

+ Niklaus Wirth
+ (HTML translation from PDF by Karl Landström) +

+ +
+

Make it as simple as possible, but not simpler. (A. Einstein)

+
+
+ +

Table of Contents

+ +
    +
  1. History and introduction
  2. +
  3. Syntax
  4. +
  5. Vocabulary
  6. +
  7. Declarations and scope rules
  8. +
  9. Constant declarations
  10. +
  11. Type declarations
  12. +
  13. Variable declarations
  14. +
  15. Expressions
  16. +
  17. Statements
  18. +
  19. Procedure declarations
  20. +
  21. Modules
  22. +
+ +

Appendix: The Syntax of Oberon

+ +

1. Introduction

+ +

Oberon is a general-purpose programming language that evolved from Modula-2. Its principal new feature is the concept of type extension. It permits the construction of new data types on the basis of existing ones and to relate them.

+ +

This report is not intended as a programmer's tutorial. It is intentionally kept concise. Its function is to serve as a reference for programmers, implementors, and manual writers. What remains unsaid is mostly left so intentionally, either because it is derivable from stated rules of the language, or because it would unnecessarily restrict the freedom of implementors.

+ +

This document describes the language defined in 1988/90 as revised in 2007 / 2016.

+ +

2. Syntax

+ +

A language is an infinite set of sentences, namely the sentences well formed according to its syntax. In Oberon, these sentences are called compilation units. Each unit is a finite sequence of symbols from a finite vocabulary. The vocabulary of Oberon consists of identifiers, numbers, strings, operators, delimiters, and comments. They are called lexical symbols and are composed of sequences of characters. (Note the distinction between symbols and characters.)

+ +

To describe the syntax, an extended Backus-Naur Formalism called EBNF is used. Brackets [ and ] denote optionality of the enclosed sentential form, and braces { and } denote its repetition (possibly 0 times). Syntactic entities (non-terminal symbols) are denoted by English words expressing their intuitive meaning. Symbols of the language vocabulary (terminal symbols) are denoted by strings enclosed in quote marks or by words in capital letters.

+ +

3. Vocabulary

+ +

The following lexical rules must be observed when composing symbols. Blanks and line breaks must not occur within symbols (except in comments, and blanks in strings). They are ignored unless they are essential to separate two consecutive symbols. Capital and lower-case letters are considered as being distinct.

+ +

Identifiers are sequences of letters and digits. The first character must be a letter.

+ +
+ident = letter {letter | digit}.
+
+ +

Examples:

+ +
+x    scan    Oberon    GetSymbol    firstLetter
+
+ +

Numbers are (unsigned) integers or real numbers. Integers are sequences of digits and may be followed by a suffix letter. If no suffix is specified, the representation is decimal. The suffix H indicates hexadecimal representation.

+ +

A real number always contains a decimal point. Optionally it may also contain a decimal scale factor. The letter E is pronounced as “times ten to the power of”.

+ +
+number = integer | real.
+integer = digit {digit} | digit {hexDigit} "H".
+real = digit {digit} "." {digit} [ScaleFactor].
+ScaleFactor = "E" ["+" | "-"] digit {digit}.
+hexDigit = digit | "A" | "B" | "C" | "D" | "E" | "F".
+digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
+
+ +

Examples:

+ +
+ + + + + + + + + + + + + + + + + +
1987
100H= 256
12.3
4.567E8= 456700000
+
+ +

Strings are sequences of characters enclosed in quote marks ("). A string cannot contain the delimiting quote mark. Alternatively, a single-character string may be specified by the ordinal number of the character in hexadecimal notation followed by an "X". The number of characters in a string is called the length of the string.

+ +
+string = """ {character} """ | digit {hexdigit} "X" .
+
+ +

Examples:

+ +
+"OBERON"    "Don't worry!"    22X
+
+ +

Operators and delimiters are the special characters, character pairs, or reserved words listed below. These reserved words consist exclusively of capital letters and cannot be used in the role of identifiers.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+:=ARRAYIMPORTTHEN
-^BEGININTO
*=BYISTRUE
/#CASEMODTYPE
~<CONSTMODULEUNTIL
&>DIVNILVAR
.<=DOOFWHILE
,>=ELSEOR
;..ELSIFPOINTER
|:ENDPROCEDURE
()FALSERECORD
[]FORREPEAT
{}IFRETURN
+
+ +

Comments may be inserted between any two symbols in a program. They are arbitrary character sequences opened by the bracket (* and closed by *). Comments do not affect the meaning of a program. They may be nested.

+ +

4. Declarations and scope rules

+ +

Every identifier occurring in a program must be introduced by a declaration, unless it is a predefined identifier. Declarations also serve to specify certain permanent properties of an object, such as whether it is a constant, a type, a variable, or a procedure.

+ +

The identifier is then used to refer to the associated object. This is possible in those parts of a program only which are within the scope of the declaration. No identifier may denote more than one object within a given scope. The scope extends textually from the point of the declaration to the end of the block (procedure or module) to which the declaration belongs and hence to which the object is local.

+ +

In its declaration, an identifier in the module's scope may be followed by an export mark (*) to indicate that it be exported from its declaring module. In this case, the identifier may be used in other modules, if they import the declaring module. The identifier is then prefixed by the identifier designating its module (see Ch. 11). The prefix and the identifier are separated by a period and together are called a qualified identifier.

+ +
+qualident = [ident "."] ident.
+identdef = ident ["*"].
+
+ +

The following identifiers are predefined; their meaning is defined in section 6.1 (types) or 10.2 (procedures):

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ABSASRASSERTBOOLEANBYTE
CHARCHRDECEXCLFLOOR
FLTINCINCLINTEGERLEN
LSLNEWODDORDPACK
REALRORSETUNPK
+
+ +

5. Constant declarations

+ +

A constant declaration associates an identifier with a constant value.

+ +
+ConstDeclaration = identdef "=" ConstExpression.
+ConstExpression = expression.
+
+ +

A constant expression can be evaluated by a mere textual scan without actually executing the program. Its operands are constants (see Ch. 8). Examples of constant declarations are:

+ +
+N = 100
+
+ +
+limit = 2*N - 1
+
+ +
+all = {0 .. WordSize - 1}
+
+ +
+name = "Oberon"
+
+ +

6. Type declarations

+ +

A data type determines the set of values which variables of that type may assume, and the operators that are applicable. A type declaration is used to associate an identifier with a type. The types define the structure of variables of this type and, by implication, the operators that are applicable to components. There are two different data structures, namely arrays and records, with different component selectors.

+ +
+TypeDeclaration = identdef "=" type.
+type = qualident | ArrayType | RecordType | PointerType | ProcedureType.
+
+ +

Examples:

+ +
+Table = ARRAY N OF REAL
+
+ +
+Tree = POINTER TO Node
+
+ +
+Node = RECORD key: INTEGER;
+	left, right: Tree
+END
+
+ +
+CenterNode = RECORD (Node)
+	name: ARRAY 32 OF CHAR;
+	subnode: Tree
+END
+
+ +
+Function = PROCEDURE (x: INTEGER): INTEGER
+
+ +

6.1. Basic types

+ +

The following basic types are denoted by predeclared identifiers. The associated operators are defined in 8.2, and the predeclared function procedures in 10.2. The values of a given basic type are the following:

+ +
+
+
BOOLEAN
+
the truth values TRUE and FALSE
+ +
CHAR
+
the characters of a standard character set
+ +
INTEGER
+
the integers
+ +
REAL
+
real numbers
+ +
BYTE
+
the integers between 0 and 255
+ +
SET
+
the sets of integers between 0 and an implementation-dependent limit
+
+
+ +

The type BYTE is compatible with the type INTEGER, and vice-versa.

+ +

6.2. Array types

+ +

An array is a structure consisting of a fixed number of elements which are all of the same type, called the element type. The number of elements of an array is called its length. The elements of the array are designated by indices, which are integers between 0 and the length minus 1.

+ +
+ArrayType = ARRAY length {"," length} OF type.
+length = ConstExpression.
+
+ +

A declaration of the form

+ +
+ARRAY N0, N1, ... , Nk OF T
+
+ +

is understood as an abbreviation of the declaration

+ +
+ARRAY N0 OF
+	ARRAY N1 OF
+		...
+			ARRAY Nk OF T
+
+ +

Examples of array types:

+ +
+ARRAY N OF INTEGER
+
+ +
+ARRAY 10, 20 OF REAL
+
+ +

6.3. Record types

+ +

A record type is a structure consisting of a fixed number of elements of possibly different types. The record type declaration specifies for each element, called field, its type and an identifier which denotes the field. The scope of these field identifiers is the record definition itself, but they are also visible within field designators (see 8.1) referring to elements of record variables.

+ +
+RecordType = RECORD ["(" BaseType ")"] [FieldListSequence] END.
+BaseType = qualident.
+FieldListSequence = FieldList {";" FieldList}.
+FieldList = IdentList ":" type.
+IdentList = identdef {"," identdef}.
+
+ +

If a record type is exported, field identifiers that are to be visible outside the declaring module must be marked. They are called public fields; unmarked fields are called private fields.

+ +

Record types are extensible, i.e. a record type can be defined as an extension of another record type. In the examples above, CenterNode (directly) extends Node, which is the (direct) base type of CenterNode. More specifically, CenterNode extends Node with the fields name and subnode.

+ +

Definition: A type T extends a type T0, if it equals T0, or if it directly extends an extension of T0. Conversely, a type T0 is a base type of T, if it equals T, or if it is the direct base type of a base type of T.

+ +

Examples of record types:

+ +
+RECORD day, month, year: INTEGER
+END
+
+ +
+RECORD
+	name, firstname: ARRAY 32 OF CHAR;
+	age: INTEGER;
+	salary: REAL
+END
+
+ +

6.4. Pointer types

+ +

Variables of a pointer type P assume as values pointers to variables of some type T. It must be a record type. The pointer type P is said to be bound to T, and T is the pointer base type of P. Pointer types inherit the extension relation of their base types, if there is any. If a type T is an extension of T0 and P is a pointer type bound to T, then P is also an extension of P0, the pointer type bound to T0.

+ +
+PointerType = POINTER TO type.
+
+ +

If a type P is defined as POINTER TO T, the identifier T can be declared textually following the declaration of P, but [if so] it must lie within the same scope.

+ +

If p is a variable of type P = POINTER TO T, then a call of the predefined procedure NEW(p) has the following effect (see 10.2): A variable of type T is allocated in free storage, and a pointer to it is assigned to p. This pointer p is of type P and the referenced variable p^ is of type T. Failure of allocation results in p obtaining the value NIL. Any pointer variable may be assigned the value NIL, which points to no variable at all.

+ +

6.5. Procedure types

+ +

Variables of a procedure type T have a procedure (or NIL) as value. If a procedure P is assigned to a procedure variable of type T, the (types of the) formal parameters of P must be the same as those indicated in the formal parameters of T. The same holds for the result type in the case of a function procedure (see 10.1). P must not be declared local to another procedure, and neither can it be a standard procedure.

+ +
+ProcedureType = PROCEDURE [FormalParameters].
+
+ +

7. Variable declarations

+ +

Variable declarations serve to introduce variables and associate them with identifiers that must be unique within the given scope. They also serve to associate fixed data types with the variables.

+ +
+VariableDeclaration = IdentList ":" type.
+
+ +

Variables whose identifiers appear in the same list are all of the same type. Examples of variable declarations (refer to examples in Ch. 6):

+ +
+i, j, k: INTEGER
+
+ +
+x, y: REAL
+
+ +
+p, q: BOOLEAN
+
+
+s: SET
+
+ +
+f: Function
+
+ +
+a: ARRAY 100 OF REAL
+
+ +
+w: ARRAY 16 OF
+	RECORD ch: CHAR;
+		count: INTEGER
+	END
+
+ +
+t: Tree
+
+ +

8. Expressions

+ +

Expressions are constructs denoting rules of computation whereby constants and current values of variables are combined to derive other values by the application of operators and function procedures. Expressions consist of operands and operators. Parentheses may be used to express specific associations of operators and operands.

+ +

8.1. Operands

+ +

With the exception of sets and literal constants, i.e. numbers and strings, operands are denoted by designators. A designator consists of an identifier referring to the constant, variable, or procedure to be designated. This identifier may possibly be qualified by module identifiers (see Ch. 4 and 11), and it may be followed by selectors, if the designated object is an element of a structure.

+ +

If A designates an array, then A[E] denotes that element of A whose index is the current value of the expression E. The type of E must be of type INTEGER. A designator of the form A[E1, E2, ... , En] stands for A[E1][E2] ... [En]. If p designates a pointer variable, p^ denotes the variable which is referenced by p. If r designates a record, then r.f denotes the field f of r. If p designates a pointer, p.f denotes the field f of the record p^, i.e. the dot implies dereferencing and p.f stands for p^.f.

+ +

The typeguard v(T0) asserts that v is of type T0 , i.e. it aborts program execution, if it is not of type T0 . The guard is applicable, if

+ +
    +
  1. T0 is an extension of the declared type T of v, and if
  2. +
  3. v is a variable parameter of record type, or v is a pointer.
  4. +
+ +
+designator = qualident {selector}.
+selector = "." ident | "[" ExpList "]" | "^" | "(" qualident ")".
+ExpList = expression {"," expression}.
+
+ +

If the designated object is a variable, then the designator refers to the variable's current value. If the object is a procedure, a designator without parameter list refers to that procedure. If it is followed by a (possibly empty) parameter list, the designator implies an activation of the procedure and stands for the value resulting from its execution. The (types of the) actual parameters must correspond to the formal parameters as specified in the procedure's declaration (see Ch. 10).

+ +

Examples of designators (see examples in Ch. 7):

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
i(INTEGER)
a[i](REAL)
w[3].ch(CHAR)
t.key(INTEGER)
t.left.right(Tree)
t(CenterNode).subnode(Tree)
+
+ +

8.2. Operators

+ +

The syntax of expressions distinguishes between four classes of operators with different precedences (binding strengths). The operator ~ has the highest precedence, followed by multiplication operators, addition operators, and relations. Operators of the same precedence associate from left to right. For example, xyz stands for (xy) − z.

+
+expression = SimpleExpression [relation SimpleExpression].
+relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
+SimpleExpression = ["+"|"-"] term {AddOperator term}.
+AddOperator = "+" | "-" | OR.
+term = factor {MulOperator factor}.
+MulOperator = "*" | "/" | DIV | MOD | "&" .
+factor = number | string | NIL | TRUE | FALSE |
+	set | designator [ActualParameters] | "(" expression ")" | "~" factor.
+set = "{" [element {"," element}] "}".
+element = expression [".." expression].
+ActualParameters = "(" [ExpList] ")" .
+
+ +

The set {m .. n} denotes {m, m+1, … , n-1, n}, and if m > n, the empty set. The available operators are listed in the following tables. In some instances, several different operations are designated by the same operator symbol. In these cases, the actual operation is identified by the type of the operands.

+ +

8.2.1. Logical operators

+ +
+ + + + + + + + + + + + + + + + + +
symbolresult
ORlogical disjunction
&logical conjunction
~negation
+
+ +

These operators apply to BOOLEAN operands and yield a BOOLEAN result.

+ +
+ + + + + + + + + + + + + + + + +
p OR qstands for“if p then TRUE, else q
p & qstands for“if p then q, else FALSE”
~ pstands for“not p
+
+ +

8.2.2. Arithmetic operators

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
symbolresult
+sum
difference
*product
/quotient
DIVinteger quotient
MODmodulus
+
+ +

The operators +, −, *, and / apply to operands of numeric types. Both operands must be of the same type, which is also the type of the result. When used as unary operators, − denotes sign inversion and + denotes the identity operation.

+ +

The operators DIV and MOD apply to integer operands only. Let q = x DIV y, and r = x MOD y. Then quotient q and remainder r are defined by the equation

+ +
+x = q*y + r       0 <= r < y
+
+ +

8.2.3. Set operators

+ +
+ + + + + + + + + + + + + + + + + + + + + +
symbolresult
+union
−difference
*intersection
/ symmetric set difference
+
+ +

When used with a single operand of type SET, the minus sign denotes the set complement.

+ +

8.2.4. Relations

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
symbolrelation
=equal
#unequal
<less
<=less or equal
>greater
>=greater or equal
INset membership
IStype test
+
+ +

Relations are Boolean. The ordering relations <, <=, >, >= apply to the numeric types, CHAR, and character arrays. The relations = and # also apply to the types BOOLEAN, SET, and to pointer and procedure types.

+ +

x IN s stands for “x is an element of s”. x must be of type INTEGER, and s of type SET.

+ +

v IS T stands for “v is of type T” and is called a type test. It is applicable, if

+ +
    +
  1. T is an extension of the declared type T0 of v, and if
  2. +
  3. v is a variable parameter of record type or v is a pointer.
  4. +
+ +

Assuming, for instance, that T is an extension of T0 and that v is a designator declared of type T0, then the test v IS T determines whether the actually designated variable is (not only a T0, but also) a T. The value of NIL IS T is undefined.

+ +

Examples of expressions (refer to examples in Ch. 7):

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1987(INTEGER)
i DIV 3(INTEGER)
~p OR q(BOOLEAN)
(i+j) * (i-j)(INTEGER)
s - {8, 9, 13}(SET)
a[i+j] * a[i-j](REAL)
(0<=i) & (i<100)(BOOLEAN)
t.key = 0(BOOLEAN)
k IN {i .. j-1}(BOOLEAN)
t IS CenterNode(BOOLEAN)
+
+ +

9. Statements

+ +

Statements denote actions. There are elementary and structured statements. Elementary statements are not composed of any parts that are themselves statements. They are the assignment and the procedure call. Structured statements are composed of parts that are themselves statements. They are used to express sequencing and conditional, selective, and repetitive execution. A statement may also be empty, in which case it denotes no action. The empty statement is included in order to relax punctuation rules in statement sequences.

+ +
+statement = [assignment | ProcedureCall | IfStatement | CaseStatement |
+	WhileStatement | RepeatStatement | ForStatement].
+
+ +

9.1. Assignments

+ +

The assignment serves to replace the current value of a variable by a new value specified by an expression. The assignment operator is written as “:=” and pronounced as becomes.

+ +
+assignment = designator ":=" expression.
+
+ +

If a value parameter is structured (of array or record type), no assignment to it or to its elements are permitted. Neither may assignments be made to imported variables.

+ +

The type of the expression must be the same as that of the designator. The following exceptions hold:

+ +
    +
  1. The constant NIL can be assigned to variables of any pointer or procedure type.
  2. +
  3. Strings can be assigned to any array of characters, provided the number of characters in the string is less than that of the array. (A null character is appended). Single-character strings can also be assigned to variables of type CHAR.
  4. +
  5. In the case of records, the type of the source must be an extension of the type of the destination.
  6. +
  7. An open array may be assigned to an array of equal base type.
  8. +
+ +

Examples of assignments (see examples in Ch. 7):

+ +
+i := 0
+
+ +
+p := i = j
+
+ +
+x := FLT(i + 1)
+
+ +
+k := (i + j) DIV 2
+
+ +
+f := log2
+
+ +
+s := {2, 3, 5, 7, 11, 13}
+
+ +
+a[i] := (x+y) * (x-y)
+
+ +
+t.key := i
+
+ +
+w[i+1].ch := "A"
+
+ +

9.2. Procedure calls

+ +

A procedure call serves to activate a procedure. The procedure call may contain a list of actual parameters which are substituted in place of their corresponding formal parameters defined in the procedure declaration (see Ch. 10). The correspondence is established by the positions of the parameters in the lists of actual and formal parameters respectively. There exist two kinds of parameters: variable and value parameters.

+ +

In the case of variable parameters, the actual parameter must be a designator denoting a variable. If it designates an element of a structured variable, the selector is evaluated when the formal/actual parameter substitution takes place, i.e. before the execution of the procedure. If the parameter is a value parameter, the corresponding actual parameter must be an expression. This expression is evaluated prior to the procedure activation, and the resulting value is assigned to the formal parameter which now constitutes a local variable (see also 10.1.).

+ +
+ProcedureCall = designator [ActualParameters].
+
+ +

Examples of procedure calls:

+ +
+ + + + + + + + + + + + + +
ReadInt(i)(see Ch. 10)
WriteInt(2*j + 1, 6)
INC(w[k].count)
+
+ +

9.3. Statement sequences

+ +

Statement sequences denote the sequence of actions specified by the component statements which are separated by semicolons.

+ +
+StatementSequence = statement {";" statement}.
+
+ +

9.4. If statements

+ +
+IfStatement = IF expression THEN StatementSequence
+	{ELSIF expression THEN StatementSequence}
+	[ELSE StatementSequence]
+	END.
+
+ +

If statements specify the conditional execution of guarded statements. The Boolean expression preceding a statement is called its guard. The guards are evaluated in sequence of occurrence, until one evaluates to TRUE, whereafter its associated statement sequence is executed. If no guard is satisfied, the statement sequence following the symbol ELSE is executed, if there is one.

+ +

Example:

+ +
+IF (ch >= "A") & (ch <= "Z") THEN ReadIdentifier
+ELSIF (ch >= "0") & (ch <= "9") THEN ReadNumber
+ELSIF ch = 22X THEN ReadString
+END
+
+ +

9.5. Case statements

+ +

Case statements specify the selection and execution of a statement sequence according to the value of an expression. First the case expression is evaluated, then the statement sequence is executed whose case label list contains the obtained value. If the case expression is of type INTEGER or CHAR, all labels must be integers or single-character strings, respectively.

+ +
+CaseStatement = CASE expression OF case {"|" case} END.
+case = [CaseLabelList ":" StatementSequence].
+CaseLabelList = LabelRange {"," LabelRange}.
+LabelRange = label [".." label].
+label = integer | string | qualident.
+
+ +

Example:

+ +
+CASE k OF
+	  0: x := x + y
+	| 1: x := x − y
+	| 2: x := x * y
+	| 3: x := x / y
+END
+
+ +

The type T of the case expression (case variable) may also be a record or pointer type. Then the +case labels must be extensions of T, and in the statements Si labelled by Ti, the case variable is considered as of type Ti.

+ +

Example:

+ +
+TYPE R = RECORD a: INTEGER END;
+	R0 = RECORD (R) b: INTEGER END;
+	R1 = RECORD (R) b: REAL END;
+	R2 = RECORD (R) b: SET END;
+	P = POINTER TO R;
+	P0 = POINTER TO R0;
+	P1 = POINTER TO R1;
+	P2 = POINTER TO R2;
+VAR p: P;
+
+ +
+CASE p OF
+	P0: p.b := 10 |
+	P1: p.b := 2.5 |
+	P2: p.b := {0, 2}
+END
+
+ +

9.6. While statements

+ +

While statements specify repetition. If any of the Boolean expressions (guards) yields TRUE, the corresponding statement sequence is executed. The expression evaluation and the statement execution are repeated until none of the Boolean expressions yields TRUE.

+ +
+WhileStatement = WHILE expression DO StatementSequence
+	{ELSIF expression DO StatementSequence} END.
+
+ +

Examples:

+ +
+WHILE j > 0 DO
+	j := j DIV 2; i := i+1
+END
+
+ +
+WHILE (t # NIL) & (t.key # i) DO
+	t := t.left
+END
+
+ +
+WHILE m > n DO m := m - n
+ELSIF n > m DO n := n - m
+END
+
+ +

9.7. Repeat Statements

+ +

A repeat statement specifies the repeated execution of a statement sequence until a condition is satisfied. The statement sequence is executed at least once.

+ +
+RepeatStatement = REPEAT StatementSequence UNTIL expression.
+
+ +

9.8. For statements

+ +

A for statement specifies the repeated execution of a statement sequence for a given number of times, while a progression of values is assigned to an integer variable called the control variable of the for statement.

+ +
+ForStatement =
+	FOR ident ":=" expression TO expression [BY ConstExpression] DO
+	StatementSequence END.
+
+ +

The for statement

+ +
+FOR v := beg TO end BY inc DO S END
+
+ +

is, if inc > 0, equivalent to

+ +
+v := beg;
+WHILE v <= end DO S; v := v + inc END
+
+ +

and if inc < 0 it is equivalent to

+ +
+v := beg;
+WHILE v >= end DO S; v := v + inc END
+
+ +

The types of v, beg and end must be INTEGER, and inc must be an integer (constant expression). If the step is not specified, it is assumed to be 1.

+ +

10. Procedure declarations

+ +

Procedure declarations consist of a procedure heading and a procedure body. The heading specifies the procedure identifier, the formal parameters, and the result type (if any). The body contains declarations and statements. The procedure identifier is repeated at the end of the procedure declaration.

+ +

There are two kinds of procedures, namely proper procedures and function procedures. The latter are activated by a function designator as a constituent of an expression, and yield a result that is an operand in the expression. Proper procedures +are activated by a procedure call. A function procedure is distinguished in the declaration by indication of the type of its result following the parameter list. Its body must end with a RETURN clause which defines the result of the function procedure.

+ +

All constants, variables, types, and procedures declared within a procedure body are local to the procedure. The values of local variables are undefined upon entry to the procedure. Since procedures may be declared as local objects too, procedure declarations may be nested.

+ +

In addition to its formal parameters and locally declared objects, the objects declared globally are also visible in the procedure.

+ +

The use of the procedure identifier in a call within its declaration implies recursive activation of the procedure.

+ +
+ProcedureDeclaration = ProcedureHeading ";" ProcedureBody ident.
+ProcedureHeading = PROCEDURE identdef [FormalParameters].
+ProcedureBody = DeclarationSequence [BEGIN StatementSequence]
+	[RETURN expression] END.
+DeclarationSequence = [CONST {ConstDeclaration ";"}]
+	 [TYPE {TypeDeclaration ";"}] [VAR {VariableDeclaration ";"}]
+	{ProcedureDeclaration ";"}.
+
+ +

10.1. Formal parameters

+ +

Formal parameters are identifiers which denote actual parameters specified in the procedure call. The correspondence between formal and actual parameters is established when the procedure is called. There are two kinds of parameters, namely +value and variable parameters. A variable parameter corresponds to an actual parameter that is a variable, and it stands for that variable. A value parameter corresponds to an actual parameter that is an expression, and it stands for its value, which cannot be changed by assignment. However, if a value parameter is of a basic type, it represents a local variable to which the value of the actual expression is initially assigned.

+ +

The kind of a parameter is indicated in the formal parameter list: Variable parameters are denoted by the symbol VAR and value parameters by the absence of a prefix.

+ +

A function procedure without parameters must have an empty parameter list. It must be called by a function designator whose actual parameter list is empty too.

+ +

Formal parameters are local to the procedure, i.e. their scope is the program text which constitutes the procedure declaration.

+ +
+FormalParameters = "(" [FPSection {";" FPSection}] ")" [":" qualident].
+FPSection = [VAR] ident {"," ident} ":" FormalType.
+FormalType = {ARRAY OF} qualident.
+
+ +

The type of each formal parameter is specified in the parameter list. For variable parameters, it must be identical to the corresponding actual parameter's type, except in the case of a record, where it must be a base type of the corresponding actual parameter's type.

+ +

If the formal parameter's type is specified as

+ +
+ARRAY OF T
+
+ +

the parameter is said to be an open array, and the corresponding actual parameter may be of arbitrary length.

+ +

If a formal parameter specifies a procedure type, then the corresponding actual parameter must be either a procedure declared globally, or a variable (or parameter) of that procedure type. It cannot be a predefined procedure. The result type of a procedure can be neither a record nor an array.

+ +

Examples of procedure declarations:

+ +
+PROCEDURE ReadInt(VAR x: INTEGER);
+	VAR i: INTEGER; ch: CHAR;
+BEGIN i := 0; Read(ch);
+	WHILE ("0" <= ch) & (ch <= "9") DO
+		i := 10*i + (ORD(ch) - ORD("0")); Read(ch)
+	END;
+	x := i
+END ReadInt
+
+ +
+PROCEDURE WriteInt(x: INTEGER); (* 0 <= x < 10^5 *)
+	VAR i: INTEGER;
+	buf: ARRAY 5 OF INTEGER;
+BEGIN i := 0;
+	REPEAT buf[i] := x MOD 10; x := x DIV 10; INC(i) UNTIL x = 0;
+	REPEAT DEC(i); Write(CHR(buf[i] + ORD("0"))) UNTIL i = 0
+END WriteInt
+
+ +
+PROCEDURE log2(x: INTEGER): INTEGER;
+	VAR y: INTEGER; (*assume x>0*)
+BEGIN y := 0;
+	WHILE x > 1 DO x := x DIV 2; INC(y) END;
+	RETURN y
+END log2
+
+ +

10.2. Predefined procedures

+ +

The following table lists the predefined procedures. Some are generic procedures, i.e. they apply to several types of operands. v stands for a variable, x and n for expressions, and T for a type.

+ +

Function procedures:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameArgument typeResult typeFunction
ABS(x)x: numeric typetype of xabsolute value
ODD(x)x: INTEGERBOOLEANx MOD 2 = 1
LEN(v) v: arrayINTEGERthe length of v
LSL(x, n)x, n: INTEGERINTEGERlogical shift left, x * 2n
ASR(x, n)x, n: INTEGERINTEGERsigned shift right, x DIV 2n
ROR(x, n)x, n: INTEGERINTEGERx rotated right by n bits
+
+ +

Type conversion functions:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameArgument typeResult typeFunction
FLOOR(x)REALINTEGERtruncation
FLT(x)INTEGERREALidentity
ORD(x)CHAR, BOOLEAN, SETINTEGERordinal number of x
CHR(x)INTEGERCHARcharacter with ordinal number x
+
+ +

Proper procedures:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameArgument typesFunction
INC(v)INTEGERv := v + 1
INC(v, n)INTEGERv := v + n
DEC(v)INTEGERv := v - 1
DEC(v, n)INTEGERv := v - n
INCL(v, x)v: SET; x: INTEGERv := v + {x}
EXCL(v, x)v: SET; x: INTEGERv := v - {x}
NEW(v)pointer typeallocate v^
ASSERT(b)BOOLEANabort, if ~b
PACK(x, n)REAL; INTEGERpack x and n into x
UNPK(x, n)REAL; INTEGERunpack x into x and n
+
+ +

The function FLOOR(x) yields the largest integer not greater than x.

+ +
+FLOOR(1.5) = 1    FLOOR(-1.5) = -2
+
+ +

The parameter n of PACK represents the exponent of x. PACK(x, y) is equivalent to x := x * 2y. UNPK is the reverse operation. The resulting x is normalized, such that 1.0 <= x < 2.0.

+ +

11. Modules

+ +

A module is a collection of declarations of constants, types, variables, and procedures, and a sequence of statements for the purpose of assigning initial values to the variables. A module typically constitutes a text that is compilable as a unit.

+ +
+module = MODULE ident ";" [ImportList] DeclarationSequence
+	[BEGIN StatementSequence] END ident "." .
+ImportList = IMPORT import {"," import} ";" .
+Import = ident [":=" ident].
+
+ +

The import list specifies the modules of which the module is a client. If an identifier x is exported from a module M, and if M is listed in a module's import list, then x is referred to as M.x. If the form “M := M1” is used in the import list, an exported object x declared within M1 is referenced in the importing module as M.x .

+ +

Identifiers that are to be visible in client modules, i.e. which are to be exported, must be marked by an asterisk (export mark) in their declaration. Variables are always exported in read-only mode.

+ +

The statement sequence following the symbol BEGIN is executed when the module is added to a system (loaded). Individual (parameterless) procedures can thereafter be activated from the system, and these procedures serve as commands.

+ +

Example:

+ +
+MODULE Out; (*exported procedures: Write, WriteInt, WriteLn*)
+	IMPORT Texts, Oberon;
+	VAR W: Texts.Writer;
+
+	PROCEDURE Write*(ch: CHAR);
+	BEGIN Texts.Write(W, ch)
+	END;
+
+	PROCEDURE WriteInt*(x, n: INTEGER);
+		VAR i: INTEGER; a: ARRAY 16 OF CHAR;
+	BEGIN i := 0;
+		IF x < 0 THEN Texts.Write(W, "-"); x := -x END ;
+		REPEAT a[i] := CHR(x MOD 10 + ORD("0")); x := x DIV 10; INC(i) UNTIL x = 0;
+		REPEAT Texts.Write(W, " "); DEC(n) UNTIL n <= i;
+		REPEAT DEC(i); Texts.Write(W, a[i]) UNTIL i = 0
+	END WriteInt;
+
+	PROCEDURE WriteLn*;
+	BEGIN Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
+	END WriteLn;
+
+BEGIN Texts.OpenWriter(W)
+END Out.
+
+ +

11.1 The Module SYSTEM

+ +

The optional module SYSTEM contains definitions that are necessary to program low-level operations referring directly to resources particular to a given computer and/or implementation. These include for example facilities for accessing devices that are controlled by the computer, and perhaps facilities to break the data type compatibility rules otherwise imposed by the language definition.

+ +

There are two reasons for providing facilities in Module SYSTEM; (1) Their value is implementation-dependent, that is, it is not derivable from the language's definition, and (2) they may corrupt a system (e.g. PUT). It is strongly recommended to restrict their use to specific low-level modules, as such modules are inherently non-portable and not “type-safe”. However, they are easily recognized due to the identifier SYSTEM appearing in the module's import lists. The subsequent definitions are generally applicable. However, individual implementations may include in their module SYSTEM additional definitions that are particular to the specific, underlying computer. In the following, v stands for a variable, x, a, and n for expressions.

+ +

Function procedures:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameArgument typesResult typeFunction
ADR(v)anyINTEGERaddress of variable v
SIZE(T)any typeINTEGERsize in bytes
BIT(a, n)a, n: INTEGERBOOLEANbit n of mem[a]
+
+ +

Proper procedures:

+ +
+ + + + + + + + + + + + + + + + + + + + + +
NameArgument typesFunction
GET(a, v)a: INTEGER; v: any basic typev := mem[a]
PUT(a, x)a: INTEGER; x: any basic typemem[a] := x
COPY(src, dst, n)all INTEGERcopy n consecutive words from src to dst
+
+ +

The following are additional procedures accepted by the compiler for the RISC processor:

+ +

Function procedures:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameArgument typesResult typeFunction
VAL(T, n)scalarTidentity
ADC(m, n)INTEGERINTEGERadd with carry C
SBC(m, n)INTEGERINTEGERsubtract with carry C
UML(m, n)INTEGERINTEGERunsigned multiplication
COND(n)INTEGERBOOLEANIF Cond(n) THEN ...
+
+ +

Proper procedures:

+ +
+ + + + + + + + + + + +
NameArgument typesFunction
LED(n)INTEGERdisplay n on LEDs
+
+ +

Appendix

+ +

The Syntax of Oberon

+ +
+letter = "A" | "B" | ... | "Z" | "a" | "b" | ... | "z".
+digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
+hexDigit = digit | "A" | "B" | "C" | "D" | "E" | "F".
+
+ +
+ident = letter {letter | digit}.
+qualident = [ident "."] ident.
+identdef = ident ["*"].
+
+ +
+integer = digit {digit} | digit {hexDigit} "H".
+real = digit {digit} "." {digit} [ScaleFactor].
+ScaleFactor = "E" ["+" | "-"] digit {digit}.
+number = integer | real.
+string = """ {character} """ | digit {hexDigit} "X".
+
+ +
+ConstDeclaration = identdef "=" ConstExpression.
+ConstExpression = expression.
+
+ +
+TypeDeclaration = identdef "=" type.
+type = qualident | ArrayType | RecordType | PointerType | ProcedureType.
+ArrayType = ARRAY length {"," length} OF type.
+length = ConstExpression.
+RecordType = RECORD ["(" BaseType ")"] [FieldListSequence] END.
+BaseType = qualident.
+FieldListSequence = FieldList {";" FieldList}.
+FieldList = IdentList ":" type.
+IdentList = identdef {"," identdef}.
+PointerType = POINTER TO type.
+ProcedureType = PROCEDURE [FormalParameters].
+
+ +
+VariableDeclaration = IdentList ":" type.
+
+ +
+expression = SimpleExpression [relation SimpleExpression].
+relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS.
+SimpleExpression = ["+" | "-"] term {AddOperator term}.
+AddOperator = "+" | "-" | OR.
+term = factor {MulOperator factor}.
+MulOperator = "*" | "/" | DIV | MOD | "&".
+factor = number | string | NIL | TRUE | FALSE |
+	set | designator [ActualParameters] | "(" expression ")" | "~" factor.
+designator = qualident {selector}.
+selector = "." ident | "[" ExpList "]" | "^" | "(" qualident ")".
+set = "{" [element {"," element}] "}".
+element = expression [".." expression].
+ExpList = expression {"," expression}.
+ActualParameters = "(" [ExpList] ")" .
+
+ +
+statement = [assignment | ProcedureCall | IfStatement | CaseStatement |
+	WhileStatement | RepeatStatement | ForStatement].
+assignment = designator ":=" expression.
+ProcedureCall = designator [ActualParameters].
+StatementSequence = statement {";" statement}.
+IfStatement = IF expression THEN StatementSequence
+	{ELSIF expression THEN StatementSequence}
+	[ELSE StatementSequence] END.
+CaseStatement = CASE expression OF case {"|" case} END.
+case = [CaseLabelList ":" StatementSequence].
+CaseLabelList = LabelRange {"," LabelRange}.
+LabelRange = label [".." label].
+label = integer | string | qualident.
+WhileStatement = WHILE expression DO StatementSequence
+	{ELSIF expression DO StatementSequence} END.
+RepeatStatement = REPEAT StatementSequence UNTIL expression.
+ForStatement = FOR ident ":=" expression TO expression [BY ConstExpression]
+	DO StatementSequence END.
+
+ +
+ProcedureDeclaration = ProcedureHeading ";" ProcedureBody ident.
+ProcedureHeading = PROCEDURE identdef [FormalParameters].
+ProcedureBody = DeclarationSequence [BEGIN StatementSequence]
+	[RETURN expression] END.
+DeclarationSequence = [CONST {ConstDeclaration ";"}]
+	[TYPE {TypeDeclaration ";"}]
+	[VAR {VariableDeclaration ";"}]
+	{ProcedureDeclaration ";"}.
+FormalParameters = "(" [FPSection {";" FPSection}] ")" [":" qualident].
+FPSection = [VAR] ident {"," ident} ":" FormalType.
+FormalType = {ARRAY OF} qualident.
+
+ +
+module = MODULE ident ";" [ImportList] DeclarationSequence
+	[BEGIN StatementSequence] END ident "." .
+ImportList = IMPORT import {"," import} ";".
+import = ident [":=" ident].
+
+ + diff --git a/share/man/man1/obnc-compile.1 b/share/man/man1/obnc-compile.1 new file mode 100644 index 0000000..91e1fe1 --- /dev/null +++ b/share/man/man1/obnc-compile.1 @@ -0,0 +1,37 @@ +.TH OBNC-COMPILE 1 +.SH NAME +obnc-compile \- compile an Oberon module to C +.SH SYNOPSIS +.B obnc-compile +[\fB\-e\fR | \fB\-l\fR] +.IR MODULE.obn +.br +.B obnc-compile +(\fB\-h\fR | \fB\-v\fR) +.SH DESCRIPTION +.B obnc-compile +compiles an Oberon module to C. All output files (C implementation file, C header file, symbol file and import list file) are stored in the subdirectory +.IR .obnc . +.P +The compiler accepts the Oberon language as defined in "The Programming Language Oberon", revision 2013-10-01 / 2016-05-03 (Oberon-07). The target language is ANSI C (C89). +.SH OPTIONS +.TP +.BR \-e +Create an entry point function (main). +.TP +.BR \-h +Display help and exit. +.TP +.BR \-l +Print names of imported modules to standard output and exit. +.TP +.BR \-v +Display version and exit. +.SH ENVIRONMENT +.IP OBNC_IMPORT_PATH +See +.BR obnc-path (1) +.SH AUTHOR +Written by Karl Landstr\[:o]m +.SH "SEE ALSO" +.BR obnc (1), obnc-path (1) diff --git a/share/man/man1/obnc-path.1 b/share/man/man1/obnc-path.1 new file mode 100644 index 0000000..d006ddb --- /dev/null +++ b/share/man/man1/obnc-path.1 @@ -0,0 +1,35 @@ +.TH OBNC-PATH 1 +.SH NAME +obnc-path \- print directory path for Oberon module +.SH SYNOPSIS +.B obnc-path +MODULE +.br +.B obnc-path +(\fB\-h\fR | \fB\-v\fR) +.SH DESCRIPTION +.B obnc-path +prints the directory path for an Oberon module. For a module M, the printed path is the first directory found which contains either +.I M.obn +, +.IR .obnc/M.sym +or +.IR M.sym . +.P +First the current directory is searched. Then paths in OBNC_IMPORT_PATH are searched. Finally the default library directory in the OBNC installation path is searched. +.P +For each path P, modules are searched both in P and in first-level subdirectories of P. Subdirectories represent individual libraries and are expected to be in lowercase. For the modules in a subdirectory L, only modules prefixed with L followed by an uppercase letter are searched. The other modules in L are considered local to the library. +.SH OPTIONS +.TP +.BR \-h +Display help and exit. +.TP +.BR \-v +Display version and exit. +.SH ENVIRONMENT +.IP OBNC_IMPORT_PATH +Colon-separated list of paths to search for Oberon modules. +.SH AUTHOR +Written by Karl Landstr\[:o]m +.SH "SEE ALSO" +.BR obnc (1), obnc-compile (1) diff --git a/share/man/man1/obnc.1 b/share/man/man1/obnc.1 new file mode 100644 index 0000000..be7398b --- /dev/null +++ b/share/man/man1/obnc.1 @@ -0,0 +1,127 @@ +.TH OBNC 1 +.SH NAME +obnc \- build an executable for an Oberon module +.SH SYNOPSIS +.B obnc +[\fB\-v\fR | \fB\-V\fR] +.IR MODULE.obn +.br +.B obnc +(\fB\-h\fR | \fB\-v\fR) +.SH DESCRIPTION +.B obnc +builds an executable file for an Oberon module. Before the module is compiled, object files for imported modules are recursively created or updated as needed. Oberon modules are first compiled to C with +.BR obnc-compile . +Each C file is then compiled to object code with an external C compiler. Finally, the object files are linked into an executable program. All output files except the final executable are stored in the subdirectory +.IR .obnc . +.P +If for any module M there exists a file named +.I M.c +in the same directory as +.I M.obn +then +.I M.c +will be used as the input to the C compiler instead of the generated C file. This provides a mechanism to implement a module in C. +.P +For any module M, environment variables for the C compiler specific to M and environment variables for the linker can be defined in a file named +.IR M.env , +located in the same directory as +.IR M.obn . +.SH OPTIONS +.TP +.BR \-h +Display help and exit. +.TP +.BR \-v +Without argument, display version and exit. Otherwise, output progress of compiled modules. +.TP +.BR \-V +Output progress of compiled modules with compiler and linker subcommands. +.SH ENVIRONMENT +.IP CC +Specifies the C compiler to use (default is cc). +.IP CFLAGS +Options for the C compiler. +.IP LDFLAGS +Additional options for the linker. +.IP LDLIBS +Additional libraries to link with. +.IP OBNC_IMPORT_PATH +See +.BR obnc-path (1) +.SH EXAMPLES +.SS Getting Started +In Oberon, the program to print "hello, world" is +.P +.RS +MODULE hello; +.P +.RS +IMPORT Out; +.P +.RE +BEGIN +.RS +Out.String("hello, world"); +.br +Out.Ln +.RE +END hello. +.RE +.P +Save the above module to a file named +.IR hello.obn +and compile it with the command +.P +.RS +obnc hello.obn +.RE +.P +This will create an executable file named +.IR hello . +When you run +.IR hello +with the command +.P +.RS +\[char46]/hello +.RE +.P +it should print +.P +.RS +hello, world +.RE +.SS Interfacing to C +To implement a module M in C: +.IP 1. 3 +Create a file named +.I M.obn +with the the exported declarations +.IP 2. 3 +Create a file named +.I MTest.obn +which imports M (and preferably write unit tests for M) +.IP 3. 3 +Build MTest with the command +.P +.RS +obnc MTest.obn +.RE +.P +.IP 4. 3 +Copy the generated file +.IR .obnc/M.c +to the current directory. Delete the generator comment on the first line and change the path in the include directive from +.IR M.h +to +.IR .obnc/M.h . +.IP 5. 3 +Implement +.IR M.c . +.P +Note: The initialization function M_Init is called each time a client module imports M. Its statements should therefore be guarded with an initialization flag to make sure they are executed only once. +.SH AUTHOR +Written by Karl Landstr\[:o]m +.SH "SEE ALSO" +.BR obnc-compile (1), obnc-path (1) diff --git a/share/man/man1/obncdoc.1 b/share/man/man1/obncdoc.1 new file mode 100644 index 0000000..5faf219 --- /dev/null +++ b/share/man/man1/obncdoc.1 @@ -0,0 +1,26 @@ +.TH OBNCDOC 1 +.SH NAME +obncdoc \- extract exported features from Oberon modules +.SH SYNOPSIS +.B obncdoc +[\fB\-h\fR | \fB\-v\fR] +.SH DESCRIPTION +.B obncdoc +creates HTML definitions and index from Oberon source files in the current directory. Each definition contains the exported declarations and the exported comments (start with an asterisk) for the corresponding module. The definition files are created, updated or deleted only as needed. A default style file, +.IR style.css , +is created only if not present. This provides for custom style sheets. All output is written to the directory +.IR obncdoc . +Oberon source files are expected to have the filename extension +.IR .obn . +.P +.B obncdoc +is not a complete parser so no syntax (or semantics) checks are performed. Also, for exported identifiers, a source file is expected to have at most one declaration per line. +.SH OPTIONS +.TP +.BR \-h +Display help and exit. +.TP +.BR \-v +Display version and exit. +.SH AUTHOR +Written by Karl Landstr\[:o]m diff --git a/src/Config.c b/src/Config.c new file mode 100644 index 0000000..d328424 --- /dev/null +++ b/src/Config.c @@ -0,0 +1,42 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Config.h" +#include + +const char *Config_Prefix(void) +{ + const char *prefix; + + prefix = getenv("OBNC_PREFIX"); + if (prefix == NULL) { + prefix = CONFIG_DEFAULT_PREFIX; + } + return prefix; +} + + +const char *Config_LibDir(void) +{ + const char *libdir; + + libdir = getenv("OBNC_LIBDIR"); + if (libdir == NULL) { + libdir = CONFIG_DEFAULT_LIBDIR; + } + return libdir; +} diff --git a/src/Files.c b/src/Files.c new file mode 100644 index 0000000..eea8827 --- /dev/null +++ b/src/Files.c @@ -0,0 +1,137 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Files.h" +#include /*POSIX*/ +#include /*POSIX*/ +#include /*POSIX*/ +#include +#include +#include +#include +#include + +int Files_Exists(const char filename[]) +{ + int error; + + assert(filename != NULL); + + error = access(filename, F_OK); + return ! error; +} + + +FILE *Files_New(const char filename[]) +{ + FILE *newFile; + + assert(filename != NULL); + + newFile = fopen(filename, "w+"); + if (newFile == NULL) { + fprintf(stderr, "obnc-compile: error: Cannot open new file: %s: %s\n", filename, strerror(errno)); + exit(EXIT_FAILURE); + } + + assert(newFile != NULL); + + return newFile; +} + + +FILE *Files_Old(const char filename[], int mode) +{ + const char *fopenMode; + FILE *oldFile; + + assert(filename != NULL); + assert((mode == FILES_READ) || (mode == FILES_WRITE)); + assert(Files_Exists(filename)); + + if (mode == FILES_READ) { + fopenMode = "r"; + } else { + fopenMode = "w"; + } + oldFile = fopen(filename, fopenMode); + if (oldFile == NULL) { + fprintf(stderr, "obnc-compile: error: Cannot open old file: %s: %s\n", filename, strerror(errno)); + exit(EXIT_FAILURE); + } + + assert(oldFile != NULL); + + return oldFile; +} + + +void Files_Close(FILE *file) +{ + int error; + + error = fclose(file); + if (error) { + fprintf(stderr, "obnc-compile: error: Closing file failed"); + exit(EXIT_FAILURE); + } +} + + +void Files_Move(const char sourceFilename[], const char destFilename[]) +{ + int error; + + assert(sourceFilename != NULL); + assert(destFilename != NULL); + + error = rename(sourceFilename, destFilename); + if (error) { + fprintf(stderr, "obnc-compile: error: Cannot move file %s to %s: %s\n", sourceFilename, destFilename, strerror(errno)); + exit(EXIT_FAILURE); + } +} + + +void Files_Remove(const char filename[]) +{ + int error; + + assert(filename != NULL); + + error = remove(filename); + if (error) { + fprintf(stderr, "obnc-compile: error: Cannot remove file: %s: %s\n", filename, strerror(errno)); + exit(EXIT_FAILURE); + } +} + + +void Files_CreateDir(const char dirname[]) +{ + const mode_t accessMode = 0755; + int error; + + assert(dirname != NULL); + assert(! Files_Exists(dirname)); + + error = mkdir(dirname, accessMode); + if (error) { + fprintf(stderr, "obnc-compile: error: Cannot create directory: %s: %s\n", dirname, strerror(errno)); + exit(EXIT_FAILURE); + } +} diff --git a/src/Files.h b/src/Files.h new file mode 100644 index 0000000..be11da8 --- /dev/null +++ b/src/Files.h @@ -0,0 +1,41 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef FILES_H +#define FILES_H + +#include + +/*file access modes*/ +#define FILES_READ 0 +#define FILES_WRITE 1 + +int Files_Exists(const char filename[]); + +FILE *Files_New(const char filename[]); + +FILE *Files_Old(const char filename[], int mode); + +void Files_CreateDir(const char dirname[]); + +void Files_Move(const char sourceFilename[], const char destFilename[]); + +void Files_Remove(const char filename[]); + +void Files_Close(FILE *file); + +#endif diff --git a/src/Generate.c b/src/Generate.c new file mode 100644 index 0000000..ac82a7f --- /dev/null +++ b/src/Generate.c @@ -0,0 +1,2685 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Generate.h" +#include "Config.h" +#include "Files.h" +#include "Maps.h" +#include "Oberon.h" +#include "Trees.h" +#include "Types.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" +#include "y.tab.h" +#include /*POSIX*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *inputModuleName; +static int isEntryPointModule; + +static char *headerComment; +static char tempCFilepath[PATH_MAX]; +static char tempHFilepath[PATH_MAX]; +static FILE *cFile; +static FILE *hFile; + +static Trees_Node importList; + +static Trees_Node declaredTypeIdent; + +static Trees_Node caseVariable; +static Trees_Node caseLabelType; + +static long int procedureDeclStart; +static struct ProcedureDeclNode { + Trees_Node procIdent; + Maps_Map localProcedures; + Trees_Node runtimeInitVars; + char *partialDecl; + struct ProcedureDeclNode *next; +} *procedureDeclStack; + +static void Indent(FILE *file, int n) +{ + int i; + + for (i = 0; i < n; i++) { + fputc('\t', file); + } +} + +static void Generate(Trees_Node tree, FILE *file, int indent); + + +/*IDENTIFIER GENERATORS*/ + +static int ModulePrefixNeeded(Trees_Node ident) +{ + int imported, indirectlyImported, exported, global, isType, isField; + + imported = Trees_Imported(ident); + indirectlyImported = ! imported && (strchr(Trees_Name(ident), '.') != NULL); + exported = Trees_Exported(ident); + global = ! Trees_Local(ident); + isType = Types_IsType(ident); + isField = Trees_Kind(ident) == TREES_FIELD_KIND; + + return ! isEntryPointModule && ! imported && ! indirectlyImported && ((exported && ! isField) || (global && isType)); +} + + +static void GenerateLocalProcedurePrefix(Trees_Node ident, struct ProcedureDeclNode *node, FILE *file) +{ + if (node != NULL) { + GenerateLocalProcedurePrefix(ident, node->next, file); + fprintf(file, "%s_", Trees_Name(node->procIdent)); + } +} + + +static void GenerateLocalProcedureIdent(Trees_Node ident, FILE *file, int indent) +{ + assert(procedureDeclStack != NULL); + Indent(file, indent); + if (Maps_HasKey(Trees_Name(ident), procedureDeclStack->localProcedures)) { + GenerateLocalProcedurePrefix(ident, procedureDeclStack, file); + } else { + GenerateLocalProcedurePrefix(ident, procedureDeclStack->next, file); + } + fprintf(file, "%s_Local", Trees_Name(ident)); +} + + +static void GenerateIdent(Trees_Node ident, FILE *file, int indent) +{ + const char *name; + char *dotPtr; + + name = Trees_UnaliasedName(ident); + if ((Trees_Kind(ident) == TREES_TYPE_KIND) && Types_Basic(Trees_Type(ident))) { + Generate(Trees_Type(ident), file, indent); + } else if (Trees_Internal(ident)) { + Indent(file, indent); + fprintf(file, "%s", name); + } else if (ModulePrefixNeeded(ident)) { + Indent(file, indent); + fprintf(file, "%s_%s_", inputModuleName, name); + } else if ((Trees_Kind(ident) == TREES_PROCEDURE_KIND) && Trees_Local(ident)) { + GenerateLocalProcedureIdent(ident, file, indent); + } else { + dotPtr = strchr(name, '.'); + if (dotPtr != NULL) { + *dotPtr = '_'; + } + Indent(file, indent); + fprintf(file, "%s_", name); + if (dotPtr != NULL) { + *dotPtr = '.'; + } + } +} + + +static const char *CurrentDirname(void) +{ + static char dir[PATH_MAX + 1]; + static const char *result = NULL; + const char *p; + + if (result == NULL) { + p = getcwd(dir, sizeof dir); + if (p != NULL) { + result = strrchr(dir, '/'); + assert(result != NULL); + result++; + } else { + fprintf(stderr, "obnc-compile: cannot get current directory: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + } + assert(result != NULL); + return result; +} + + +static const char *DirPrefix(void) +{ + static char result[16]; + static int initialized = 0; + const char *dir; + int i, j; + + if (! initialized) { + dir = CurrentDirname(); + i = 0; + j = 0; + while ((dir[i] != '\0') && (j < LEN(result) - 2)) { + if (((j == 0) && isalpha(dir[i])) || ((j > 0) && isalnum(dir[i]))) { + result[j] = dir[i]; + j++; + } + i++; + } + result[j] = '_'; + result[j + 1] = '\0'; + initialized = 1; + } + return result; +} + + +static void GenerateObjectFileSymbolDefinitions(Trees_Node identList, const char *suffix, FILE *file, int indent) +{ + const char *dirPrefix; + Trees_Node ident; + + /*NOTE: To prevent potential name collisions at link time when two modules with the same name (from different directories) are combined, we add a directory prefix to object file symbols with external linkage.*/ + + dirPrefix = DirPrefix(); + if (strcmp(dirPrefix, "") != 0) { + while (identList != NULL) { + ident = Trees_Left(identList); + Indent(file, indent); + fprintf(file, "#define "); + GenerateIdent(ident, file, 0); + fprintf(file, "%s %s", suffix, dirPrefix); + GenerateIdent(ident, file, 0); + fprintf(file, "%s\n", suffix); + identList = Trees_Right(identList); + } + } +} + + +/*LITERAL GENERATORS*/ + +static void GenerateReal(OBNC_LONGR double value, FILE *file) +{ + int formattedAsInteger; +#ifdef OBNC_CONFIG_USE_LONG_REAL + char buffer[LDBL_DIG + 10]; /*LDBL_DIG + strlen("-") + strlen(".") + strlen("e+9999") + strlen("L") + 1*/ + + sprintf(buffer, "%.*" OBNC_REAL_MOD_W "g", LDBL_DIG, value); +#else + char buffer[DBL_DIG + 8]; /*DBL_DIG + strlen("-") + strlen(".") + strlen("e+999") + 1*/ + + sprintf(buffer, "%.*g", DBL_DIG, value); +#endif + if (strcmp(buffer, "inf") == 0) { + fprintf(file, "(1.0 / 0.0)"); + } else if (strcmp(buffer, "-inf") == 0) { + fprintf(file, "(-1.0 / 0.0)"); + } else if (strcmp(buffer, "nan") == 0) { + fprintf(file, "(0.0 / 0.0)"); + } else if (strcmp(buffer, "-nan") == 0) { + fprintf(file, "(0.0 / 0.0)"); + } else { + formattedAsInteger = (strchr(buffer, '.') == NULL) && (strchr(buffer, 'e') == 0); + if (formattedAsInteger) { + strcat(buffer, ".0"); + } +#ifdef OBNC_CONFIG_USE_LONG_REAL + strcat(buffer, "L"); +#endif + fprintf(file, "%s", buffer); + } +} + + +static void GenerateString(const char s[], FILE *file) +{ + int i; + + fputc('"', file); + i = 0; + while (s[i] != '\0') { + if ((s[i] >= 0) && ((unsigned char) s[i] <= 127)) { + if (isprint(s[i])) { + if ((s[i] == '"') || (s[i] == '\\')) { + fputc('\\', file); + } + fputc(s[i], file); + } else { + fprintf(file, "\" \"\\x%02x\" \"", (unsigned char) s[i]); + } + } else { + fputc(s[i], file); + } + i++; + } + fputc('"', file); +} + + +static void GenerateChar(char ch, FILE *file) +{ + switch (ch) { + case '\'': + case '\\': + fprintf(file, "'\\%c'", ch); + break; + default: + if (isprint(ch)) { + fprintf(file, "'%c'", ch); + } else { + fprintf(file, "'\\x%x'", (unsigned char) ch); + } + } +} + + +/*CONSTANT DECLARATION GENERATORS*/ + +void Generate_ConstDeclaration(Trees_Node ident) +{ + if (Trees_Exported(ident)) { + /*add constant declaration to header file to provide access to it from hand-written C file*/ + fprintf(hFile, "\n#define "); + Generate(ident, hFile, 0); + fprintf(hFile, " "); + Generate(Trees_Value(ident), hFile, 0); + fprintf(hFile, "\n"); + } +} + + +/*TYPE DECLARATION GENERATORS*/ + +static void GenerateDeclaration(Trees_Node declaration, FILE *file, int indent); + +static void GenerateFields(Trees_Node type, FILE *file, int indent) +{ + Trees_Node typeDesc, baseType, pointerBaseType, fieldListSeq, identList; + + assert(type != NULL); + + typeDesc = Types_Descriptor(type); + fieldListSeq = Types_Fields(typeDesc); + baseType = Types_RecordBaseType(typeDesc); + if (baseType != NULL) { + if (Types_IsPointer(baseType)) { + pointerBaseType = Types_PointerBaseType(baseType); + if (Trees_Symbol(pointerBaseType) == RECORD) { + Indent(file, indent); + fprintf(file, "struct "); + Generate(baseType, file, 0); + } else { + assert(Trees_Symbol(pointerBaseType) == IDENT); + Generate(pointerBaseType, file, indent); + } + } else { + Generate(baseType, file, indent); + } + fprintf(file, " base;\n"); + } else if (fieldListSeq == NULL) { + Indent(file, indent); + fprintf(file, "char dummy;\n"); + } + while (fieldListSeq != NULL) { + identList = Trees_Left(fieldListSeq); + GenerateDeclaration(Trees_NewNode(TREES_NOSYM, identList, NULL), file, indent); + fieldListSeq = Trees_Right(fieldListSeq); + } +} + + +static void GenerateRecord(Trees_Node type, Trees_Node declIdent, FILE *file, int indent) +{ + Indent(file, indent); + fprintf(file, "struct "); + if ((declIdent != NULL) && (Trees_Kind(declIdent) == TREES_TYPE_KIND)) { + Generate(declIdent, file, 0); + fprintf(file, " "); + } + fprintf(file, "{\n"); + GenerateFields(type, file, indent + 1); + Indent(file, indent); + fprintf(file, "}"); +} + + +static Trees_Node TypeDescIdent(Trees_Node type) +{ + Trees_Node result, initialIdent, unaliasedIdent, typeStruct, pointerBaseType; + + result = NULL; + initialIdent = type; + if (Trees_Symbol(type) == POINTER) { + initialIdent = Trees_Left(type); + assert(Trees_Symbol(initialIdent) == IDENT); + } + unaliasedIdent = Types_UnaliasedIdent(initialIdent); + typeStruct = Types_Structure(unaliasedIdent); + switch (Trees_Symbol(typeStruct)) { + case RECORD: + result = unaliasedIdent; + break; + case POINTER: + pointerBaseType = Types_PointerBaseType(typeStruct); + switch (Trees_Symbol(pointerBaseType)) { + case RECORD: + result = unaliasedIdent; + break; + case IDENT: + result = Types_UnaliasedIdent(pointerBaseType); + break; + default: + assert(0); + } + break; + default: + assert(0); + } + + assert(result != NULL); + + return result; +} + + +static void GenerateStorageClassSpecifier(Trees_Node ident, FILE *file) +{ + if (Trees_Kind(ident) == TREES_TYPE_KIND) { + fprintf(file, "typedef "); + } else if (! Trees_Local(ident)) { + if (file == hFile) { + fprintf(file, "extern "); + } else if (! Trees_Exported(ident)) { + fprintf(file, "static "); + } + } +} + + +static int TypeIncomplete(Trees_Node type, Trees_Node ident) +{ + return ((Trees_Kind(ident) == TREES_TYPE_KIND) + && ((Trees_Type(type) == NULL) || Types_IsRecord(type))) + || (type == declaredTypeIdent); +} + + +static void GenerateTypeSpecifier(Trees_Node ident, Trees_Node type, FILE *file, int indent) +{ + switch (Trees_Symbol(type)) { + case IDENT: + if (TypeIncomplete(type, ident)) { + fprintf(file, "struct "); + } + Generate(type, file, 0); + break; + case ARRAY: + GenerateTypeSpecifier(ident, Types_ElementType(type), file, indent); + break; + case RECORD: + GenerateRecord(type, ident, file, indent); + break; + case POINTER: + GenerateTypeSpecifier(ident, Types_PointerBaseType(type), file, indent); + break; + case PROCEDURE: + if (Types_ResultType(type) != NULL) { + GenerateTypeSpecifier(ident, Types_ResultType(type), file, indent); + } else { + Indent(file, indent); + fprintf(file, "void"); + } + break; + default: + Generate(type, file, indent); + } +} + + +static void GenerateFormalParameterList(Trees_Node paramList, FILE *file, int indent); + +static void GenerateDeclarator(Trees_Node ident, FILE *file) +{ + Trees_Node type, firstNonArrayType, resultType; + + type = Trees_Type(ident); + firstNonArrayType = type; + while (Trees_Symbol(firstNonArrayType) == ARRAY) { + firstNonArrayType = Types_ElementType(firstNonArrayType); + } + if ((Trees_Symbol(firstNonArrayType) == POINTER) + || (Types_IsPointer(firstNonArrayType) && TypeIncomplete(firstNonArrayType, ident))) { + fprintf(file, "*"); + } else if (Trees_Symbol(firstNonArrayType) == PROCEDURE) { + resultType = Types_ResultType(firstNonArrayType); + if ((declaredTypeIdent != NULL) && (resultType == declaredTypeIdent)) { + fprintf(file, "*"); + } + fprintf(file, "(*"); + } + Generate(ident, file, 0); + while (Trees_Symbol(type) == ARRAY) { + fprintf(file, "[%" OBNC_INT_MOD "d]", Trees_Integer(Types_ArrayLength(type))); + type = Types_ElementType(type); + } + if (Trees_Symbol(firstNonArrayType) == PROCEDURE) { + fprintf(file, ")("); + if (Types_Parameters(type) != NULL) { + GenerateFormalParameterList(Types_Parameters(firstNonArrayType), file, 0); + } else { + fprintf(file, "void"); + } + fprintf(file, ")"); + } +} + + +static void SearchPointersAndProceduresRec(Trees_Node type, int *hasPointer, int *hasProcedure) +{ + Trees_Node recordBaseType, fieldListSeq, fieldList, ident; + + if ((type != NULL) && ! (*hasPointer && *hasProcedure)) { + switch (Trees_Symbol(Types_Structure(type))) { + case ARRAY: + SearchPointersAndProceduresRec(Types_ElementType(type), hasPointer, hasProcedure); + break; + case RECORD: + recordBaseType = Types_RecordBaseType(type); + if (recordBaseType != NULL) { + SearchPointersAndProceduresRec( + Types_Descriptor(recordBaseType), hasPointer, hasProcedure); + } + fieldListSeq = Types_Fields(type); + while ((fieldListSeq != NULL) && ! (*hasPointer && *hasProcedure)) { + fieldList = Trees_Left(fieldListSeq); + ident = Trees_Left(fieldList); + SearchPointersAndProceduresRec(Trees_Type(ident), hasPointer, hasProcedure); + fieldListSeq = Trees_Right(fieldListSeq); + } + break; + case POINTER: + *hasPointer = 1; + break; + case PROCEDURE: + *hasProcedure = 1; + break; + } + } +} + + +static void SearchPointersAndProcedures(Trees_Node type, int *hasPointer, int *hasProcedure) +{ + *hasPointer = 0; + *hasProcedure = 0; + SearchPointersAndProceduresRec(type, hasPointer, hasProcedure); +} + + +static void GenerateDeclaration(Trees_Node declaration, FILE *file, int indent) +{ + Trees_Node identList, firstIdent, ident; + int hasPointer, hasProcedure; + + if (Trees_Symbol(Trees_Left(declaration)) == IDENT) { + identList = Trees_NewNode(TREES_IDENT_LIST, Trees_Left(declaration), NULL); + } else { + identList = Trees_Left(declaration); + } + firstIdent = Trees_Left(identList); + + Indent(file, indent); + GenerateStorageClassSpecifier(firstIdent, file); + GenerateTypeSpecifier(firstIdent, Trees_Type(firstIdent), file, indent); + fprintf(file, " "); + + do { + ident = Trees_Left(identList); + GenerateDeclarator(ident, file); + + if ((Trees_Kind(firstIdent) == TREES_VARIABLE_KIND) && Trees_Local(firstIdent) && (file != hFile)) { + switch (Trees_Symbol(Types_Structure(Trees_Type(firstIdent)))) { + case ARRAY: + case RECORD: + SearchPointersAndProcedures(Trees_Type(firstIdent), &hasPointer, &hasProcedure); + if (hasPointer || hasProcedure) { + fprintf(file, " = {0}"); + } + break; + case POINTER: + case PROCEDURE: + fprintf(file, " = 0"); + break; + } + } + if (Trees_Right(identList) != NULL) { + fprintf(file, ", "); + } + identList = Trees_Right(identList); + } while (identList != NULL); + + fprintf(file, ";\n"); +} + + +static void GenerateTypeIDs(Trees_Node type) +{ + Trees_Node baseType; + + baseType = Types_RecordBaseType(type); + if (baseType != NULL) { + GenerateTypeIDs(baseType); + fprintf(cFile, ", "); + } + fprintf(cFile, "&"); + Generate(TypeDescIdent(type), cFile, 0); + fprintf(cFile, "id"); +} + + +static void GenerateHeapTypeDecl(Trees_Node typeIdent, FILE* file, int indent) +{ + Indent(file, indent); + fprintf(file, "struct "); + Generate(typeIdent, file, 0); + fprintf(file, "Heap {\n"); + Indent(file, indent + 1); + fprintf(file, "const OBNC_Td *td;\n"); + Indent(file, indent + 1); + fprintf(file, "struct "); + Generate(typeIdent, file, 0); + Indent(file, indent); + fprintf(file, " fields;\n"); + Indent(file, indent); + fprintf(file, "};\n"); +} + + +static void GenerateTypeDescDecl(Trees_Node typeIdent, int indent) +{ + int extensionLevel; + Trees_Node identList; + const char *storageClass; + + + /*generate type descriptor (type ID used for its unique address only)*/ + extensionLevel = Types_ExtensionLevel(typeIdent); + if (ModulePrefixNeeded(typeIdent)) { + identList = Trees_NewNode(TREES_NOSYM, typeIdent, NULL); + + fprintf(hFile, "\n"); + GenerateObjectFileSymbolDefinitions(identList, "id", hFile, 0); + Indent(hFile, indent); + fprintf(hFile, "extern const int "); + Generate(typeIdent, hFile, 0); + fprintf(hFile, "id;\n\n"); + + GenerateObjectFileSymbolDefinitions(identList, "ids", hFile, 0); + Indent(hFile, indent); + fprintf(hFile, "extern const int *const "); + Generate(typeIdent, hFile, 0); + fprintf(hFile, "ids[%d];\n\n", extensionLevel + 1); + + GenerateObjectFileSymbolDefinitions(identList, "td", hFile, 0); + Indent(hFile, indent); + fprintf(hFile, "extern const OBNC_Td "); + Generate(typeIdent, hFile, 0); + fprintf(hFile, "td;\n"); + + storageClass = ""; + } else { + storageClass = "static "; + } + fprintf(cFile, "\n"); + Indent(cFile, indent); + fprintf(cFile, "%sconst int ", storageClass); + Generate(typeIdent, cFile, 0); + fprintf(cFile, "id;\n"); + + Indent(cFile, indent); + fprintf(cFile, "%sconst int *const ", storageClass); + Generate(typeIdent, cFile, 0); + fprintf(cFile, "ids[%d] = {", extensionLevel + 1); + GenerateTypeIDs(typeIdent); + fprintf(cFile, "};\n"); + + Indent(cFile, indent); + fprintf(cFile, "%sconst OBNC_Td ", storageClass); + Generate(typeIdent, cFile, 0); + fprintf(cFile, "td = {"); + Generate(typeIdent, cFile, 0); + fprintf(cFile, "ids, %d};\n", extensionLevel + 1); +} + + +void Generate_TypeDeclaration(Trees_Node ident) +{ + int indent = Trees_Local(ident)? 1: 0; + Trees_Node type, declaration, typeDescIdent; + int modulePrefixNeeded; + + type = Trees_Type(ident); + modulePrefixNeeded = ModulePrefixNeeded(ident); + + declaredTypeIdent = ident; + declaration = Trees_NewNode(TREES_NOSYM, ident, type); + if (modulePrefixNeeded) { + fprintf(hFile, "\n"); + GenerateDeclaration(declaration, hFile, indent); + } else { + if (! Trees_Local(ident)) { + fprintf(cFile, "\n"); + } + GenerateDeclaration(declaration, cFile, indent); + } + declaredTypeIdent = NULL; + if ((Trees_Symbol(type) == RECORD) + || ((Trees_Symbol(type) == POINTER) && (Trees_Symbol(Types_PointerBaseType(type)) == RECORD))) { + typeDescIdent = TypeDescIdent(ident); + + if (modulePrefixNeeded) { + fprintf(hFile, "\n"); + GenerateHeapTypeDecl(typeDescIdent, hFile, 0); + } else { + fprintf(cFile, "\n"); + GenerateHeapTypeDecl(typeDescIdent, cFile, indent); + } + GenerateTypeDescDecl(typeDescIdent, indent); + } +} + + +/*VARIABLE DECLARATION GENERATORS*/ + +static int HasExportedIdent(Trees_Node identList) +{ + while ((identList != NULL) && ! Trees_Exported(Trees_Left(identList))) { + identList = Trees_Right(identList); + } + return identList != NULL; +} + + +static int NameEquivalenceNeeded(Trees_Node type) +{ + int result; + + assert(type != NULL); + + switch (Trees_Symbol(type)) { + case ARRAY: + result = NameEquivalenceNeeded(Types_ElementType(type)); + break; + case RECORD: + result = 1; + break; + case POINTER: + result = (Trees_Symbol(Types_PointerBaseType(type)) == RECORD); + break; + default: + result = 0; + } + return result; +} + + +static int DigitCount(int i) +{ + int n = 0; + + do { + n++; + i = i / 10; + } while (i != 0); + return n; +} + + +void Generate_VariableDeclaration(Trees_Node identList) +{ + static int typeCounter; + + char *newTypeName; + int newTypeNameLen, allExported; + Trees_Node ident, type, declaration, newTypeIdent, newTypeDecl, p, exportedIdents, nonExportedIdents, exportedDecl, nonExportedDecl; + int indent; + + ident = Trees_Left(identList); + indent = Trees_Local(ident)? 1: 0; + type = Trees_Type(ident); + declaration = Trees_NewNode(TREES_NOSYM, identList, type); + if (! Trees_Local(ident)) { + fprintf(cFile, "\n"); + } + if (HasExportedIdent(identList) && ! isEntryPointModule) { + fprintf(hFile, "\n"); + if (NameEquivalenceNeeded(type)) { + /*declare anonymous type in header file*/ + newTypeNameLen = strlen(inputModuleName) + strlen("_T") + DigitCount(typeCounter) + 1; + NEW_ARRAY(newTypeName, newTypeNameLen); + sprintf(newTypeName, "%s_T%d", inputModuleName, typeCounter); + + newTypeIdent = Trees_NewIdent(newTypeName); + Trees_SetKind(TREES_TYPE_KIND, newTypeIdent); + Trees_SetType(type, newTypeIdent); + Trees_SetInternal(newTypeIdent); + newTypeDecl = Trees_NewNode(TREES_NOSYM, newTypeIdent, type); + + GenerateDeclaration(newTypeDecl, hFile, indent); + + /*replace anonymous type with named type*/ + p = identList; + do { + ident = Trees_Left(p); + Trees_SetType(newTypeIdent, ident); + p = Trees_Right(p); + } while (p != NULL); + + typeCounter++; + } + + allExported = 1; + p = identList; + do { + ident = Trees_Left(p); + if (! Trees_Exported(ident)) { + allExported = 0; + } + p = Trees_Right(p); + } while ((p != NULL) && allExported); + + if (allExported) { + GenerateObjectFileSymbolDefinitions(identList, "", hFile, indent); + GenerateDeclaration(declaration, hFile, indent); + GenerateDeclaration(declaration, cFile, indent); + } else { + exportedIdents = NULL; + nonExportedIdents = NULL; + p = identList; + do { + ident = Trees_Left(p); + if (Trees_Exported(ident)) { + exportedIdents = Trees_NewNode(TREES_IDENT_LIST, ident, exportedIdents); + } else { + nonExportedIdents = Trees_NewNode(TREES_IDENT_LIST, ident, nonExportedIdents); + } + p = Trees_Right(p); + } while (p != NULL); + assert(exportedIdents != NULL); + Trees_ReverseList(&exportedIdents); + exportedDecl = Trees_NewNode(TREES_NOSYM, exportedIdents, Trees_Right(declaration)); + GenerateObjectFileSymbolDefinitions(exportedIdents, "", hFile, indent); + GenerateDeclaration(exportedDecl, hFile, indent); + GenerateDeclaration(exportedDecl, cFile, indent); + if (nonExportedIdents != NULL) { + Trees_ReverseList(&nonExportedIdents); + nonExportedDecl = Trees_NewNode(TREES_NOSYM, nonExportedIdents, Trees_Right(declaration)); + GenerateDeclaration(nonExportedDecl, cFile, indent); + } + } + + if (Trees_Symbol(type) != IDENT) { + /*reset original type*/ + p = identList; + do { + ident = Trees_Left(p); + Trees_SetType(type, ident); + p = Trees_Right(p); + } while (p != NULL); + } + } else { + GenerateDeclaration(declaration, cFile, indent); + } +} + + +/*EXPRESSION GENERATORS*/ + +static Trees_Node VarIdent(Trees_Node var) +{ + assert(Trees_Symbol(var) == TREES_DESIGNATOR); + return Trees_Left(var); +} + + +static Trees_Node VarSelector(Trees_Node var) +{ + assert(Trees_Symbol(var) == TREES_DESIGNATOR); + return Trees_Right(var); +} + + +static int IsVarParam(Trees_Node var) +{ + return (Trees_Kind(VarIdent(var)) == TREES_VAR_PARAM_KIND) && (VarSelector(var) == NULL); +} + + +static int IsProcedureCall(int symbol) +{ + int result; + + switch (symbol) { + case TREES_ABS_PROC: + case TREES_ODD_PROC: + case TREES_LEN_PROC: + case TREES_LSL_PROC: + case TREES_ASR_PROC: + case TREES_ROR_PROC: + case TREES_FLOOR_PROC: + case TREES_FLT_PROC: + case TREES_ORD_PROC: + case TREES_CHR_PROC: + case TREES_INC_PROC: + case TREES_DEC_PROC: + case TREES_INCL_PROC: + case TREES_EXCL_PROC: + /*case TREES_NEW_PROC*/ + case TREES_ASSERT_PROC: + case TREES_PACK_PROC: + case TREES_UNPK_PROC: + case TREES_PROCEDURE_CALL: + result = 1; + break; + default: + result = 0; + } + return result; +} + + +static void PrintCOperator(Trees_Node opNode, FILE *file) +{ + int leftType, rightType; + + leftType = Trees_Symbol(Types_Structure(Trees_Type(Trees_Left(opNode)))); + if (Trees_Right(opNode) != NULL) { + rightType = Trees_Symbol(Types_Structure(Trees_Type(Trees_Right(opNode)))); + } else { + rightType = -1; + } + + switch (Trees_Symbol(opNode)) { + case '#': + fprintf(file, "!="); + break; + case '&': + fprintf(file, "&&"); + break; + case '*': + if (leftType == TREES_SET_TYPE) { + fprintf(file, "&"); + } else { + fprintf(file, "*"); + } + break; + case '+': + if ((leftType == TREES_SET_TYPE) && (rightType >= 0)) { + fprintf(file, "|"); + } else { + fprintf(file, "+"); + } + break; + case '-': + if (leftType == TREES_SET_TYPE) { + if (rightType == -1) { + fprintf(file, "~"); + } else { + fprintf(file, "& ~"); + } + } else { + fprintf(file, "-"); + } + break; + case '/': + if (leftType == TREES_SET_TYPE) { + fprintf(file, "^"); + } else { + fprintf(file, "/"); + } + break; + case '<': + fprintf(file, "<"); + break; + case '=': + fprintf(file, "=="); + break; + case '>': + fprintf(file, ">"); + break; + case '~': + fprintf(file, "! "); + break; + case OR: + fprintf(file, "||"); + break; + case GE: + fprintf(file, ">="); + break; + case LE: + fprintf(file, "<="); + break; + default: + assert(0); + } +} + + +static void GenerateArrayLength(Trees_Node ident, Trees_Node arrayType, FILE *file) +{ + assert(Trees_Symbol(ident) == IDENT); + assert(Types_IsArray(arrayType)); + + if (Types_IsOpenArray(arrayType)) { + Generate(ident, file, 0); + fprintf(file, "len"); + } else { + fprintf(file, "%" OBNC_INT_MOD "d", Trees_Integer(Types_ArrayLength(arrayType))); + } +} + + +static void GenerateNonScalarOperation(Trees_Node opNode, FILE *file, int indent) +{ + Trees_Node leftOperand, rightOperand; + Trees_Node leftType, rightType; + + leftOperand = Trees_Left(opNode); + rightOperand = Trees_Right(opNode); + leftType = Types_Structure(Trees_Type(leftOperand)); + rightType = Types_Structure(Trees_Type(rightOperand)); + + switch (Trees_Symbol(opNode)) { + case '=': + case '#': + case '<': + case LE: + case '>': + case GE: + Indent(file, indent); + fprintf(file, "OBNC_CMP("); + Generate(leftOperand, file, indent); + fprintf(file, ", "); + if (Trees_Symbol(leftType) == TREES_STRING_TYPE) { + fprintf(file, "%lu", (long unsigned) strlen(Trees_String(leftOperand)) + 1); + } else { + GenerateArrayLength(VarIdent(leftOperand), leftType, file); + } + fprintf(file, ", "); + Generate(rightOperand, file, indent); + fprintf(file, ", "); + if (Trees_Symbol(rightType) == TREES_STRING_TYPE) { + fprintf(file, "%lu", (long unsigned) strlen(Trees_String(rightOperand)) + 1); + } else { + GenerateArrayLength(VarIdent(rightOperand), rightType, file); + } + fprintf(file, ") "); + PrintCOperator(opNode, file); + fprintf(file, " 0"); + break; + default: + assert(0); + } +} + + +static void GenerateWithPrecedence(Trees_Node exp, FILE *file) +{ + if (Trees_IsLeaf(exp) + || (Trees_Symbol(exp) == TREES_DESIGNATOR) + || IsProcedureCall(Trees_Symbol(exp))) { + Generate(exp, file, 0); + } else { + fprintf(file, "("); + Generate(exp, file, 0); + fprintf(file, ")"); + } +} + + +static void GenerateTypeDescExp(Trees_Node var, FILE *file, int indent) +{ + if (Types_IsRecord(Trees_Type(var))) { + if (IsVarParam(var)) { + GenerateIdent(VarIdent(var), file, indent); + fprintf(file, "td"); + } else { + fprintf(file, "&"); + GenerateIdent(TypeDescIdent(Trees_Type(var)), file, 0); + fprintf(file, "td"); + } + } else { + assert(Types_IsPointer(Trees_Type(var))); + fprintf(file, "OBNC_TD("); + Generate(var, file, 0); + fprintf(file, ", struct "); + Generate(TypeDescIdent(Trees_Type(var)), file, 0); + fprintf(file, "Heap)"); + } +} + + +static void GenerateISExpression(Trees_Node var, Trees_Node type, FILE *file) +{ + int isPointer; + + isPointer = Types_IsPointer(Trees_Type(var)); + if (isPointer) { + fprintf(file, "((void) OBNC_PT("); + Generate(var, file, 0); + fprintf(file, "), "); + } + fprintf(file, "OBNC_IS("); + GenerateTypeDescExp(var, file, 0); + fprintf(file, ", &"); + Generate(TypeDescIdent(type), file, 0); + fprintf(file, "id, %d)", Types_ExtensionLevel(type)); + if (isPointer) { + fprintf(file, ")"); + } +} + + +static void GenerateOperator(Trees_Node opNode, FILE *file) +{ + Trees_Node leftOperand, rightOperand, leftType, rightType; + int opSym; + + leftOperand = Trees_Left(opNode); + rightOperand = Trees_Right(opNode); + opSym = Trees_Symbol(opNode); + + if (Trees_Right(opNode) == NULL) { + /*unary operator*/ + PrintCOperator(opNode, file); + GenerateWithPrecedence(leftOperand, file); + } else { + /*binary operator*/ + leftType = Trees_Type(leftOperand); + rightType = Trees_Type(rightOperand); + + if ((Types_IsString(leftType) || Types_IsCharacterArray(leftType)) + && (Types_IsString(rightType) || Types_IsCharacterArray(rightType))) { + GenerateNonScalarOperation(opNode, file, 0); + } else { + switch (opSym) { + case DIV: + case MOD: + if (opSym == DIV) { + fprintf(file, "OBNC_DIV("); + } else { + fprintf(file, "OBNC_MOD("); + } + Generate(leftOperand, file, 0); + fprintf(file, ", "); + Generate(rightOperand, file, 0); + fprintf(file, ")"); + break; + case '<': + case LE: + case '>': + case GE: + if (Types_IsChar(Trees_Type(leftOperand))) { + fprintf(file, "(unsigned char) "); + } + GenerateWithPrecedence(leftOperand, file); + fprintf(file, " "); + PrintCOperator(opNode, file); + fprintf(file, " "); + if (Types_IsChar(Trees_Type(rightOperand))) { + fprintf(file, "(unsigned char) "); + } + GenerateWithPrecedence(rightOperand, file); + break; + default: + if (Types_IsPointer(leftType) && (Trees_Symbol(leftOperand) != NIL) && ! Types_Same(leftType, rightType) && (Trees_Symbol(rightOperand) != NIL)) { + if (Types_Extends(leftType, rightType)) { + GenerateWithPrecedence(leftOperand, file); + fprintf(file, " "); + PrintCOperator(opNode, file); + fprintf(file, " ("); + Generate(leftType, file, 0); + fprintf(file, ") "); + GenerateWithPrecedence(rightOperand, file); + } else { + fprintf(file, "("); + Generate(rightType, file, 0); + fprintf(file, ") "); + GenerateWithPrecedence(leftOperand, file); + fprintf(file, " "); + PrintCOperator(opNode, file); + fprintf(file, " "); + GenerateWithPrecedence(rightOperand, file); + } + } else { + GenerateWithPrecedence(leftOperand, file); + fprintf(file, " "); + PrintCOperator(opNode, file); + fprintf(file, " "); + GenerateWithPrecedence(rightOperand, file); + } + } + } + } +} + + +static int IsConstExpression(Trees_Node exp) +{ + int result; + + result = 0; + switch (Trees_Symbol(exp)) { + case TRUE: + case FALSE: + case STRING: + case INTEGER: + case REAL: + case TREES_SET_CONSTANT: + result = 1; + } + return result; +} + + +static int ContainsProcedureCall(Trees_Node exp) +{ + int result; + + result = 0; + if (exp != NULL) { + if (Trees_Symbol(exp) == TREES_PROCEDURE_CALL) { + result = 1; + } else { + result = ContainsProcedureCall(Trees_Left(exp)); + if (result == 0) { + result = ContainsProcedureCall(Trees_Right(exp)); + } + } + } + return result; +} + + +static void GenerateArrayIndex(Trees_Node designator, Trees_Node elemSelector, FILE *file) +{ + Trees_Node indexExp, type, ident; + + assert(designator != NULL); + assert(Trees_Symbol(designator) == TREES_DESIGNATOR); + assert(elemSelector != NULL); + assert(Trees_Symbol(elemSelector) == '['); + + ident = Trees_Left(designator); + indexExp = Trees_Left(elemSelector); + type = Trees_Type(elemSelector); + assert(Types_IsArray(type)); + + if (IsConstExpression(indexExp)) { + Generate(indexExp, file, 0); + } else { + if (ContainsProcedureCall(indexExp)) { + fprintf(file, "OBNC_IT1("); + } else { + fprintf(file, "OBNC_IT("); + } + Generate(indexExp, file, 0); + fprintf(file, ", "); + GenerateArrayLength(ident, type, file); + fprintf(file, ")"); + } +} + + +static void GenerateDesignatorVar(Trees_Node ident, FILE *file) +{ + int identKind, paramDerefNeeded; + Trees_Node identType; + + identKind = Trees_Kind(ident); + identType = Trees_Type(ident); + paramDerefNeeded = ((identKind == TREES_VALUE_PARAM_KIND) && Types_IsRecord(identType)) + || ((identKind == TREES_VAR_PARAM_KIND) && ! Types_IsArray(identType)); + + if (paramDerefNeeded) { + fprintf(file, "(*"); + Generate(ident, file, 0); + fprintf(file, ")"); + } else { + Generate(ident, file, 0); + } +} + + +static void GenerateDesignatorRec(Trees_Node des, Trees_Node reversedSelectors, FILE *file) +{ + Trees_Node field, fieldIdent, fieldBaseType, typeIdent; + int castNeeded; + + if (reversedSelectors == NULL) { + if ((caseVariable != NULL) && (caseLabelType != NULL) && (VarIdent(des) == caseVariable) && ! Types_Same(Trees_Type(caseVariable), caseLabelType)) { + fprintf(file, "(*(("); + Generate(caseLabelType, file, 0); + fprintf(file, " *) &"); + GenerateDesignatorVar(VarIdent(des), file); + fprintf(file, "))"); + } else { + GenerateDesignatorVar(VarIdent(des), file); + } + } else { + switch (Trees_Symbol(reversedSelectors)) { + case '[': + GenerateDesignatorRec(des, Trees_Right(reversedSelectors), file); + fprintf(file, "["); + GenerateArrayIndex(des, reversedSelectors, file); + fprintf(file, "]"); + break; + case '.': + field = Trees_Left(reversedSelectors); + Types_GetFieldIdent(Trees_Name(field), Trees_Type(reversedSelectors), Trees_Imported(VarIdent(des)), &fieldIdent, &fieldBaseType); + castNeeded = ! Types_Same(fieldBaseType, Trees_Type(reversedSelectors)); + if (castNeeded) { + fprintf(file, "(*(("); + Generate(fieldBaseType, file, 0); + if (Types_IsRecord(fieldBaseType)) { + fprintf(file, " *"); + } + fprintf(file, ") &"); + } + GenerateDesignatorRec(des, Trees_Right(reversedSelectors), file); + if (castNeeded) { + fprintf(file, "))"); + } + fprintf(file, "."); + Generate(Trees_Left(reversedSelectors), file, 0); + break; + case '^': + fprintf(file, "(*OBNC_PT("); + GenerateDesignatorRec(des, Trees_Right(reversedSelectors), file); + fprintf(file, "))"); + break; + case '(': + typeIdent = Trees_Left(reversedSelectors); + + fprintf(file, "(*(("); + Generate(typeIdent, file, 0); + if (Types_IsRecord(typeIdent)) { + fprintf(file, "*) OBNC_RTT(&("); + } else { + fprintf(file, "*) OBNC_PTT(&("); + } + GenerateDesignatorRec(des, Trees_Right(reversedSelectors), file); + fprintf(file, "), "); + if (Types_IsRecord(typeIdent)) { + if (IsVarParam(des) && (reversedSelectors == VarSelector(des))) { + GenerateIdent(VarIdent(des), file, 0); + fprintf(file, "td"); + } else { + fprintf(file, "&"); + GenerateIdent(TypeDescIdent(Trees_Type(reversedSelectors)), file, 0); + fprintf(file, "td"); + } + } else { + assert(Types_IsPointer(typeIdent)); + fprintf(file, "OBNC_TD("); + GenerateDesignatorRec(des, Trees_Right(reversedSelectors), file); + fprintf(file, ", struct "); + Generate(TypeDescIdent(Trees_Type(reversedSelectors)), file, 0); + fprintf(file, "Heap)"); + } + fprintf(file, ", &"); + Generate(TypeDescIdent(typeIdent), file, 0); + fprintf(file, "id, %d)))", Types_ExtensionLevel(typeIdent)); + break; + default: + assert(0); + } + } +} + + +static void GenerateDesignator(Trees_Node des, FILE *file) +{ + Trees_Node selectors; + + selectors = Trees_Right(des); + Trees_ReverseList(&selectors); + GenerateDesignatorRec(des, selectors, file); + Trees_ReverseList(&selectors); /*reset order*/ +} + + +static void GenerateSingleElementSet(Trees_Node node, FILE *file) +{ + fprintf(file, "(0x1u << "); + GenerateWithPrecedence(Trees_Left(node), file); + fprintf(file, ")"); +} + + +static void GenerateRangeSet(Trees_Node node, FILE *file) +{ + fprintf(file, "OBNC_RANGE("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ")"); +} + + +static void GenerateExpList(Trees_Node expList, FILE *file) +{ + Trees_Node exp, tail; + + exp = Trees_Right(expList); + Generate(exp, file, 0); + tail = Trees_Right(expList); + if (tail != NULL) { + fprintf(file, ", "); + Generate(tail, file, 0); + } +} + + +/*STATEMENT GENERATORS*/ + +static void GenerateArrayAssignment(Trees_Node source, Trees_Node target, FILE *file, int indent) +{ + Trees_Node sourceIdent, targetIdent, sourceType, targetType; + + assert(Trees_Symbol(target) == TREES_DESIGNATOR); + + if (Trees_Symbol(source) == TREES_DESIGNATOR) { + sourceIdent = Trees_Left(source); + } else { + sourceIdent = source; + } + targetIdent = Trees_Left(target); + sourceType = Trees_Type(source); + targetType = Types_Structure(Trees_Type(target)); + assert(Trees_Symbol(targetType) == ARRAY); + + if (Types_IsOpenArray(sourceType) || Types_IsOpenArray(targetType)) { + Indent(file, indent); + fprintf(file, "OBNC_AAT("); + if (Trees_Symbol(source) == STRING) { + fprintf(file, "%lu", (long unsigned) strlen(Trees_String(source)) + 1); + } else { + GenerateArrayLength(sourceIdent, sourceType, file); + } + fprintf(file, ", "); + GenerateArrayLength(targetIdent, targetType, file); + fprintf(file, ");\n"); + } + Indent(file, indent); + fprintf(file, "OBNC_COPY_ARRAY("); + Generate(source, file, 0); + fprintf(file, ", "); + Generate(target, file, 0); + fprintf(file, ", "); + if (Trees_Symbol(source) == STRING) { + fprintf(file, "%lu", (long unsigned) strlen(Trees_String(source)) + 1); + } else { + GenerateArrayLength(sourceIdent, sourceType, file); + } + fprintf(file, ");\n"); +} + + +static void GenerateRecordAssignment(Trees_Node source, Trees_Node target, FILE *file, int indent) +{ + Trees_Node sourceType, targetType; + + sourceType = Trees_Type(source); + targetType = Trees_Type(target); + + if (IsVarParam(target)) { + Indent(file, indent); + fprintf(file, "OBNC_RAT("); + GenerateTypeDescExp(source, file, 0); + fprintf(file, ", "); + GenerateTypeDescExp(target, file, 0); + fprintf(file, ");\n"); + } + if (Types_Same(sourceType, targetType) && ! IsVarParam(target)) { + GenerateDesignator(target, file); + fprintf(file, " = "); + Generate(source, file, 0); + fprintf(file, ";\n"); + } else { + Generate(target, file, indent); + fprintf(file, " = "); + if (! Types_Same(sourceType, targetType)) { + assert(Types_Extends(targetType, sourceType)); + fprintf(file, "*("); + Generate(targetType, file, 0); + fprintf(file, " *) &"); + } + Generate(source, file, 0); + fprintf(file, ";\n"); + } +} + + +static int CastNeeded(Trees_Node sourceType, Trees_Node targetType) +{ + return (Types_IsByte(targetType) && ! Types_IsByte(sourceType)) + || ((Types_IsRecord(targetType) || Types_IsPointer(targetType)) + && (Trees_Symbol(sourceType) != TREES_NIL_TYPE) + && Types_Extends(targetType, sourceType) + && ! Types_Same(targetType, sourceType)); +} + + +static void GenerateAssignment(Trees_Node becomesNode, FILE *file, int indent) +{ + Trees_Node source, target; + Trees_Node sourceType, targetType; + + source = Trees_Right(becomesNode); + target = Trees_Left(becomesNode); + sourceType = Trees_Type(source); + targetType = Trees_Type(target); + + switch (Trees_Symbol(Types_Structure(targetType))) { + case ARRAY: + GenerateArrayAssignment(source, target, file, indent); + break; + case RECORD: + GenerateRecordAssignment(source, target, file, indent); + break; + default: + Indent(file, indent); + GenerateDesignator(target, file); + fprintf(file, " = "); + if (CastNeeded(sourceType, targetType)) { + fprintf(file, "("); + Generate(targetType, file, 0); + fprintf(file, ") "); + } + GenerateWithPrecedence(source, file); + fprintf(file, ";\n"); + } +} + + +static void GenerateProcedureCall(Trees_Node call, FILE *file, int indent) +{ + Trees_Node designator, designatorTypeStruct, expList, fpList, fpType, exp, expType, resultType; + int procKind, isProcVar, isValueParam, isVarParam; + + designator = Trees_Left(call); + designatorTypeStruct = Types_Structure(Trees_Type(designator)); + procKind = Trees_Kind(Trees_Left(designator)); + assert(Types_IsProcedure(designatorTypeStruct)); + resultType = Types_ResultType(designatorTypeStruct); + isProcVar = procKind != TREES_PROCEDURE_KIND; + + Indent(file, indent); + if (isProcVar) { + fprintf(file, "OBNC_PCT("); + Generate(designator, file, 0); + fprintf(file, ")"); + } else { + Generate(designator, file, 0); + } + + fprintf(file, "("); + + expList = Trees_Right(call); + fpList = Types_Parameters(designatorTypeStruct); + while (expList != NULL) { + assert(fpList != NULL); + exp = Trees_Left(expList); + expType = Trees_Type(exp); + isValueParam = Trees_Kind(Trees_Left(fpList)) == TREES_VALUE_PARAM_KIND; + isVarParam = Trees_Kind(Trees_Left(fpList)) == TREES_VAR_PARAM_KIND; + fpType = Trees_Type(Trees_Left(fpList)); + + if (isValueParam && Types_IsArray(fpType) && Types_IsArray(Types_ElementType(fpType))) { + /*cast to const needed for array of array parameters*/ + fprintf(file, "(const "); + Generate(Types_ElementType(expType), file, 0); + fprintf(file, " *) "); + } else if (CastNeeded(expType, fpType)) { + fprintf(file, "("); + Generate(fpType, file, 0); + if ((isVarParam && ! Types_IsArray(fpType)) || Types_IsRecord(fpType)) { + fprintf(file, " *"); + } + fprintf(file, ") "); + } + if ((isValueParam && Types_IsRecord(fpType)) || (isVarParam && ! Types_IsArray(fpType))) { + fprintf(file, "&"); + } + GenerateWithPrecedence(exp, file); + + /*additional type info parameters*/ + if (Types_IsArray(expType)) { + fprintf(file, ", "); + if (Trees_Symbol(exp) == TREES_DESIGNATOR) { + GenerateArrayLength(Trees_Left(exp), expType, file); + } else { + GenerateArrayLength(exp, expType, file); + } + } else if (Trees_Symbol(exp) == STRING) { + fprintf(file, ", %lu", (long unsigned) strlen(Trees_String(exp)) + 1); + } else if (isVarParam && Types_IsRecord(fpType)) { + fprintf(file, ", "); + GenerateTypeDescExp(exp, file, 0); + } + + if (Trees_Right(expList) != NULL) { + fprintf(file, ", "); + } + expList = Trees_Right(expList); + fpList = Trees_Right(fpList); + } + + fprintf(file, ")"); + if (resultType == NULL) { + fprintf(file, ";\n"); + } +} + + +static void GenerateAssert(Trees_Node node, FILE *file, int indent) +{ + Trees_Node exp, filename, line; + + exp = Trees_Left(node); + filename = Trees_Left(Trees_Right(node)); + line = Trees_Right(Trees_Right(node)); + + Indent(file, indent); + if (Trees_Symbol(exp) == FALSE) { + /*unconditional ASSERT(FALSE) replaces HALT(1)*/ + fprintf(file, "exit(1);\n"); + } else { + fprintf(file, "OBNC_ASSERT("); + Generate(exp, file, 0); + fprintf(file, ", "); + Generate(filename, file, 0); + fprintf(file, ", "); + Generate(line, file, 0); + fprintf(file, ");\n"); + } +} + + +static void GenerateIntegralCaseStatement(Trees_Node caseStmtNode, FILE *file, int indent) +{ + Trees_Node expNode, currCaseRepNode, currCaseNode, currCaseLabelListNode, currStmtSeqNode, currLabelRangeNode; + int rangeMin, rangeMax, label; + + expNode = Trees_Left(caseStmtNode); + + Indent(file, indent); + fprintf(file, "switch ("); + Generate(expNode, file, 0); + fprintf(file, ") {\n"); + currCaseRepNode = Trees_Right(caseStmtNode); + while (currCaseRepNode != NULL) { + currCaseNode = Trees_Left(currCaseRepNode); + currStmtSeqNode = Trees_Right(currCaseNode); + + /*generate case labels for current case*/ + currCaseLabelListNode = Trees_Left(currCaseNode); + do { + currLabelRangeNode = Trees_Left(currCaseLabelListNode); + if (Trees_Right(currLabelRangeNode) == NULL) { + /*generate single label*/ + Indent(file, indent + 1); + fprintf(file, "case "); + Generate(currLabelRangeNode, file, 0); + fprintf(file, ":\n"); + } else { + /*generate label range*/ + if (Trees_Symbol(Trees_Left(currLabelRangeNode)) == INTEGER) { + rangeMin = Trees_Integer(Trees_Left(currLabelRangeNode)); + rangeMax = Trees_Integer(Trees_Right(currLabelRangeNode)); + for (label = rangeMin; label <= rangeMax; label++) { + Indent(file, indent + 1); + fprintf(file, "case %d:\n", label); + } + } else { + rangeMin = Trees_Char(Trees_Left(currLabelRangeNode)); + rangeMax = Trees_Char(Trees_Right(currLabelRangeNode)); + for (label = rangeMin; label <= rangeMax; label++) { + Indent(file, indent + 1); + fprintf(file, "case "); + GenerateChar(label, file); + fprintf(file, ":\n"); + } + } + } + currCaseLabelListNode = Trees_Right(currCaseLabelListNode); + } while (currCaseLabelListNode != NULL); + + /*generate statement sequence for current case*/ + Generate(currStmtSeqNode, file, indent + 2); + Indent(file, indent + 2); + fprintf(file, "break;\n"); + + currCaseRepNode = Trees_Right(currCaseRepNode); + } + Indent(file, indent + 1); + fprintf(file, "default:\n"); + Indent(file, indent + 2); + fprintf(file, "OBNC_CT;\n"); + Indent(file, indent); + fprintf(file, "}\n"); +} + + +static void GenerateTypeCaseStatement(Trees_Node caseStmtNode, FILE *file, int indent) +{ + Trees_Node caseExp, caseList, caseNode, label, statementSeq; + int caseNumber; + + caseExp = Trees_Left(caseStmtNode); + assert(Trees_Symbol(caseExp) == TREES_DESIGNATOR); + caseVariable = Trees_Left(caseExp); + + caseList = Trees_Right(caseStmtNode); + caseNumber = 0; + while (caseList != NULL) { + caseNode = Trees_Left(caseList); + label = Trees_Left(Trees_Left(caseNode)); + statementSeq = Trees_Right(caseNode); + + if (caseNumber == 0) { + Indent(file, indent); + fprintf(file, "if ("); + } else { + fprintf(file, " else if ("); + } + GenerateISExpression(caseExp, label, file); + fprintf(file, ") {\n"); + caseLabelType = label; + Generate(statementSeq, file, indent + 1); + caseLabelType = NULL; + Indent(file, indent); + fprintf(file, "}"); + caseList = Trees_Right(caseList); + if (caseList == NULL) { + fprintf(file, "\n"); + } + caseNumber++; + } + + caseVariable = NULL; +} + + +static void GenerateCaseStatement(Trees_Node caseStmtNode, FILE *file, int indent) +{ + Trees_Node expNode, expType; + + expNode = Trees_Left(caseStmtNode); + expType = Trees_Type(expNode); + if (Types_IsInteger(expType) || Types_IsChar(expType)) { + GenerateIntegralCaseStatement(caseStmtNode, file, indent); + } else { + GenerateTypeCaseStatement(caseStmtNode, file, indent); + } +} + + +static void GenerateWhileStatement(Trees_Node whileNode, FILE *file, int indent) +{ + Trees_Node expNode, doNode, stmtSeqNode, elsifNode; + + expNode = Trees_Left(whileNode); + doNode = Trees_Right(whileNode); + stmtSeqNode = Trees_Left(doNode); + elsifNode = Trees_Right(doNode); + if (elsifNode == NULL) { + Indent(file, indent); + fprintf(file, "while ("); + Generate(expNode, file, 0); + fprintf(file, ") {\n"); + Generate(stmtSeqNode, file, indent + 1); + Indent(file, indent); + fprintf(file, "}\n"); + } else { + Indent(file, indent); + fprintf(file, "while (1) {\n"); + Indent(file, indent + 1); + fprintf(file, "if ("); + Generate(expNode, file, 0); + fprintf(file, ") {\n"); + Generate(stmtSeqNode, file, indent + 2); + Indent(file, indent + 1); + fprintf(file, "}\n"); + Generate(elsifNode, file, indent + 1); + Indent(file, indent + 1); + fprintf(file, "else {\n"); + Indent(file, indent + 2); + fprintf(file, "break;\n"); + Indent(file, indent + 1); + fprintf(file, "}\n"); + Indent(file, indent); + fprintf(file, "}\n"); + } +} + + +static void GenerateForStatement(Trees_Node forNode, FILE *file, int indent) +{ + Trees_Node initNode, controlVarNode, toNode, limit, byNode, statementSeq; + int inc; + + initNode = Trees_Left(forNode); + controlVarNode = Trees_Left(initNode); + toNode = Trees_Right(forNode); + limit = Trees_Left(toNode); + byNode = Trees_Right(toNode); + inc = Trees_Integer(Trees_Left(byNode)); + assert(inc != 0); + statementSeq = Trees_Right(byNode); + + Indent(file, indent); + fprintf(file, "for ("); + Generate(controlVarNode, file, 0); + fprintf(file, " = "); + Generate(Trees_Right(initNode), file, 0); + fprintf(file, "; "); + Generate(controlVarNode, file, 0); + if (inc > 0) { + fprintf(file, " <= "); + } else { + fprintf(file, " >= "); + } + Generate(limit, file, 0); + fprintf(file, "; "); + Generate(controlVarNode, file, 0); + fprintf(file, " += %d) {\n", inc); + Generate(statementSeq, file, indent + 1); + Indent(file, indent); + fprintf(file, "}\n"); +} + + +static void GenerateMemoryAllocation(Trees_Node var, FILE *file, int indent) +{ + Trees_Node type; + int hasPointer, hasProcedure; + const char *allocKind; + + assert(var != NULL); + assert(Trees_Symbol(var) == TREES_DESIGNATOR); + + type = Trees_Type(var); + SearchPointersAndProcedures(Types_PointerBaseType(type), &hasPointer, &hasProcedure); + allocKind = "OBNC_ATOMIC_NOINIT_ALLOC"; + if (hasPointer) { + allocKind = "OBNC_REGULAR_ALLOC"; + } else if (hasProcedure) { + allocKind = "OBNC_ATOMIC_ALLOC"; + } + if ((Trees_Symbol(type) == IDENT) || (Trees_Symbol(Types_PointerBaseType(type)) == IDENT)) { + Indent(file, indent); + fprintf(file, "OBNC_NEW("); + Generate(var, file, 0); + fprintf(file, ", &"); + Generate(TypeDescIdent(type), file, 0); + fprintf(file, "td, struct "); + Generate(TypeDescIdent(type), file, 0); + fprintf(file, "Heap, %s);\n", allocKind); + } else { + Indent(file, indent); + fprintf(file, "OBNC_NEW_ANON("); + Generate(var, file, 0); + fprintf(file, ", %s);\n", allocKind); + } +} + + +/*PROCEDURE DECLARATION GENERATORS*/ + +static void CopyText(FILE *source, long int pos, int count, FILE *target) +{ + long int oldPos; + int i, ch; + + assert(source != NULL); + assert(pos >= 0); + assert(count >= 0); + assert(target != NULL); + + oldPos = ftell(source); + if (oldPos >= 0) { + fseek(source, pos, SEEK_SET); + if (! ferror(source)) { + i = 0; + ch = fgetc(source); + while ((i < count) && (ch != EOF)) { + fputc(ch, target); + i++; + ch = fgetc(source); + } + } + fseek(source, oldPos, SEEK_SET); + } + + if (ferror(source) || ferror(target)) { + fprintf(stderr, "obnc-compile: file input/output failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } +} + + +static void ReadText(FILE *fp, long int pos, long int count, char result[], int resultLen) +{ + int i, ch; + + assert(count < resultLen); + + fseek(fp, pos, SEEK_SET); + if (! ferror(fp)) { + i = 0; + ch = fgetc(fp); + while ((ch != EOF) && (i < count)) { + result[i] = ch; + i++; + ch = fgetc(fp); + } + result[count] = '\0'; + fseek(fp, 0, SEEK_CUR); + } else { + perror(NULL); + exit(EXIT_FAILURE); + } +} + + +static void PushProcedureDeclaration(Trees_Node procIdent) +{ + struct ProcedureDeclNode *node; + int generatedLen, isFirstLocalProc, ch; + + NEW(node); + node->procIdent = procIdent; + node->localProcedures = Maps_New(); + node->runtimeInitVars = NULL; + if (Trees_Local(procIdent)) { + /*save unfinished procedure declaration*/ + generatedLen = ftell(cFile) - procedureDeclStart + 1; + NEW_ARRAY(node->partialDecl, generatedLen); + ReadText(cFile, procedureDeclStart, generatedLen - 1, node->partialDecl, generatedLen); + } else { + node->partialDecl = NULL; + } + node->next = procedureDeclStack; + + if (Trees_Local(procIdent)) { + assert(procedureDeclStack != NULL); + isFirstLocalProc = (procedureDeclStack->next == NULL) && Maps_IsEmpty(procedureDeclStack->localProcedures); + Maps_Put(Trees_Name(procIdent), NULL, &(procedureDeclStack->localProcedures)); + + /*set file position for writing local procedure*/ + fseek(cFile, procedureDeclStart, SEEK_SET); + if (isFirstLocalProc) { + /*keep function signature for global procedure*/ + do { + ch = fgetc(cFile); + } while ((ch != EOF) && (ch != ')')); + assert(ch == ')'); + fseek(cFile, 0, SEEK_CUR); + fprintf(cFile, ";\n"); + } + } + + procedureDeclStack = node; +} + + +static void PopProcedureDeclaration(void) +{ + assert(procedureDeclStack != NULL); + procedureDeclStart = ftell(cFile); + if (procedureDeclStack->partialDecl != NULL) { + fprintf(cFile, "%s", procedureDeclStack->partialDecl); + } + procedureDeclStack = procedureDeclStack->next; +} + + +static void GenerateFormalParameter(Trees_Node param, FILE *file, int indent) +{ + int kind; + Trees_Node type; + + kind = Trees_Kind(param); + type = Trees_Type(param); + if (kind == TREES_VALUE_PARAM_KIND) { + if (Types_IsArray(type) || Types_IsRecord(type)) { + fprintf(file, "const "); + } + if (Types_IsRecord(type) || (type == declaredTypeIdent)) { + fprintf(file, "struct "); + } + if (Types_IsArray(type)) { + Generate(Types_ElementType(type), file, 0); + fprintf(file, " "); + Generate(param, file, 0); + fprintf(file, "[], OBNC_LONGI int "); + Generate(param, file, 0); + fprintf(file, "len"); + } else { + Generate(type, file, 0); + fprintf(file, " "); + if (Types_IsRecord(type) || (type == declaredTypeIdent)) { + fprintf(file, "*"); + } + Generate(param, file, 0); + } + } else { + assert(kind == TREES_VAR_PARAM_KIND); + if (type == declaredTypeIdent) { + fprintf(file, "struct "); + } + if (Types_IsArray(type)) { + Generate(Types_ElementType(type), file, 0); + fprintf(file, " "); + Generate(param, file, 0); + fprintf(file, "[], OBNC_LONGI int "); + Generate(param, file, 0); + fprintf(file, "len"); + } else { + Generate(type, file, 0); + fprintf(file, " *"); + if (Types_IsPointer(type) && (type == declaredTypeIdent)) { + fprintf(file, "*"); + } + Generate(param, file, 0); + if (Types_IsRecord(type)) { + fprintf(file, ", const OBNC_Td *"); + Generate(param, file, 0); + fprintf(file, "td"); + } + } + } +} + + +static void GenerateFormalParameterList(Trees_Node paramList, FILE *file, int indent) +{ + Trees_Node param; + + assert(paramList != NULL); + + do { + param = Trees_Left(paramList); + GenerateFormalParameter(param, file, 0); + if (Trees_Right(paramList) != NULL) { + fprintf(file, ", "); + } + paramList = Trees_Right(paramList); + } while (paramList != NULL); +} + + +void Generate_ProcedureHeading(Trees_Node procIdent) +{ + Trees_Node procType, resultType, paramList; + + PushProcedureDeclaration(procIdent); + procedureDeclStart = ftell(cFile); + fprintf(cFile, "\n"); + + /*generate export status*/ + if (! Trees_Exported(procIdent)) { + fprintf(cFile, "static "); + } + + /*generate return type*/ + procType = Trees_Type(procIdent); + resultType = Types_ResultType(procType); + if (resultType != NULL) { + Generate(resultType, cFile, 0); + fprintf(cFile, " "); + } else { + fprintf(cFile, "void "); + } + + /*generate function identifier*/ + Generate(procIdent, cFile, 0); + + /*generate parameter list*/ + fprintf(cFile, "("); + paramList = Types_Parameters(procType); + if (paramList != NULL) { + GenerateFormalParameterList(paramList, cFile, 0); + } else { + fprintf(cFile, "void"); + } + fprintf(cFile, ")"); + + if (Trees_Exported(procIdent)) { + fprintf(hFile, "\n"); + GenerateObjectFileSymbolDefinitions(Trees_NewNode(TREES_NOSYM, procIdent, NULL), "", hFile, 0); + CopyText(cFile, procedureDeclStart + 1, ftell(cFile) - procedureDeclStart, hFile); + fprintf(hFile, ";\n"); + } + + fprintf(cFile, "\n{\n"); +} + + +void Generate_ProcedureStatements(Trees_Node stmtSeq) +{ + fprintf(cFile, "\n"); + Generate(stmtSeq, cFile, 1); +} + + +void Generate_ReturnClause(Trees_Node exp) +{ + Trees_Node resultType; + + assert(procedureDeclStack != NULL); + + resultType = Types_ResultType(Trees_Type(procedureDeclStack->procIdent)); + + Indent(cFile, 1); + fprintf(cFile, "return "); + if (CastNeeded(Trees_Type(exp), resultType)) { + fprintf(cFile, "("); + Generate(resultType, cFile, 0); + fprintf(cFile, ") "); + } + Generate(exp, cFile, 0); + fprintf(cFile, ";\n"); +} + + +void Generate_ProcedureEnd(Trees_Node procIdent) +{ + fprintf(cFile, "}\n\n"); + PopProcedureDeclaration(); +} + + +/*MODULE GENERATORS*/ + +static void GenerateInitCalls(int indent) +{ + Trees_Node current, moduleAndDirPath, module; + + current = importList; + while (current != NULL) { + moduleAndDirPath = Trees_Left(current); + module = Trees_Left(moduleAndDirPath); + Indent(cFile, indent); + fprintf(cFile, "%s_Init();\n", Trees_Name(module)); + current = Trees_Right(current); + } +} + + +static int Generated(const char filename[]) +{ + FILE *file; + const char *p; + int result, n, ch, i; + + assert(filename != NULL); + + result = 0; + file = Files_Old(filename, FILES_READ); + p = strrchr(headerComment, ' '); + if (p != NULL) { + n = p - headerComment; /*ignore version string*/ + i = 0; + ch = fgetc(file); + while ((ch != EOF) && (i < n) && (headerComment[i] == ch)) { + i++; + ch = fgetc(file); + } + result = (i == n) && (headerComment[i] == ch); + } + Files_Close(file); + return result; +} + + +static void DeleteTemporaryFiles(void) +{ + if (Files_Exists(tempCFilepath)) { + Files_Remove(tempCFilepath); + } + if (Files_Exists(tempHFilepath)) { + Files_Remove(tempHFilepath); + } +} + + +void Generate_Open(const char moduleName[], int isEntryPoint) +{ + const char *template; + + inputModuleName = moduleName; + isEntryPointModule = isEntryPoint; + + /*initialize header comment*/ + if (strcmp(CONFIG_VERSION, "") != 0) { + template = "/*GENERATED BY OBNC %s*/"; + NEW_ARRAY(headerComment, strlen(template) + strlen(CONFIG_VERSION) + 1); + sprintf(headerComment, "/*GENERATED BY OBNC %s*/", CONFIG_VERSION); + } else { + template = "/*GENERATED BY OBNC*/"; + NEW_ARRAY(headerComment, strlen(template) + 1); + strcpy(headerComment, template); + } + + /*make sure output directory exists*/ + if (! Files_Exists(".obnc")) { + Files_CreateDir(".obnc"); + } + + /*create temporary C file*/ + sprintf(tempCFilepath, ".obnc/%s.c.%d", inputModuleName, getpid()); + cFile = Files_New(tempCFilepath); + + /*create temporary header file*/ + sprintf(tempHFilepath, ".obnc/%s.h.%d", inputModuleName, getpid()); + hFile = Files_New(tempHFilepath); + + atexit(DeleteTemporaryFiles); +} + + +void Generate_ModuleHeading(void) +{ + fprintf(cFile, "%s\n\n", headerComment); + fprintf(cFile, "#include \n"); + if (! isEntryPointModule) { + fprintf(cFile, "#include \"%s.h\"\n", inputModuleName); + } + + fprintf(hFile, "%s\n\n", headerComment); + fprintf(hFile, "#ifndef %s_h\n", inputModuleName); + fprintf(hFile, "#define %s_h\n\n", inputModuleName); + fprintf(hFile, "#include \n"); +} + + +static int IsInstalledLibrary(const char *path) +{ + char *dotObncPath; + const char *prefix = Config_Prefix(); + + NEW_ARRAY(dotObncPath, strlen(path) + strlen("/.obnc") + 1); + sprintf(dotObncPath, "%s/.obnc", path); + return (strstr(path, prefix) == path) && (path[strlen(prefix)] == '/') && ! Files_Exists(dotObncPath); +} + + +static const char *RelativeInstalledLibraryPath(const char *path) +{ + const char *prefix = Config_Prefix(); + const char *libdir = Config_LibDir(); + const char *result, *tail; + + result = path; + if (strstr(path, prefix) == path) { + tail = result + strlen(prefix); + if (tail[0] == '/') { + tail++; + if (strstr(tail, libdir) == tail) { + tail += strlen(libdir); + if (tail[0] == '/') { + result = tail + 1; + } + } + } + } + return result; +} + + +void Generate_ImportList(Trees_Node list) +{ + static char hFileDir[PATH_MAX + 1]; + + Trees_Node moduleAndDirPath, module, dirPathNode; + const char *dirPath, *parentDirPrefix, *relativePath; + + importList = list; + + while (list != NULL) { + moduleAndDirPath = Trees_Left(list); + module = Trees_Left(moduleAndDirPath); + dirPathNode = Trees_Right(moduleAndDirPath); + dirPath = Trees_String(dirPathNode); + if (IsInstalledLibrary(dirPath)) { + relativePath = RelativeInstalledLibraryPath(dirPath); + fprintf(cFile, "#include <%s/%s.h>\n", relativePath, Trees_Name(module)); + fprintf(hFile, "#include <%s/%s.h>\n", relativePath, Trees_Name(module)); + } else if (strcmp(dirPath, ".") == 0) { + fprintf(cFile, "#include \"%s.h\"\n", Trees_Name(module)); + fprintf(hFile, "#include \"%s.h\"\n", Trees_Name(module)); + } else { + if ((dirPath[0] == '.') && (dirPath[1] == '/') && Files_Exists(".obnc")) { + parentDirPrefix = "."; + } else { + parentDirPrefix = ""; + } + sprintf(hFileDir, "%s/.obnc", dirPath); + if (! Files_Exists(hFileDir)) { + sprintf(hFileDir, "%s", dirPath); + } + fprintf(cFile, "#include \"%s%s/%s.h\"\n", parentDirPrefix, hFileDir, Trees_Name(module)); + fprintf(hFile, "#include \"%s%s/%s.h\"\n", parentDirPrefix, hFileDir, Trees_Name(module)); + } + list = Trees_Right(list); + } +} + + +void Generate_ModuleStatements(Trees_Node stmtSeq) +{ + static char initFuncName[FILENAME_MAX + 1]; + Trees_Node initFuncIdent; + + if (isEntryPointModule) { + fprintf(cFile, "\nint main(int argc, char *argv[])\n"); + fprintf(cFile, "{\n"); + Indent(cFile, 1); + fprintf(cFile, "OBNC_Initialize(argc, argv);\n"); + if (importList != NULL) { + GenerateInitCalls(1); + } + Generate(stmtSeq, cFile, 1); + Indent(cFile, 1); + fprintf(cFile, "return 0;\n"); + fprintf(cFile, "}\n"); + } else { + sprintf(initFuncName, "%s_Init", inputModuleName); + fprintf(cFile, "\nvoid %s(void)\n", initFuncName); + fprintf(cFile, "{\n"); + if ((importList != NULL) || (stmtSeq != NULL)) { + Indent(cFile, 1); + fprintf(cFile, "static int initialized = 0;\n\n"); + Indent(cFile, 1); + fprintf(cFile, "if (! initialized) {\n"); + GenerateInitCalls(2); + Generate(stmtSeq, cFile, 2); + Indent(cFile, 2); + fprintf(cFile, "initialized = 1;\n"); + Indent(cFile, 1); + fprintf(cFile, "}\n"); + } + fprintf(cFile, "}\n"); + + fprintf(hFile, "\n"); + initFuncIdent = Trees_NewIdent(initFuncName); + Trees_SetInternal(initFuncIdent); + GenerateObjectFileSymbolDefinitions(Trees_NewNode(TREES_NOSYM, initFuncIdent, NULL), "", hFile, 0); + fprintf(hFile, "void %s(void);\n", initFuncName); + } +} + + +void Generate_ModuleEnd(void) +{ + fprintf(hFile, "\n#endif\n"); +} + + +void Generate_Close(void) +{ + static char cFilepath[PATH_MAX]; + static char hFilepath[PATH_MAX]; + + /*close temporary files*/ + Files_Close(cFile); + Files_Close(hFile); + + /*move temporary C file to permanent C file*/ + sprintf(cFilepath, ".obnc/%s.c", inputModuleName); + if (! Files_Exists(cFilepath) || Generated(cFilepath)) { + Files_Move(tempCFilepath, cFilepath); + } else { + fprintf(stderr, "obnc-compile: error: C file generated by obnc-compile expected, will not overwrite: %s\n", cFilepath); + exit(EXIT_FAILURE); + } + + sprintf(hFilepath, ".obnc/%s.h", inputModuleName); + if (isEntryPointModule) { + /*delete generated header file*/ + if (Files_Exists(hFilepath)) { + if (Generated(hFilepath)) { + Files_Remove(hFilepath); + } else { + fprintf(stderr, "obnc-compile: error: header file generated by obnc-compile expected, will not delete: %s\n", hFilepath); + exit(EXIT_FAILURE); + } + } + } else { + /*move temporary header file to permanent header file*/ + if (! Files_Exists(hFilepath) || Generated(hFilepath)) { + Files_Move(tempHFilepath, hFilepath); + } else { + fprintf(stderr, "obnc-compile: error: header file generated by obnc-compile expected, will not overwrite: %s\n", hFilepath); + exit(EXIT_FAILURE); + } + } +} + + +/*GENERAL GENERATOR*/ + +static void Generate(Trees_Node node, FILE *file, int indent) +{ + int symbol; + + if (node != NULL) { + symbol = Trees_Symbol(node); + switch (symbol) { + case '#': + case '&': + case '*': + case '+': + case '-': + case '/': + case '<': + case '=': + case '>': + case '~': + case DIV: + case MOD: + case OR: + case GE: + case LE: + GenerateOperator(node, file); + break; + case BECOMES: + GenerateAssignment(node, file, indent); + break; + case CASE: + GenerateCaseStatement(node, file, indent); + break; + case ELSE: + Indent(file, indent); + fprintf(file, "else {\n"); + Generate(Trees_Left(node), file, indent + 1); + Indent(file, indent); + fprintf(file, "}\n"); + break; + case ELSIF: + Indent(file, indent); + fprintf(file, "else if ("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ") "); + Generate(Trees_Right(node), file, indent); + break; + case FALSE: + fprintf(file, "0"); + break; + case FOR: + GenerateForStatement(node, file, indent); + break; + case IDENT: + GenerateIdent(node, file, indent); + break; + case IF: + Indent(file, indent); + fprintf(file, "if ("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ") "); + Generate(Trees_Right(node), file, indent); + break; + case IN: + fprintf(file, "OBNC_IN("); + Generate(Trees_Left(node), file, indent); + fprintf(file, ", "); + Generate(Trees_Right(node), file, indent); + fprintf(file, ")"); + break; + case INTEGER: + fprintf(file, "%" OBNC_INT_MOD "d", Trees_Integer(node)); + break; + case IS: + GenerateISExpression(Trees_Left(node), Trees_Right(node), file); + break; + case NIL: + fprintf(file, "0"); + break; + case POINTER: + Generate(Trees_Left(node), file, indent); + fprintf(file, " *"); + break; + case REAL: + GenerateReal(Trees_Real(node), file); + break; + case REPEAT: + Indent(file, indent); + fprintf(file, "do {\n"); + Generate(Trees_Left(node), file, indent + 1); + Indent(file, indent); + fprintf(file, "} while (! ("); + Generate(Trees_Right(node), file, 0); + fprintf(file, "));\n"); + break; + case STRING: + GenerateString(Trees_String(node), file); + break; + case THEN: + fprintf(file, "{\n"); + Generate(Trees_Left(node), file, indent + 1); + Indent(file, indent); + fprintf(file, "}\n"); + Generate(Trees_Right(node), file, indent); + break; + case TREES_NOSYM: + Generate(Trees_Left(node), file, indent); + Generate(Trees_Right(node), file, indent); + break; + case TREES_ABS_PROC: + if (Types_IsInteger(Trees_Type(Trees_Left(node)))) { + fprintf(file, "OBNC_ABS_INT("); + } else { + fprintf(file, "OBNC_ABS_FLT("); + } + Generate(Trees_Left(node), file, 0); + fprintf(file, ")"); + break; + case TREES_ASR_PROC: + Indent(file, indent); + fprintf(file, "OBNC_ASR("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ")"); + break; + case TREES_ASSERT_PROC: + GenerateAssert(node, file, indent); + break; + case TREES_BOOLEAN_TYPE: + fprintf(file, "int"); + break; + case TREES_BYTE_TYPE: + fprintf(file, "unsigned char"); + break; + case TREES_CHAR_CONSTANT: + GenerateChar(Trees_Char(node), file); + break; + case TREES_CHAR_TYPE: + fprintf(file, "char"); + break; + case TREES_CHR_PROC: + fprintf(file, "OBNC_CHR("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ")"); + break; + case TREES_DEC_PROC: + if (Trees_Right(node) == NULL) { + Indent(file, indent); + fprintf(file, "OBNC_DEC("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ");\n"); + } else { + Indent(file, indent); + fprintf(file, "OBNC_DEC_N("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ");\n"); + } + break; + case TREES_DESIGNATOR: + GenerateDesignator(node, file); + break; + case TREES_EXCL_PROC: + Indent(file, indent); + fprintf(file, "OBNC_EXCL("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ");\n"); + break; + case TREES_EXP_LIST: + GenerateExpList(node, file); + break; + case TREES_FIELD_LIST_SEQUENCE: + Generate(Trees_Left(node), file, indent); + Generate(Trees_Right(node), file, indent); + break; + case TREES_FLOOR_PROC: + fprintf(file, "OBNC_FLOOR("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ")"); + break; + case TREES_FLT_PROC: + fprintf(file, "OBNC_FLT("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ")"); + break; + case TREES_INC_PROC: + if (Trees_Right(node) == NULL) { + Indent(file, indent); + fprintf(file, "OBNC_INC("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ");\n"); + } else { + Indent(file, indent); + fprintf(file, "OBNC_INC_N("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ");\n"); + } + break; + case TREES_INCL_PROC: + Indent(file, indent); + fprintf(file, "OBNC_INCL("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ");\n"); + break; + case TREES_INTEGER_TYPE: + fprintf(file, "OBNC_LONGI int"); + break; + case TREES_LEN_PROC: + GenerateArrayLength(Trees_Left(Trees_Left(node)), Trees_Type(Trees_Left(node)), file); + break; + case TREES_LSL_PROC: + fprintf(file, "OBNC_LSL("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ")"); + break; + case TREES_NEW_PROC: + GenerateMemoryAllocation(Trees_Left(node), file, indent); + break; + case TREES_ODD_PROC: + fprintf(file, "OBNC_ODD("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ")"); + break; + case TREES_ORD_PROC: + fprintf(file, "OBNC_ORD("); + if (Types_IsChar(Trees_Type(Trees_Left(node)))) { + fprintf(file, "(unsigned char) "); + } + GenerateWithPrecedence(Trees_Left(node), file); + fprintf(file, ")"); + break; + case TREES_PACK_PROC: + Indent(file, indent); + fprintf(file, "OBNC_PACK("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ");\n"); + break; + case TREES_PROCEDURE_CALL: + GenerateProcedureCall(node, file, indent); + break; + case TREES_RANGE_SET: + GenerateRangeSet(node, file); + break; + case TREES_REAL_TYPE: + fprintf(file, "OBNC_LONGR double"); + break; + case TREES_ROR_PROC: + fprintf(file, "OBNC_ROR("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ")"); + break; + case TREES_SET_CONSTANT: + fprintf(file, "0x%" OBNC_INT_MOD "Xu", Trees_Set(node)); + break; + case TREES_SET_TYPE: + fprintf(file, "OBNC_LONGI unsigned int"); + break; + case TREES_SINGLE_ELEMENT_SET: + GenerateSingleElementSet(node, file); + break; + case TREES_STATEMENT_SEQUENCE: + Generate(Trees_Left(node), file, indent); + Generate(Trees_Right(node), file, indent); + break; + case TREES_UNPK_PROC: + Indent(file, indent); + fprintf(file, "OBNC_UNPK("); + Generate(Trees_Left(node), file, 0); + fprintf(file, ", "); + Generate(Trees_Right(node), file, 0); + fprintf(file, ");\n"); + break; + case TRUE: + fprintf(file, "1"); + break; + case WHILE: + GenerateWhileStatement(node, file, indent); + break; + default: + fprintf(stderr, "obnc-compile: unknown symbol: %d\n", Trees_Symbol(node)); + assert(0); + } + } +} diff --git a/src/Generate.h b/src/Generate.h new file mode 100644 index 0000000..a12b5da --- /dev/null +++ b/src/Generate.h @@ -0,0 +1,49 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef GENERATE_H +#define GENERATE_H + +#include "Trees.h" + +void Generate_Open(const char moduleName[], int isEntryPoint); + +void Generate_ModuleHeading(void); + +void Generate_ImportList(Trees_Node importList); + +void Generate_ConstDeclaration(Trees_Node constIdent); + +void Generate_TypeDeclaration(Trees_Node typeIdent); + +void Generate_VariableDeclaration(Trees_Node varIdentList); + +void Generate_ProcedureHeading(Trees_Node procIdent); + +void Generate_ProcedureStatements(Trees_Node stmtSeq); + +void Generate_ReturnClause(Trees_Node exp); + +void Generate_ProcedureEnd(Trees_Node procIdent); + +void Generate_ModuleStatements(Trees_Node stmtSeq); + +void Generate_ModuleEnd(void); + +void Generate_Close(void); + +#endif diff --git a/src/Maps.c b/src/Maps.c new file mode 100644 index 0000000..5a7c2e5 --- /dev/null +++ b/src/Maps.c @@ -0,0 +1,108 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Maps.h" +#include "Util.h" +#include +#include +#include + +struct Maps_MapDesc { + char *key; + void *value; + Maps_Map next; +}; + +Maps_Map Maps_New(void) +{ + return NULL; +} + + +int Maps_IsEmpty(Maps_Map map) +{ + return map == NULL; +} + + +void Maps_Put(const char key[], void *value, Maps_Map *map) +{ + Maps_Map node; + + assert(key != NULL); + assert(map != NULL); + + NEW(node); + NEW_ARRAY(node->key, strlen(key) + 1); + strcpy(node->key, key); + node->value = value; + node->next = *map; + *map = node; +} + + +int Maps_HasKey(const char key[], Maps_Map map) +{ + assert(key != NULL); + + while ((map != NULL) && (strcmp(map->key, key) != 0)) { + map = map->next; + } + return map != NULL; +} + + +void *Maps_At(const char key[], Maps_Map map) +{ + void *result; + + assert(key != NULL); + + while ((map != NULL) && (strcmp(map->key, key) != 0)) { + map = map->next; + } + if (map != NULL) { + result = map->value; + } else { + result = NULL; + } + return result; +} + + +static Maps_Map DeletedDuplicates(Maps_Map map) +{ + Maps_Map result = Maps_New(); + + while (map != NULL) { + if (! Maps_HasKey(map->key, result)) { + Maps_Put(map->key, map->value, &result); + } + map = map->next; + } + return result; +} + + +void Maps_Apply(Maps_Applicator f, Maps_Map map, void *data) +{ + map = DeletedDuplicates(map); + while (map != NULL) { + f(map->key, map->value, data); + map = map->next; + } +} diff --git a/src/Maps.h b/src/Maps.h new file mode 100644 index 0000000..8a054c8 --- /dev/null +++ b/src/Maps.h @@ -0,0 +1,36 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef MAPS_H +#define MAPS_H + +typedef struct Maps_MapDesc *Maps_Map; +typedef void (*Maps_Applicator)(const char key[], void *value, void *data); + +Maps_Map Maps_New(void); + +int Maps_IsEmpty(Maps_Map map); + +void Maps_Put(const char key[], void *value, Maps_Map *map); + +int Maps_HasKey(const char key[], Maps_Map map); + +void *Maps_At(const char key[], Maps_Map map); + +void Maps_Apply(Maps_Applicator f, Maps_Map map, void *data); + +#endif diff --git a/src/MapsTest.c b/src/MapsTest.c new file mode 100644 index 0000000..eeb1fd2 --- /dev/null +++ b/src/MapsTest.c @@ -0,0 +1,88 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Maps.h" +#include "Util.h" +#include +#include + +typedef struct { int value; } *BoxedInteger; + +int count; + +static void Count(const char key[], void *value, void *data) +{ + count++; +} + + +static void Increment(const char key[], void *value, void *data) +{ + ((BoxedInteger) value)->value++; +} + + +int main(void) +{ + Maps_Map map; + BoxedInteger boxedInteger; + struct { const char *key; int value; } items[] = {{"foo", 1}, {"bar", 2}, {"baz", 3}}; + int i; + + Util_Init(); + map = Maps_New(); + assert(Maps_IsEmpty(map)); + + /*insert items*/ + for (i = 0; i < LEN(items); i++) { + NEW(boxedInteger); + boxedInteger->value = items[i].value; + Maps_Put(items[i].key, boxedInteger, &map); + } + assert(! Maps_IsEmpty(map)); + + /*retrieve keys*/ + for (i = 0; i < LEN(items); i++) { + assert(Maps_HasKey(items[i].key, map)); + } + + /*retrieve values*/ + for (i = 0; i < LEN(items); i++) { + boxedInteger = Maps_At(items[i].key, map); + assert(boxedInteger->value == items[i].value); + } + + /*reinsert element*/ + NEW(boxedInteger); + boxedInteger->value = 1; + Maps_Put("foo", boxedInteger, &map); + + /*count elements*/ + count = 0; + Maps_Apply(Count, map, NULL); + assert(count == 3); + + /*increment all values by one*/ + Maps_Apply(Increment, map, NULL); + for (i = 0; i < LEN(items); i++) { + assert(Maps_HasKey(items[i].key, map)); + boxedInteger = Maps_At(items[i].key, map); + assert(boxedInteger->value == items[i].value + 1); + } + + return 0; +} diff --git a/src/Oberon.h b/src/Oberon.h new file mode 100644 index 0000000..affbfbe --- /dev/null +++ b/src/Oberon.h @@ -0,0 +1,30 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef OBERON_H +#define OBERON_H + +/*parse modes*/ +#define OBERON_NORMAL_MODE 0 +#define OBERON_ENTRY_POINT_MODE 1 +#define OBERON_IMPORT_LIST_MODE 2 + +void Oberon_Parse(const char inputFile[], int mode); + +void Oberon_PrintContext(void); + +#endif diff --git a/src/Oberon.l b/src/Oberon.l new file mode 100644 index 0000000..b2aab86 --- /dev/null +++ b/src/Oberon.l @@ -0,0 +1,234 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +%option always-interactive + +%{ +#include "Oberon.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" +#include "Trees.h" /*needed by YYSTYPE in y.tab.h*/ +#include "y.tab.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static int KeywordToken(const char word[]); + +%} + +WORD [A-Za-z][A-Za-z0-9]* + +INTEGER [0-9]+|[0-9][0-9A-F]*H + +REAL [0-9]+"."[0-9]*(E[+-]?[0-9]+)? + +QUOTED-STRING \"[^"\n]*\" + +ORDINAL-STRING [0-9][0-9A-F]*X + +%% + +[ \t\r]+ + +\n { + yylineno++; +} + +":=" return BECOMES; + +".." return DOTDOT; + +"<=" return LE; + +">=" return GE; + +[][*+/&~.,;|({^:)}=#<>-] return yytext[0]; + +{WORD} { + int token; + char *lexeme; + + token = KeywordToken(yytext); + if (token < 0) { + token = IDENT; + NEW_ARRAY(lexeme, yyleng + 1); + strcpy(lexeme, yytext); + yylval.ident = lexeme; + } + return token; +} + +{INTEGER}/".."? { +#ifdef OBNC_CONFIG_USE_LONG_INT + const long int max = LONG_MAX; +#else + const int max = INT_MAX; +#endif + int base; + long lexeme; + + base = (yytext[yyleng - 1] == 'H')? 16: 10; + errno = 0; + lexeme = strtol(yytext, NULL, base); + if ((errno != 0) || (lexeme > max)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > %" OBNC_INT_MOD "d\n", strerror(ERANGE), yytext, max); + } + yylval.integer = (OBNC_LONGI int) lexeme; + return INTEGER; +} + +{REAL} { +#ifdef OBNC_CONFIG_USE_LONG_REAL + int n = sscanf(yytext, "%Lf", &yylval.real); + if (n != 1) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > %LG\n", strerror(ERANGE), yytext, LDBL_MAX); + } +#else + errno = 0; + yylval.real = strtod(yytext, NULL); + if (errno != 0) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > %G\n", strerror(ERANGE), yytext, DBL_MAX); + } +#endif + return REAL; +} + +{QUOTED-STRING} { + int lexemeLen; + char *lexeme; + + lexemeLen = yyleng - 1; + NEW_ARRAY(lexeme, lexemeLen); + memcpy(lexeme, yytext + 1, lexemeLen - 1); + lexeme[lexemeLen - 1] = '\0'; + yylval.string = lexeme; + return STRING; +} + +{ORDINAL-STRING} { + long ordinalNumber; + char *lexeme; + + if (strcmp(yytext, "0X") == 0) { + ordinalNumber = 0; + } else { + errno = 0; + ordinalNumber = strtol(yytext, NULL, 16); + if ((errno != 0) || (ordinalNumber > UCHAR_MAX)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > 0%XX\n", strerror(ERANGE), yytext, UCHAR_MAX); + } + } + NEW_ARRAY(lexeme, 2); + lexeme[0] = (char) ordinalNumber; + lexeme[1] = '\0'; + yylval.string = lexeme; + return STRING; +} + +"(*" { + int level, ch; + + level = 1; + do { + ch = input(); + switch (ch) { + case '(': + ch = input(); + if (ch == '*') { + level++; + } else { + unput(ch); + } + break; + case '*': + ch = input(); + if (ch == ')') { + level--; + } else { + unput(ch); + } + break; + case '\n': + yylineno++; + break; + } + } while ((level > 0) && (ch != EOF)); + + if (level > 0) { + Oberon_PrintContext(); + fprintf(stderr, "error: unterminated comment\n"); + exit(EXIT_FAILURE); + } +} + +. { + if (isprint(yytext[0])) { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected character: %c\n", yytext[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected character: %02X (hex)\n", yytext[0]); + } + return -1; +} + +%% + +static int Cmp(const void *word, const void *keywordPtr) +{ + return strcmp((char *) word, * (char **) keywordPtr); +} + + +static int KeywordToken(const char word[]) +{ + static const char *keywords[] = {"ARRAY", "BEGIN", "BY", "CASE", "CONST", "DIV", "DO", "ELSE", "ELSIF", "END", "FALSE", "FOR", "IF", "IMPORT", "IN", "IS", "MOD", "MODULE", "NIL", "OF", "OR", "POINTER", "PROCEDURE", "RECORD", "REPEAT", "RETURN", "THEN", "TO", "TRUE", "TYPE", "UNTIL", "VAR", "WHILE"}; + + static int keywordTokens[] = {ARRAY, BEGIN_, BY, CASE, CONST, DIV, DO, ELSE, ELSIF, END, FALSE, FOR, IF, IMPORT, IN, IS, MOD, MODULE, NIL, OF, OR, POINTER, PROCEDURE, RECORD, REPEAT, RETURN, THEN, TO, TRUE, TYPE, UNTIL, VAR, WHILE}; + + const char **keywordPtr; + int pos, token; + + keywordPtr = bsearch(word, keywords, LEN(keywords), sizeof keywords[0], Cmp); + if (keywordPtr != NULL) { + pos = keywordPtr - keywords; + assert(pos >= 0); + assert(pos < LEN(keywordTokens)); + token = keywordTokens[pos]; + } else { + token = -1; + } + return token; +} + + +int yywrap(void) +{ + const int done = 1; + + return done; +} diff --git a/src/Oberon.y b/src/Oberon.y new file mode 100644 index 0000000..39811c2 --- /dev/null +++ b/src/Oberon.y @@ -0,0 +1,4078 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +%{ +#include "Config.h" +#include "Files.h" +#include "Generate.h" +#include "lex.yy.h" +#include "Maps.h" +#include "Oberon.h" +#include "Path.h" +#include "Range.h" +#include "Table.h" +#include "Types.h" +#include "Trees.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/*assignment contexts*/ +#define ASSIGNMENT_CONTEXT 0 +#define PARAM_SUBST_CONTEXT 1 +#define PROC_RESULT_CONTEXT 2 + +static const char *inputFilename; +static int parseMode; +static char *inputModuleName; + +static Trees_Node unresolvedPointerTypes; +static Trees_Node currentTypeIdentdef; +static Trees_Node currentCaseExpression; +static Trees_Node caseExpressionType; +static Trees_Node currentlyDefinedCaseLabels; +static Trees_Node procedureDeclarationStack; + +void yyerror(const char format[], ...); + +static char *QualidentName(const char qualifier[], const char ident[]); + +/*constant predicate functions*/ + +static int IsBoolean(Trees_Node node); +static int IsChar(Trees_Node node); +static int IsInteger(Trees_Node node); +static int IsReal(Trees_Node node); +static int IsString(Trees_Node node); +static int IsSet(Trees_Node node); + +/*functions for type declaration productions*/ + +static Trees_Node ResolvedType(Trees_Node type, int isTypeDecl); +static void ResolvePointerTypes(Trees_Node baseType); +static const char *TypeString(Trees_Node type); + +/*functions for expression productions*/ + +static Trees_Node Designator(const char ident[], Trees_Node selectorList); +static int IsDesignator(Trees_Node exp); +static Trees_Node BaseIdent(Trees_Node designator); +static Trees_Node FirstSelector(Trees_Node designator); +static const char *DesignatorString(Trees_Node designator); +static void CheckIsValueExpression(Trees_Node exp); +static void SetSelectorTypes(Trees_Node identType, Trees_Node designator, int *parameterListFound); +static void RemoveActualParameters(Trees_Node *designator, Trees_Node *actualParameters); +static Trees_Node ExpressionConstValue(int relation, Trees_Node expA, Trees_Node expB); +static Trees_Node SimpleExpressionConstValue(int addOperator, Trees_Node expA, Trees_Node expB); +static Trees_Node TermConstValue(int mulOperator, Trees_Node expA, Trees_Node expB); +static const char *OperatorString(int operator); + +/*functions for statement productions*/ + +static int Writable(Trees_Node designator); +static void ValidateAssignment(Trees_Node expression, Trees_Node targetType, int context, int paramPos); +static void HandleProcedureCall(Trees_Node designator, Trees_Node actualParameters, int isFunctionCall, Trees_Node *ast); +static void CheckCaseLabelUniqueness(Trees_Node label); + +/*functions for module productions*/ + +static void ExportSymbolTable(const char symfilePath[]); +%} + +%union { + const char *ident; + OBNC_LONGI int integer; + OBNC_LONGR double real; + const char *string; + Trees_Node node; +} + +%token TOKEN_START + +/*reserved words (underscore avoids name clash)*/ +%token ARRAY BEGIN_ BY CASE CONST DIV DO ELSE ELSIF END FALSE FOR IF IMPORT IN IS MOD MODULE NIL OF OR POINTER PROCEDURE RECORD REPEAT RETURN THEN TO TRUE TYPE UNTIL VAR WHILE + +/*two-character operators and delimiters*/ +%token BECOMES DOTDOT GE LE + +/*tokens with semantic values*/ +%token IDENT +%token INTEGER +%token REAL +%token STRING + +%token TOKEN_END + +/*nonterminals with semantic values*/ +%type AddOperator +%type ArrayLengthOf +%type ArrayType +%type assignment +%type BaseType +%type BaseTypeOpt +%type BecomesIdentOpt +%type ByOpt +%type case +%type CaseExpression +%type CaseLabelList +%type CaseRep +%type CaseStatement +%type ConstExpression +%type designator +%type element +%type ElementRep +%type ElseIfDoOptRep +%type ElseIfThenOptRep +%type ElseOpt +%type ExpList +%type ExportMarkOpt +%type expression +%type factor +%type FieldList +%type FieldListSequence +%type FieldListSequenceOpt +%type ForInit +%type ForLimit +%type FormalParameters +%type FormalParametersOpt +%type FormalType +%type ForStatement +%type FPSection +%type FPSectionRep +%type FPSectionsOpt +%type guard +%type identdef +%type IdentRep +%type IfStatement +%type IdentList +%type import +%type ImportRep +%type length +%type label +%type LabelRange +%type LengthRep +%type ModuleStatements +%type MulOperator +%type number +%type OpenArrayOpt +%type ParameterKindOpt +%type PointerTo +%type PointerType +%type ProcedureCall +%type ProcedureHeading +%type ProcedureHeadingSansParam +%type ProcedureType +%type ProcedureTypeSansParam +%type qualident +%type RecordHeading +%type RecordType +%type relation +%type RepeatStatement +%type ResultTypeOpt +%type ReturnExpressionOpt +%type selector +%type SelectorOptRep +%type set +%type SignOpt +%type SimpleExpression +%type statement +%type StatementSequence +%type StatementSequenceOpt +%type StatementSequenceReversed +%type term +%type type +%type TypeIdentDef +%type TypeKeyword +%type TypeSectionOpt +%type WhileStatement + +%start module + +%% + +/*IDENTIFIER RULES*/ + +qualident: + IDENT + { + $$ = Trees_NewIdent($1); + } + | IDENT '.' IDENT + { + $$ = Trees_NewIdent(QualidentName($1, $3)); + } + ; + +identdef: + IDENT ExportMarkOpt + { + if (! Table_LocallyDeclared($1)) { + $$ = Trees_NewIdent($1); + if ($2) { + Trees_SetExported($$); + } + if (Table_ScopeLocal()) { + Trees_SetLocal($$); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: redeclaration of identifier: %s\n", $1); + YYABORT; + } + } + ; + +ExportMarkOpt: + '*' + { + $$ = 1; + } + | /*empty*/ + { + $$ = 0; + } + ; + + +/*NUMBER RULE*/ + +number: + INTEGER + { + $$ = Trees_NewInteger($1); + } + | REAL + { + $$ = Trees_NewReal($1); + } + ; + + +/*CONSTANT DECLARATION RULES*/ + +ConstDeclaration: + identdef '=' ConstExpression + { + if (! (Trees_Exported($1) && Trees_Local($1))) { + Trees_SetKind(TREES_CONSTANT_KIND, $1); + Trees_SetType(Trees_Type($3), $1); + Trees_SetValue($3, $1); + Table_Put($1); + Generate_ConstDeclaration($1); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: cannot export local constant: %s\n", Trees_Name($1)); + YYABORT; + } + } + ; + +ConstExpression: + expression + { + switch (Trees_Symbol($1)) { + case TRUE: + case FALSE: + case STRING: + case TREES_CHAR_CONSTANT: + case INTEGER: + case REAL: + case TREES_SET_CONSTANT: + case NIL: + $$ = $1; + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: constant expression expected\n"); + YYABORT; + } + } + ; + + +/*TYPE DECLARATION RULES*/ + +TypeDeclaration: + TypeIdentDef type + { + Trees_Node sourceType; + + sourceType = ResolvedType($2, 1); + if (sourceType != NULL) { + if (! (Trees_Exported($1) && Trees_Local($1))) { + Trees_SetType(sourceType, $1); + ResolvePointerTypes($1); + currentTypeIdentdef = NULL; + Generate_TypeDeclaration($1); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: cannot export local type: %s\n", Trees_Name($1)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", Trees_Name($2)); + YYABORT; + } + } + ; + +TypeIdentDef: + identdef '=' + { + Trees_SetKind(TREES_TYPE_KIND, $1); + currentTypeIdentdef = $1; + Table_Put($1); + $$ = $1; + } + ; + +type: + qualident + | ArrayType + | RecordType + | PointerType + | ProcedureType + ; + +ArrayType: + ArrayLengthOf type + { + Trees_Node reversedLengths, length; + + $$ = ResolvedType($2, 0); + if ($$ != NULL) { + reversedLengths = $1; + do { + length = Trees_Left(reversedLengths); + $$ = Types_NewArray(length, $$); + reversedLengths = Trees_Right(reversedLengths); + } while (reversedLengths != NULL); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", Trees_Name($2)); + exit(EXIT_FAILURE); + } + } + ; + +ArrayLengthOf: + ARRAY LengthRep OF + { + if ((currentTypeIdentdef != NULL) && (Trees_Type(currentTypeIdentdef) == NULL)) { + Trees_SetType(Trees_NewLeaf(ARRAY), currentTypeIdentdef); /*incomplete type*/ + } + $$ = $2; + } + ; + +LengthRep: + length + { + $$ = Trees_NewNode(TREES_NOSYM, $1, NULL); + } + | LengthRep ',' length + { + $$ = Trees_NewNode(TREES_NOSYM, $3, $1); + } + ; + +length: + ConstExpression + { + if (IsInteger($1)) { + if (Trees_Integer($1) > 0) { + $$ = $1; + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: positive length expected: %" OBNC_INT_MOD "d" OBNC_INT_MOD "\n", Trees_Integer($1)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer length expected\n"); + YYABORT; + } + } + ; + +RecordType: + RecordHeading FieldListSequenceOpt END + { + Table_CloseScope(); + $$ = Types_NewRecord(Types_RecordBaseType($1), $2); + } + ; + +RecordHeading: + RECORD BaseTypeOpt + { + $$ = Types_NewRecord($2, NULL); + if ((currentTypeIdentdef != NULL) && (Trees_Type(currentTypeIdentdef) == NULL)) { + Trees_SetType($$, currentTypeIdentdef); + } + Table_OpenScope(); + } + ; + +BaseTypeOpt: + '(' BaseType ')' + { + $$ = $2; + } + | /*empty*/ + { + $$ = NULL; + } + ; + +BaseType: + qualident + { + const char *name; + Trees_Node symbol; + + $$ = NULL; + name = Trees_Name($1); + symbol = Table_At(name); + if (symbol != NULL) { + if (Trees_Kind(symbol) == TREES_TYPE_KIND) { + switch (Trees_Symbol(Types_Structure(symbol))) { + case RECORD: + case POINTER: + $$ = symbol; + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: record or pointer base type expected: %s\n", name); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: type name expected: %s\n", name); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", name); + YYABORT; + } + } + ; + +FieldListSequenceOpt: + FieldListSequence + { + Trees_ReverseList(&$1); /*correct order*/ + $$ = $1; + } + | /*empty*/ + { + $$ = NULL; + } + ; + +FieldListSequence: + FieldList + { + $$ = Trees_NewNode(TREES_FIELD_LIST_SEQUENCE, $1, NULL); + } + | FieldListSequence ';' FieldList + { + $$ = Trees_NewNode(TREES_FIELD_LIST_SEQUENCE, $3, $1); + } + ; + +FieldList: + IdentList ':' type + { + Trees_Node type, identList, ident; + + $$ = NULL; + type = ResolvedType($3, 0); + if (type != NULL) { + if (! ((type == currentTypeIdentdef) && ! Types_IsPointer(type))) { + Trees_ReverseList(&$1); /*correct order*/ + identList = $1; + do { + ident = Trees_Left(identList); + if (! Table_LocallyDeclared(Trees_Name(ident))) { + Trees_SetKind(TREES_FIELD_KIND, ident); + Trees_SetType(type, ident); + Table_Put(ident); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: redeclaration of identifier with the same name: %s\n", Trees_Name(ident)); + YYABORT; + } + identList = Trees_Right(identList); + } while (identList != NULL); + + $$ = $1; + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: recursive field type must be a pointer: %s\n", Trees_Name($3)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared type: %s\n", Trees_Name($3)); + YYABORT; + } + } + ; + +IdentList: + identdef + { + $$ = Trees_NewNode(TREES_IDENT_LIST, $1, NULL); + } + | IdentList ',' identdef + { + Trees_Node reversedIdents; + + reversedIdents = Trees_NewNode(TREES_IDENT_LIST, $3, $1); + $$ = reversedIdents; + } + ; + +PointerType: + PointerTo type + { + const char *baseTypeName; + Trees_Node declaredBaseType; + + $$ = NULL; + if (Trees_Symbol($2) == IDENT) { + baseTypeName = Trees_Name($2); + declaredBaseType = Table_At(baseTypeName); + if (declaredBaseType != NULL) { + if (Types_IsRecord(declaredBaseType)) { + $$ = Types_NewPointer(declaredBaseType); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record expected as pointer base type: %s\n", baseTypeName); + YYABORT; + } + } else if (currentTypeIdentdef != NULL) { + Trees_SetKind(TREES_TYPE_KIND, $2); + Trees_SetType(Types_NewRecord(NULL, NULL), $2); + $$ = Types_NewPointer($2); + unresolvedPointerTypes = Trees_NewNode(TREES_NOSYM, $$, unresolvedPointerTypes); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared type: %s\n", baseTypeName); + YYABORT; + } + } else if(Trees_Symbol($2) == RECORD) { + $$ = Types_NewPointer($2); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record expected as pointer base type\n"); + YYABORT; + } + } + ; + +PointerTo: + POINTER TO + { + if ((currentTypeIdentdef != NULL) && (Trees_Type(currentTypeIdentdef) == NULL)) { + Trees_SetType(Types_NewPointer(NULL), currentTypeIdentdef); /*incomplete type*/ + } + } + ; + +ProcedureType: + ProcedureTypeSansParam FormalParametersOpt + { + Table_CloseScope(); + $$ = $2; + } + ; + +ProcedureTypeSansParam: + PROCEDURE + { + Table_OpenScope(); + $$ = NULL; + } + ; + +FormalParametersOpt: + FormalParameters + | /*empty*/ + { + $$ = Trees_NewLeaf(PROCEDURE); + } + ; + + +/*VARIABLE DECLARATION RULE*/ + +VariableDeclaration: + IdentList ':' type + { + Trees_Node type, identList, ident; + + type = ResolvedType($3, 0); + if (type != NULL) { + Trees_ReverseList(&$1); /*correct order*/ + identList = $1; + do { + ident = Trees_Left(identList); + if (! (Trees_Exported(ident) && Trees_Local(ident))) { + if (! Table_LocallyDeclared(Trees_Name(ident))) { + Trees_SetKind(TREES_VARIABLE_KIND, ident); + Trees_SetType(type, ident); + Table_Put(ident); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: redeclaration of identifier with the same name: %s\n", Trees_Name(ident)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: cannot export local variable: %s\n", Trees_Name(ident)); + YYABORT; + } + identList = Trees_Right(identList); + } while (identList != NULL); + + Generate_VariableDeclaration($1); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", Trees_Name($3)); + exit(EXIT_FAILURE); + } + } + ; + + +/*EXPRESSION RULES*/ + +expression: + SimpleExpression + | SimpleExpression relation SimpleExpression + { + Trees_Node expA, expB, typeA, typeB; + + expA = $1; + expB = $3; + typeA = Trees_Type($1); + typeB = Trees_Type($3); + + CheckIsValueExpression($1); + if ($2 == IS) { + if (! Types_IsRecord(typeA) + || (IsDesignator($1) && (Trees_Kind(BaseIdent($1)) == TREES_VAR_PARAM_KIND))) { + if (IsDesignator($3)) { + expB = BaseIdent($3); + typeB = BaseIdent($3); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: identifier expected as first operand of IS\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: variable parameter expected as first operand of IS\n"); + YYABORT; + } + } else { + CheckIsValueExpression($3); + } + + if (Types_ExpressionCompatible($2, typeA, typeB)) { + $$ = ExpressionConstValue($2, expA, expB); + if ($$ == NULL) { + if (IsString(expA) && Types_IsChar(typeB)) { + expA = Trees_NewChar(Trees_String(expA)[0]); + } else if (Types_IsChar(typeA) && IsString(expB)) { + expB = Trees_NewChar(Trees_String(expB)[0]); + } + $$ = Trees_NewNode($2, expA, expB); + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), $$); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in relation \"%s\": %s, %s\n", + OperatorString($2), TypeString(typeA), TypeString(typeB)); + YYABORT; + } + } + ; + +relation: + '=' + { + $$ = '='; + } + | '#' + { + $$ = '#'; + } + | '<' + { + $$ = '<'; + } + | LE + { + $$ = LE; + } + | '>' + { + $$ = '>'; + } + | GE + { + $$ = GE; + } + | IN + { + $$ = IN; + } + | IS + { + $$ = IS; + } + ; + +SimpleExpression: + SignOpt term + { + $$ = $2; + if ($1 >= 0) { + CheckIsValueExpression($2); + if (Types_ExpressionCompatible($1, Trees_Type($2), NULL)) { + $$ = SimpleExpressionConstValue($1, $2, NULL); + if ($$ == NULL) { + $$ = Trees_NewNode($1, $2, NULL); + if (Types_IsByte(Trees_Type($2))) { + Trees_SetType(Trees_NewLeaf(TREES_INTEGER_TYPE), $$); + } else { + Trees_SetType(Trees_Type($2), $$); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible type in unary operation \"%s\": %s\n", OperatorString($1), TypeString(Trees_Type($2))); + YYABORT; + } + } + } + | SimpleExpression AddOperator term + { + $$ = NULL; + + CheckIsValueExpression($1); + CheckIsValueExpression($3); + + if (Types_ExpressionCompatible($2, Trees_Type($1), Trees_Type($3))) { + $$ = SimpleExpressionConstValue($2, $1, $3); + if ($$ == NULL) { + $$ = Trees_NewNode($2, $1, $3); + if (Types_IsByte(Trees_Type($1)) || Types_IsByte(Trees_Type($3))) { + Trees_SetType(Trees_NewLeaf(TREES_INTEGER_TYPE), $$); + } else { + Trees_SetType(Trees_Type($1), $$); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in operation \"%s\": %s, %s\n", + OperatorString($2), TypeString(Trees_Type($1)), TypeString(Trees_Type($3))); + YYABORT; + } + assert($$ != NULL); + } + ; + +SignOpt: + '+' + { + $$ = '+'; + } + | '-' + { + $$ = '-'; + } + | /*empty*/ + { + $$ = -1; + } + ; + +AddOperator: + '+' + { + $$ = '+'; + } + | '-' + { + $$ = '-'; + } + | OR + { + $$ = OR; + } + ; + +term: + factor + | term MulOperator factor + { + $$ = NULL; + + CheckIsValueExpression($1); + CheckIsValueExpression($3); + + if (Types_ExpressionCompatible($2, Trees_Type($1), Trees_Type($3))) { + $$ = TermConstValue($2, $1, $3); + if ($$ == NULL) { + $$ = Trees_NewNode($2, $1, $3); + if (Types_IsByte(Trees_Type($1)) || Types_IsByte(Trees_Type($3))) { + Trees_SetType(Trees_NewLeaf(TREES_INTEGER_TYPE), $$); + } else { + Trees_SetType(Trees_Type($1), $$); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in operation \"%s\": %s, %s\n", + OperatorString($2), TypeString(Trees_Type($1)), TypeString(Trees_Type($3))); + YYABORT; + } + + assert($$ != NULL); + } + ; + +MulOperator: + '*' + { + $$ = '*'; + } + | '/' + { + $$ = '/'; + } + | DIV + { + $$ = DIV; + } + | MOD + { + $$ = MOD; + } + | '&' + { + $$ = '&'; + } + ; + +factor: + number + | STRING + { + $$ = Trees_NewString($1); + } + | NIL + { + $$ = Trees_NewLeaf(NIL); + Trees_SetType(Trees_NewLeaf(TREES_NIL_TYPE), $$); + } + | TRUE + { + $$ = Trees_NewLeaf(TRUE); + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), $$); + } + | FALSE + { + $$ = Trees_NewLeaf(FALSE); + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), $$); + } + | set + { + $$ = $1; + Trees_SetType(Trees_NewLeaf(TREES_SET_TYPE), $$); + } + /*NOTE: actual parameters are parsed by rule `designator'*/ + | designator + { + const int isFunctionCall = 1; + Trees_Node designator, actualParameters, ident; + + $$ = NULL; + if (Trees_Symbol($1) == TREES_PROCEDURE_CALL) { + designator = Trees_Left($1); + actualParameters = Trees_Right($1); + HandleProcedureCall(designator, actualParameters, isFunctionCall, &$$); + } else { + ident = Trees_Left($1); + if (Trees_Kind(ident) == TREES_CONSTANT_KIND) { + $$ = Trees_Value(ident); + } else { + $$ = $1; + } + } + assert($$ != NULL); + } + | '(' expression ')' + { + CheckIsValueExpression($2); + $$ = $2; + } + | '~' factor + { + $$ = NULL; + CheckIsValueExpression($2); + if (Types_ExpressionCompatible('~', Trees_Type($2), NULL)) { + switch (Trees_Symbol($2)) { + case TRUE: + $$ = Trees_NewLeaf(FALSE); + break; + case FALSE: + $$ = Trees_NewLeaf(TRUE); + break; + default: + $$ = Trees_NewNode('~', $2, NULL); + } + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), $$); + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible type in operation \"~\": %s\n", TypeString(Trees_Type($2))); + YYABORT; + } + assert($$ != NULL); + } + ; + +designator: + /*NOTE: qualified (imported) identifiers are parsed as field designators and detected semantically*/ + IDENT SelectorOptRep + { + Trees_Node designator, identType, actualParameters; + int parameterListFound; /*possibly empty*/ + + Trees_ReverseList(&$2); /*correct order*/ + designator = Designator($1, $2); + + identType = Trees_Type(BaseIdent(designator)); + SetSelectorTypes(identType, designator, ¶meterListFound); + if (parameterListFound) { + RemoveActualParameters(&designator, &actualParameters); + $$ = Trees_NewNode(TREES_PROCEDURE_CALL, designator, actualParameters); + } else { + $$ = designator; + } + } + ; + +SelectorOptRep: + SelectorOptRep selector + { + Trees_Node curr; + + if ((Trees_Symbol($2) == '[') && (Trees_Right($2) != NULL)) { /*multi-dimensional element selector*/ + /*attatch last element selector node to $1*/ + Trees_ReverseList(&$2); + $$ = $1; + curr = $2; + do { + $$ = Trees_NewNode('[', Trees_Left(curr), $$); + curr = Trees_Right(curr); + } while (curr != NULL); + Trees_ReverseList(&$$); + } else { + $$ = Trees_NewNode(Trees_Symbol($2), Trees_Left($2), $1); + } + } + | /*empty*/ + { + $$ = NULL; + } + ; + +selector: + '.' IDENT + { + Trees_Node field; + + field = Trees_NewIdent($2); + Trees_SetKind(TREES_FIELD_KIND, field); + $$ = Trees_NewNode('.', field, NULL); + } + | '[' ExpList ']' + { + Trees_Node curr, exp; + + /*create one selector node per index*/ + $$ = NULL; + curr = $2; /*NOTE: ExpList is reversed*/ + do { + exp = Trees_Left(curr); + if (Types_IsInteger(Trees_Type(exp))) { + $$ = Trees_NewNode('[', Trees_Left(curr), $$); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer array index expected\n"); + YYABORT; + } + curr = Trees_Right(curr); + } while (curr != NULL); + } + | '^' + { + $$ = Trees_NewNode('^', NULL, NULL); + } + /*NOTE: Procedure calls are parsed as designators and distinguished from type guards through semantic analysis.*/ + | '(' ExpList ')' /*type guard or actual parameters*/ + { + Trees_ReverseList(&$2); /*correct order*/ + $$ = Trees_NewNode('(', $2, NULL); + } + | '(' ')' /*actual parameters*/ + { + $$ = Trees_NewNode('(', NULL, NULL); + } + ; + +set: + '{' '}' + { + $$ = Trees_NewSet(0x0u); + } + | '{' ElementRep '}' + { + $$ = $2; + } + ; + +ElementRep: + element + | ElementRep ',' element + { + if ((Trees_Symbol($1) == TREES_SET_CONSTANT) + && (Trees_Symbol($3) == TREES_SET_CONSTANT)) { + $$ = Trees_NewSet(Trees_Set($1) | Trees_Set($3)); + } else { + $$ = Trees_NewNode('+', $1, $3); + } + } + ; + +element: + expression + { + int i; + Trees_Node type; + + CheckIsValueExpression($1); + $$ = NULL; + type = Trees_Type($1); + if (IsInteger($1)) { + i = Trees_Integer($1); + Range_CheckSetElement(i); + $$ = Trees_NewSet(1 << i); + } else if (Types_IsInteger(type)) { + $$ = Trees_NewNode(TREES_SINGLE_ELEMENT_SET, $1, NULL); + Trees_SetType(Trees_NewLeaf(TREES_SET_TYPE), $$); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: element must have integer type\n"); + YYABORT; + } + } + | expression DOTDOT expression + { + CheckIsValueExpression($1); + CheckIsValueExpression($3); + $$ = NULL; + if (IsInteger($1)) { + Range_CheckSetElement(Trees_Integer($1)); + } + if (IsInteger($3)) { + Range_CheckSetElement(Trees_Integer($3)); + } + if (IsInteger($1) && IsInteger($3)) { + $$ = Trees_NewSet(OBNC_RANGE(Trees_Integer($1), Trees_Integer($3))); + } else if (Types_IsInteger(Trees_Type($1)) && Types_IsInteger(Trees_Type($3))) { + $$ = Trees_NewNode(TREES_RANGE_SET, $1, $3); + Trees_SetType(Trees_NewLeaf(TREES_SET_TYPE), $$); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: element must have integer type\n"); + YYABORT; + } + } + ; + +ExpList: + expression + { + $$ = Trees_NewNode(TREES_EXP_LIST, $1, NULL); + Trees_SetType(Trees_Type($1), $$); + } + | ExpList ',' expression + { + Trees_Node reversedList; + + reversedList = Trees_NewNode(TREES_EXP_LIST, $3, $1); + $$ = reversedList; + Trees_SetType(Trees_Type($3), $$); + } + ; + + +/*STATEMENT RULES*/ + +statement: + assignment + | ProcedureCall + | IfStatement + | CaseStatement + | WhileStatement + | RepeatStatement + | ForStatement + | /*empty*/ + { + $$ = NULL; + } + ; + +assignment: + designator BECOMES expression + { + Trees_Node designator, ident, designatorType, exp; + + CheckIsValueExpression($3); + switch (Trees_Symbol($1)) { + case TREES_DESIGNATOR: + designator = $1; + exp = $3; + ident = BaseIdent($1); + designatorType = Trees_Type($1); + switch (Trees_Kind(ident)) { + case TREES_VARIABLE_KIND: + case TREES_VALUE_PARAM_KIND: + case TREES_VAR_PARAM_KIND: + if (Writable($1)) { + ValidateAssignment(exp, designatorType, ASSIGNMENT_CONTEXT, 0); + if (Types_IsChar(designatorType) && IsString(exp)) { + exp = Trees_NewChar(Trees_String(exp)[0]); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: assignment to read-only variable\n"); + YYABORT; + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: assignment to non-variable\n"); + YYABORT; + } + $$ = Trees_NewNode(BECOMES, designator, exp); + break; + case TREES_PROCEDURE_CALL: + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected procedure call in assignment target\n"); + YYABORT; + break; + default: + assert(0); + } + } + ; + +ProcedureCall: + /*NOTE: actual parameters are parsed by rule `designator'*/ + designator + { + const int isFunctionCall = 0; + Trees_Node designator, actualParameters; + + if (Trees_Symbol($1) == TREES_PROCEDURE_CALL) { + designator = Trees_Left($1); + actualParameters = Trees_Right($1); + } else { + designator = $1; + actualParameters = NULL; + } + HandleProcedureCall(designator, actualParameters, isFunctionCall, &$$); + assert($$ != NULL); + } + ; + +StatementSequence: + StatementSequenceReversed + { + Trees_ReverseList(&$1); /*correct order*/ + $$ = $1; + } + ; + +StatementSequenceReversed: + statement + { + if ($1 == NULL) { + $$ = NULL; + } else { + $$ = Trees_NewNode(TREES_STATEMENT_SEQUENCE, $1, NULL); + } + } + | StatementSequenceReversed ';' statement + { + if ($3 != NULL) { + $$ = Trees_NewNode(TREES_STATEMENT_SEQUENCE, $3, $1); + } else { + $$ = $1; + } + } + ; + +IfStatement: + IF guard THEN StatementSequence ElseIfThenOptRep ElseOpt END + { + Trees_Node currElsif, currExp, currThen, currStmt; + + if ($5 == NULL) { + $$ = Trees_NewNode(IF, $2, Trees_NewNode(THEN, $4, $6)); + } else { + /*correct order of elsif nodes*/ + $$ = $6; + currElsif = $5; + do { + currExp = Trees_Left(currElsif); + currThen = Trees_Right(currElsif); + currStmt = Trees_Left(currThen); + $$ = Trees_NewNode(ELSIF, currExp, Trees_NewNode(THEN, currStmt, $$)); + currElsif = Trees_Right(currThen); + } while (currElsif != NULL); + $$ = Trees_NewNode(IF, $2, Trees_NewNode(THEN, $4, $$)); + } + } + ; + +guard: + expression + { + CheckIsValueExpression($1); + if (Types_IsBoolean(Trees_Type($1))) { + $$ = $1; + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: boolean expression expected\n"); + YYABORT; + } + } + ; + +ElseIfThenOptRep: + ElseIfThenOptRep ELSIF guard THEN StatementSequence + { + $$ = Trees_NewNode(ELSIF, $3, Trees_NewNode(THEN, $5, $1)); + } + | /*empty*/ + { + $$ = NULL; + } + ; + +ElseOpt: + ELSE StatementSequence + { + $$ = Trees_NewNode(ELSE, $2, NULL); + } + | /*empty*/ + { + $$ = NULL; + } + ; + +CaseStatement: + CASE CaseExpression OF CaseRep END + { + Trees_Node expType, caseVariable; + + if ($4 != NULL) { + Trees_ReverseList(&$4); /*correct order*/ + } + expType = Trees_Type($2); + if (Types_IsRecord(expType) || Types_IsPointer(expType)) { + /*reset original type*/ + caseVariable = Trees_Left($2); + Trees_SetType(caseExpressionType, caseVariable); + } + $$ = Trees_NewNode(CASE, $2, $4); + } + ; + +CaseExpression: + expression + { + Trees_Node typeStruct, caseVariable; + + CheckIsValueExpression($1); + typeStruct = Types_Structure(Trees_Type($1)); + switch (Trees_Symbol(typeStruct)) { + case RECORD: + /*fall through*/ + case POINTER: + if (IsDesignator($1) && (FirstSelector($1) == NULL)) { + caseVariable = BaseIdent($1); + if (! Types_IsRecord(typeStruct) || (Trees_Kind(caseVariable) == TREES_VAR_PARAM_KIND)) { + $$ = $1; + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record CASE expression must be a variable parameter\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: non-integral CASE expression must be a variable\n"); + YYABORT; + } + /*fall through*/ + case TREES_INTEGER_TYPE: + /*fall through*/ + case TREES_CHAR_TYPE: + currentCaseExpression = $1; + caseExpressionType = Trees_Type($1); + currentlyDefinedCaseLabels = NULL; + $$ = $1; + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: invalid type of CASE expression\n"); + YYABORT; + } + } + ; + +CaseRep: + case + { + if ($1 != NULL) { + $$ = Trees_NewNode(TREES_CASE_REP, $1, NULL); + } else { + $$ = NULL; + } + } + | CaseRep '|' case + { + if ($3 != NULL) { + if ($1 != NULL) { + $$ = Trees_NewNode(TREES_CASE_REP, $3, $1); + } else { + $$ = Trees_NewNode(TREES_CASE_REP, $3, NULL); + } + } else { + $$ = NULL; + } + } + ; + +case: + CaseLabelList ':' StatementSequence + { + Trees_ReverseList(&$1); /*correct order*/ + $$ = Trees_NewNode(TREES_CASE, $1, $3); + } + | /*empty*/ + { + $$ = NULL; + } + ; + +CaseLabelList: + LabelRange + { + $$ = Trees_NewNode(TREES_CASE_LABEL_LIST, $1, NULL); + } + | CaseLabelList ',' LabelRange + { + switch (Trees_Symbol($3)) { + case INTEGER: + case TREES_CHAR_CONSTANT: + case DOTDOT: + $$ = Trees_NewNode(TREES_CASE_LABEL_LIST, $3, $1); + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected list of type name labels\n"); + YYABORT; + } + } + ; + +LabelRange: + label + { + $$ = $1; + CheckCaseLabelUniqueness($1); + currentlyDefinedCaseLabels = Trees_NewNode(TREES_NOSYM, $1, currentlyDefinedCaseLabels); + } + | label DOTDOT label + { + const int rangeLenMax = 255; + int leftSym, rightSym; + int rangeMin, rangeMax; + + leftSym = Trees_Symbol($1); + rightSym = Trees_Symbol($3); + if (leftSym == rightSym) { + switch (leftSym) { + case INTEGER: + rangeMin = Trees_Integer($1); + rangeMax = Trees_Integer($3); + if (rangeMin <= rangeMax) { + if (rangeMax - rangeMin > rangeLenMax) { + Oberon_PrintContext(); + fprintf(stderr, "maximum range length of %d exceeded\n", rangeLenMax); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: left integer must be less than right integer in case range\n"); + YYABORT; + } + break; + case TREES_CHAR_CONSTANT: + if (Trees_Char($1) >= Trees_Char($3)) { + Oberon_PrintContext(); + fprintf(stderr, "error: left string must be less than right string in case range\n"); + YYABORT; + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: case label ranges must contain integers or single-character strings\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: case labels in a range must have the same type\n"); + YYABORT; + } + $$ = Trees_NewNode(DOTDOT, $1, $3); + CheckCaseLabelUniqueness($$); + currentlyDefinedCaseLabels = Trees_NewNode(TREES_NOSYM, $$, currentlyDefinedCaseLabels); + } + ; + +label: + INTEGER + { + if (Types_IsInteger(Trees_Type(currentCaseExpression))) { + $$ = Trees_NewInteger($1); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected integer label\n"); + YYABORT; + } + } + | STRING + { + if (Types_IsChar(Trees_Type(currentCaseExpression))) { + if (strlen($1) <= 1) { + $$ = Trees_NewChar($1[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "single-character string expected: %s\n", $1); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected string label: %s\n", $1); + YYABORT; + } + } + | qualident + { + Trees_Node caseExpTypeStruct, constValue, caseVariable; + + $$ = Table_At(Trees_Name($1)); + if ($$ != NULL) { + caseExpTypeStruct = Types_Structure(Trees_Type(currentCaseExpression)); + switch (Trees_Symbol(caseExpTypeStruct)) { + case TREES_INTEGER_TYPE: + constValue = Trees_Value($$); + if (Trees_Symbol(constValue) == INTEGER) { + if (Trees_Integer(constValue) >= 0) { + $$ = constValue; + } else { + Oberon_PrintContext(); + fprintf(stderr, "non-negative case label expected: %" OBNC_INT_MOD "d\n", Trees_Integer(constValue)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer label expected\n"); + YYABORT; + } + break; + case TREES_CHAR_TYPE: + constValue = Trees_Value($$); + if (Trees_Symbol(constValue) == STRING) { + if (Types_StringLength(Trees_Type(constValue)) <= 1) { + $$ = Trees_NewChar(Trees_String(constValue)[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "single-character string expected: %s\n", Trees_String(constValue)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: character label expected\n"); + YYABORT; + } + break; + case RECORD: + if (Types_IsRecord($$)) { + if (Types_Extends(Trees_Type(currentCaseExpression), $$)) { + caseVariable = Trees_Left(currentCaseExpression); + Trees_SetType($$, caseVariable); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: extended type expected in label\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record type label expected\n"); + YYABORT; + } + break; + case POINTER: + if (Types_IsPointer($$)) { + if (Types_Extends(Trees_Type(currentCaseExpression), $$)) { + caseVariable = Trees_Left(currentCaseExpression); + Trees_SetType($$, caseVariable); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: extended type expected in label\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: pointer type label expected\n"); + YYABORT; + } + break; + default: + assert(0); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name($1)); + YYABORT; + } + } + ; + +WhileStatement: + WHILE guard DO StatementSequence ElseIfDoOptRep END + { + $$ = Trees_NewNode(WHILE, $2, Trees_NewNode(DO, $4, $5)); + } + ; + +ElseIfDoOptRep: + ElseIfDoOptRep ELSIF guard DO StatementSequence + { + $$ = Trees_NewNode(ELSIF, $3, Trees_NewNode(THEN, $5, $1)); + } + | /*empty*/ + { + $$ = NULL; + } + ; + +RepeatStatement: + REPEAT StatementSequence UNTIL expression + { + CheckIsValueExpression($4); + $$ = NULL; + if (Types_IsBoolean(Trees_Type($4))) { + $$ = Trees_NewNode(REPEAT, $2, $4); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: boolean expression expected\n"); + YYABORT; + } + } + ; + + +ForStatement: + FOR ForInit TO ForLimit ByOpt DO StatementSequence END + { + Trees_Node byExp; + + if ($5 != NULL) { + byExp = $5; + } else { + byExp = Trees_NewInteger(1); + } + $$ = Trees_NewNode(FOR, + $2, + Trees_NewNode(TO, + $4, + Trees_NewNode(BY, byExp, $7))); + } + ; + +ForInit: + IDENT BECOMES expression + { + Trees_Node ctrlVar, ctrlVarType; + + CheckIsValueExpression($3); + ctrlVar = Table_At($1); + if (ctrlVar != NULL) { + ctrlVarType = Trees_Type(ctrlVar); + if (Types_IsInteger(ctrlVarType)) { + if (Types_IsInteger(Trees_Type($3))) { + $$ = Trees_NewNode(BECOMES, ctrlVar, $3); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer expression expected as initial value\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer control variable expected: %s\n", $1); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared control variable: %s\n", $1); + YYABORT; + } + } + +ForLimit: + expression + { + CheckIsValueExpression($1); + if (! Types_IsInteger(Trees_Type($1))) { + Oberon_PrintContext(); + fprintf(stderr, "error: integer expression expected as upper limit\n"); + YYABORT; + } + } + ; + +ByOpt: + BY ConstExpression + { + if (IsInteger($2)) { + if (Trees_Integer($2) != 0) { + $$ = $2; + } else { + Oberon_PrintContext(); + fprintf(stderr, "warning: steps by zero leads to infinite loop\n"); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer increment expected\n"); + YYABORT; + } + } + | /*empty*/ + { + $$ = NULL; + } + ; + + +/*PROCEDURE DECLARATION RULES*/ + +ProcedureDeclaration: + ProcedureHeading ';' DeclarationSequence StatementSequenceOpt ReturnExpressionOpt END IDENT + { + Trees_Node procIdent, procType, resultType, procStatements, returnExp; + const char *procName; + + procIdent = $1; + procName = Trees_Name(procIdent); + procType = Trees_Type($1); + resultType = Types_ResultType(procType); + procStatements = $4; + returnExp = $5; + + if (strcmp(procName, $7) == 0) { + if (resultType == NULL) { + if (returnExp != NULL) { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected return expression\n"); + YYABORT; + } + } else { + if (returnExp != NULL) { + CheckIsValueExpression(returnExp); + ValidateAssignment(returnExp, resultType, PROC_RESULT_CONTEXT, 0); + if ((Trees_Symbol(returnExp) == STRING) && Types_IsChar(resultType)) { + returnExp = Trees_NewChar(Trees_String(returnExp)[0]); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: return expression expected\n"); + YYABORT; + } + } + if (procStatements != NULL) { + Generate_ProcedureStatements(procStatements); + } + if (returnExp != NULL) { + Generate_ReturnClause(returnExp); + } + if (procedureDeclarationStack != NULL) { + procedureDeclarationStack = Trees_Right(procedureDeclarationStack); + } + Generate_ProcedureEnd(procIdent); + Table_CloseScope(); + } else { + Oberon_PrintContext(); + fprintf(stderr, "expected procedure name: %s\n", procName); + YYABORT; + } + } + ; + +ProcedureHeading: + ProcedureHeadingSansParam FormalParametersOpt + { + Trees_Node paramList, param; + + $$ = NULL; + Table_CloseScope(); + Trees_SetType($2, $1); + Table_OpenScope(); + + /*reenter parameters in the symbol table*/ + paramList = Types_Parameters($2); + while (paramList != NULL) { + param = Trees_Left(paramList); + Table_Put(param); + paramList = Trees_Right(paramList); + } + + procedureDeclarationStack = Trees_NewNode(TREES_NOSYM, $1, procedureDeclarationStack); + Generate_ProcedureHeading($1); + $$ = $1; + } + ; + +ProcedureHeadingSansParam: + PROCEDURE identdef + { + if (! (Trees_Exported($2) && Trees_Local($2))) { + Trees_SetKind(TREES_PROCEDURE_KIND, $2); + Table_Put($2); + Table_OpenScope(); + } else { + Oberon_PrintContext(); + fprintf(stderr, "cannot export local procedure: %s\n", Trees_Name($2)); + YYABORT; + } + $$ = $2; + } + ; + +StatementSequenceOpt: + BEGIN_ StatementSequence + { + $$ = $2; + } + | /*empty*/ + { + $$ = NULL; + } + ; + +ReturnExpressionOpt: + RETURN expression + { + $$ = $2; + } + | /*empty*/ + { + $$ = NULL; + } + ; + +DeclarationSequence: + ConstSectionOpt TypeSectionOpt VariableSectionOpt ProcedureDeclarationOptRep + ; + +ConstSectionOpt: + CONST ConstDeclarationOptRep + | /*empty*/ + ; + +ConstDeclarationOptRep: + ConstDeclarationOptRep ConstDeclaration ';' + | /*empty*/ + ; + +TypeSectionOpt: + TypeKeyword TypeDeclarationOptRep + { + Trees_Node unresolvedPointerType, undeclaredBaseType; + + if (unresolvedPointerTypes != NULL) { + unresolvedPointerType = Trees_Left(unresolvedPointerTypes); + undeclaredBaseType = Types_PointerBaseType(unresolvedPointerType); + Oberon_PrintContext(); + fprintf(stderr, "undeclared pointer base type: %s\n", Trees_Name(undeclaredBaseType)); + YYABORT; + } + } + | /*empty*/ + { + $$ = NULL; + } + ; + +TypeKeyword: + TYPE + { + unresolvedPointerTypes = NULL; + } + ; + +TypeDeclarationOptRep: + TypeDeclarationOptRep TypeDeclaration ';' + | /*empty*/ + ; + +VariableSectionOpt: + VAR VariableDeclarationOptRep + | /*empty*/ + ; + +VariableDeclarationOptRep: + VariableDeclarationOptRep VariableDeclaration ';' + | /*empty*/ + ; + +ProcedureDeclarationOptRep: + ProcedureDeclarationOptRep ProcedureDeclaration ';' + | /*empty*/ + ; + +FormalParameters: + '(' FPSectionsOpt ')' ResultTypeOpt + { + $$ = Types_NewProcedure($2, $4); + } + ; + +FPSectionsOpt: + FPSectionRep + { + Trees_ReverseList(&$1); /*correct order*/ + $$ = $1; + } + | /*empty*/ + { + $$ = NULL; + } + ; + +FPSectionRep: + FPSection + { + $$ = $1; + Trees_ReverseList(&$$); + } + | FPSectionRep ';' FPSection + { + Trees_Node curr; + + /*make one list of the two lists*/ + $$ = $1; + curr = $3; + do { + $$ = Trees_NewNode(TREES_IDENT_LIST, Trees_Left(curr), $$); + curr = Trees_Right(curr); + } while (curr != NULL); + /*$$ in reversed order*/ + } + ; + +ResultTypeOpt: + ':' qualident + { + $$ = ResolvedType($2, 0); + if ($$ != NULL) { + if (Trees_Symbol($$) == IDENT) { + if (Trees_Kind($$) != TREES_TYPE_KIND) { + Oberon_PrintContext(); + fprintf(stderr, "type name expected as result type: %s\n", Trees_Name($2)); + YYABORT; + } + if (! Types_Scalar($$)) { + Oberon_PrintContext(); + fprintf(stderr, "scalar result type expected: %s\n", Trees_Name($2)); + YYABORT; + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name($2)); + YYABORT; + } + } + | /*empty*/ + { + $$ = NULL; + } + ; + +FPSection: + ParameterKindOpt IdentRep ':' FormalType + { + Trees_Node curr, ident; + + Trees_ReverseList(&$2); /*correct order*/ + curr = $2; + do { + ident = Trees_Left(curr); + Trees_SetKind($1, ident); + Trees_SetType($4, ident); + Trees_SetLocal(ident); + if (! Table_LocallyDeclared(Trees_Name(ident))) { + Table_Put(ident); + } else { + Oberon_PrintContext(); + fprintf(stderr, "redeclaration of identifier with the same name: %s\n", Trees_Name(ident)); + YYABORT; + } + curr = Trees_Right(curr); + } while (curr != NULL); + + $$ = $2; + } + ; + +ParameterKindOpt: + VAR + { + $$ = TREES_VAR_PARAM_KIND; + } + | /*empty*/ + { + $$ = TREES_VALUE_PARAM_KIND; + } + ; + +IdentRep: + IDENT + { + $$ = Trees_NewNode(TREES_IDENT_LIST, Trees_NewIdent($1), NULL); + } + | IdentRep ',' IDENT + { + $$ = Trees_NewNode(TREES_IDENT_LIST, Trees_NewIdent($3), $1); + } + ; + +FormalType: + OpenArrayOpt qualident + { + $$ = ResolvedType($2, 0); + if ($$ != NULL) { + if ($1) { + $$ = Types_NewArray(NULL, $$); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name($2)); + exit(EXIT_FAILURE); + } + } + ; + +OpenArrayOpt: + ARRAY OF + { + $$ = 1; + } + | /*empty*/ + { + $$ = 0; + } + ; + + +/*MODULE RULES*/ + +module: + ModuleHeading ';' ImportListOpt DeclarationSequence ModuleStatements END IDENT '.' + { + static char symfilePath[PATH_MAX + 1]; + + if (strcmp($7, inputModuleName) == 0) { + Generate_ModuleEnd(); + Generate_Close(); + + sprintf(symfilePath, ".obnc/%s.sym", inputModuleName); + if (parseMode == OBERON_ENTRY_POINT_MODE) { + if (Files_Exists(symfilePath)) { + Files_Remove(symfilePath); + } + } else { + ExportSymbolTable(symfilePath); + } + YYACCEPT; + } else { + Oberon_PrintContext(); + fprintf(stderr, "expected identifier %s\n", inputModuleName); + YYABORT; + } + } + ; + + +ModuleHeading: + MODULE IDENT + { + if (strcmp($2, inputModuleName) == 0) { + if (parseMode != OBERON_IMPORT_LIST_MODE) { + Generate_ModuleHeading(); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "module name does not match filename: %s\n", $2); + YYABORT; + } + } + ; + +ImportListOpt: + ImportList + { + if (parseMode == OBERON_IMPORT_LIST_MODE) { + YYACCEPT; + } + } + | /*empty*/ + { + if (parseMode == OBERON_IMPORT_LIST_MODE) { + YYACCEPT; + } + } + ; + +ImportList: + IMPORT ImportRep ';' + { + static char impfilePath[PATH_MAX + 1]; + Trees_Node moduleAndDirPath, module, p; + FILE *impFile; + const char *name; + + Trees_ReverseList(&$2); /*correct order*/ + if (parseMode == OBERON_IMPORT_LIST_MODE) { + while ($2 != NULL) { + module = Trees_Left($2); + puts(Trees_Name(module)); + $2 = Trees_Right($2); + } + } else { + sprintf(impfilePath, ".obnc/%s.imp", inputModuleName); + if (parseMode == OBERON_ENTRY_POINT_MODE) { + if (Files_Exists(impfilePath)) { + Files_Remove(impfilePath); + } + } else { + impFile = Files_New(impfilePath); + p = $2; + do { + moduleAndDirPath = Trees_Left(p); + module = Trees_Left(moduleAndDirPath); + name = Trees_UnaliasedName(module); + fprintf(impFile, "%s\n", name); + p = Trees_Right(p); + } while (p != NULL); + Files_Close(impFile); + } + Generate_ImportList($2); + } + } + ; + +ImportRep: + import + { + $$ = Trees_NewNode(TREES_NOSYM, $1, NULL); + } + | ImportRep ',' import + { + $$ = Trees_NewNode(TREES_NOSYM, $3, $1); + } + ; + +import: + IDENT BecomesIdentOpt + { + static Maps_Map importedModules = NULL; + static char symbolFileDir[PATH_MAX + 1]; + static char symbolFileName[PATH_MAX + 1]; + static char moduleDirPath[PATH_MAX + 1]; + const char *module, *qualifier; + Trees_Node qualifierSym, moduleIdent; + + if (importedModules == NULL) { + importedModules = Maps_New(); + } + if ($2 != NULL) { + module = $2; + qualifier = $1; + } else { + module = $1; + qualifier = $1; + } + $$ = NULL; + if (strcmp(module, inputModuleName) != 0) { + if (! Maps_HasKey(module, importedModules)) { + Maps_Put(module, NULL, &importedModules); + qualifierSym = Table_At(qualifier); + if (qualifierSym == NULL) { + qualifierSym = Trees_NewIdent(qualifier); + if ($2 != NULL) { + Trees_SetUnaliasedName(module, qualifierSym); + } + Trees_SetKind(TREES_QUALIFIER_KIND, qualifierSym); + Table_Put(qualifierSym); + + if (parseMode == OBERON_IMPORT_LIST_MODE) { + $$ = Trees_NewIdent(module); + } else { + Path_Get(module, moduleDirPath, LEN(moduleDirPath)); + if (moduleDirPath[0] != '\0') { + /*import identifiers into the symbol table*/ + sprintf(symbolFileDir, "%s/.obnc", moduleDirPath); + if (! Files_Exists(symbolFileDir)) { + sprintf(symbolFileDir, "%s", moduleDirPath); + } + sprintf(symbolFileName, "%s/%s.sym", symbolFileDir, module); + if (Files_Exists(symbolFileName)) { + Table_Import(symbolFileName, module, qualifier); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: symbol file not found for module %s: %s\n", module, symbolFileName); + YYABORT; + } + + moduleIdent = Trees_NewIdent(module); + Trees_SetKind(TREES_QUALIFIER_KIND, moduleIdent); + $$ = Trees_NewNode(TREES_NOSYM, moduleIdent, Trees_NewString(moduleDirPath)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: imported module not found: %s\n", module); + YYABORT; + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: qualifier already used: %s\n", qualifier); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: module already imported: %s\n", module); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: a module cannot import itself\n"); + YYABORT; + } + } + ; + +BecomesIdentOpt: + BECOMES IDENT + { + $$ = $2; + } + | /*empty*/ + { + $$ = NULL; + } + ; + +ModuleStatements: + StatementSequenceOpt + { + Generate_ModuleStatements($1); + } + ; + +%% + +static char *ModuleName(const char filename[]) +{ + char *result; + int startPos, endPos, resultLen, i; + + endPos = strlen(filename); + do { + endPos--; + } while ((endPos >= 0) && (filename[endPos] != '.')); + assert(endPos > 0); + assert(filename[endPos] == '.'); + + startPos = endPos - 1; + while ((startPos >= 0) && isalnum(filename[startPos])) { + startPos--; + } + if ((startPos < 0) || ! isalnum(filename[startPos])) { + startPos++; + } + + resultLen = endPos - startPos; + NEW_ARRAY(result, resultLen); + for (i = 0; i < resultLen; i++) { + result[i] = filename[startPos + i]; + } + return result; +} + + +void Oberon_Parse(const char inputFile[], int mode) +{ + int error; + + Table_Init(); + inputFilename = inputFile; + parseMode = mode; + inputModuleName = ModuleName(inputFile); + + yyin = fopen(inputFile, "r"); + if (yyin != NULL) { + if (mode != OBERON_IMPORT_LIST_MODE) { + Generate_Open(inputModuleName, mode == OBERON_ENTRY_POINT_MODE); + } + error = yyparse(); + if (error) { + fprintf(stderr, "compilation failed\n"); + exit(1); + } + } else { + fprintf(stderr, "obnc-compile: error: cannot open file: %s: %s\n", inputFile, strerror(errno)); + exit(1); + } +} + + +/*NOTE: prefer Oberon_PrintContext and fprintf over yyerror since a C compiler cannot type-check the format string of yyerror*/ + +void Oberon_PrintContext(void) +{ + fprintf(stderr, "obnc-compile: %s:%d: ", inputFilename, yylineno); +} + + +void yyerror(const char format[], ...) +{ + va_list ap; + + Oberon_PrintContext(); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fputc('\n', stderr); +} + + +/*accessor functions*/ + +static char *QualidentName(const char qualifier[], const char ident[]) +{ + int resultLen; + char *result; + + resultLen = strlen(qualifier) + strlen(".") + strlen(ident) + 1; + NEW_ARRAY(result, resultLen); + sprintf(result, "%s.%s", qualifier, ident); + return result; +} + + +/*constant predicate functions*/ + +static int IsBoolean(Trees_Node node) +{ + return (Trees_Symbol(node) == TRUE) || (Trees_Symbol(node) == FALSE); +} + + +static int IsChar(Trees_Node node) +{ + return Trees_Symbol(node) == TREES_CHAR_CONSTANT; +} + + +static int IsInteger(Trees_Node node) +{ + return Trees_Symbol(node) == INTEGER; +} + + +static int IsReal(Trees_Node node) +{ + return Trees_Symbol(node) == REAL; +} + + +static int IsString(Trees_Node node) +{ + return Trees_Symbol(node) == STRING; +} + + +static int IsSet(Trees_Node node) +{ + return Trees_Symbol(node) == TREES_SET_CONSTANT; +} + + +/*functions for type declaration productions*/ + +static Trees_Node ResolvedType(Trees_Node type, int isTypeDecl) +{ + Trees_Node result, identDef, typeStruct; + const char *name; + + result = NULL; + if (Trees_Symbol(type) == IDENT) { + name = Trees_Name(type); + identDef = Table_At(name); + if (identDef != NULL) { + if (Trees_Kind(identDef) == TREES_TYPE_KIND) { + typeStruct = Types_Structure(identDef); + if (typeStruct != NULL) { + if (Types_Basic(Trees_Type(identDef)) && ! isTypeDecl) { + result = Trees_Type(identDef); + } else { + result = identDef; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unresolved type: %s\n", name); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "type expected: %s\n", name); + exit(EXIT_FAILURE); + } + } + } else { + result = type; + } + return result; +} + + +static void ResolvePointerTypes(Trees_Node baseType) +{ + const char *baseTypeName; + Trees_Node prev, curr, currPointerType, currBaseType; + + assert(Trees_Symbol(baseType) == IDENT); + baseTypeName = Trees_Name(baseType); + + prev = NULL; + curr = unresolvedPointerTypes; + while (curr != NULL) { + currPointerType = Trees_Left(curr); + currBaseType = Types_PointerBaseType(currPointerType); + if (strcmp(Trees_Name(currBaseType), baseTypeName) == 0) { + if (Types_IsRecord(baseType)) { + /*update pointer base type*/ + Types_SetPointerBaseType(baseType, currPointerType); + /*delete current node*/ + if (curr == unresolvedPointerTypes) { + unresolvedPointerTypes = Trees_Right(curr); + } else { + Trees_SetRight(Trees_Right(curr), prev); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "record type expected in declaration of pointer base type: %s\n", baseTypeName); + exit(EXIT_FAILURE); + } + } + prev = curr; + curr = Trees_Right(curr); + } +} + + +static const char *TypeString(Trees_Node type) +{ + const char *result = ""; + + assert(Types_IsType(type)); + + switch (Trees_Symbol(type)) { + case IDENT: + result = Trees_Name(type); + break; + case TREES_STRING_TYPE: + switch (Types_StringLength(type)) { + case 0: + result = "empty string"; + break; + case 1: + result = "single-char string"; + break; + default: + result = "multi-char string"; + } + break; + case TREES_BOOLEAN_TYPE: + result = "BOOLEAN"; + break; + case TREES_CHAR_TYPE: + result = "CHAR"; + break; + case TREES_INTEGER_TYPE: + result = "INTEGER"; + break; + case TREES_REAL_TYPE: + result = "REAL"; + break; + case TREES_BYTE_TYPE: + result = "BYTE"; + break; + case TREES_SET_TYPE: + result = "SET"; + break; + case ARRAY: + if (Types_IsOpenArray(type)) { + result = "open ARRAY"; + } else { + result = "anon ARRAY"; + } + break; + case RECORD: + result = "anon RECORD"; + break; + case POINTER: + result = "anon POINTER"; + break; + case PROCEDURE: + result = "anon PROCEDURE"; + break; + default: + assert(0); + } + return result; +} + + +/*functions for expression productions*/ + +static int IsDesignator(Trees_Node exp) +{ + return Trees_Symbol(exp) == TREES_DESIGNATOR; +} + + +static void CheckIsValueExpression(Trees_Node exp) +{ + Trees_Node ident; + + if (Trees_Symbol(exp) == TREES_DESIGNATOR) { + ident = Trees_Left(exp); + switch (Trees_Kind(ident)) { + case TREES_CONSTANT_KIND: + case TREES_FIELD_KIND: + case TREES_VARIABLE_KIND: + case TREES_PROCEDURE_KIND: + case TREES_VALUE_PARAM_KIND: + case TREES_VAR_PARAM_KIND: + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "value expected: %s\n", Trees_Name(ident)); + exit(EXIT_FAILURE); + } + } +} + + +static Trees_Node Designator(const char identName[], Trees_Node selectorList) +{ + Trees_Node identSym, qualidentSym, designator, qualidentSelectorList; + const char *qualidentName; + + /*set qualident name, symbol and selector list*/ + qualidentSym = NULL; + qualidentSelectorList = NULL; + if ((procedureDeclarationStack != NULL) + && (strcmp(identName, Trees_Name(Trees_Left(procedureDeclarationStack))) == 0)) { + qualidentSym = Trees_Left(procedureDeclarationStack); + qualidentSelectorList = selectorList; + } else { + identSym = Table_At(identName); + if (identSym != NULL) { + if (Trees_Kind(identSym) == TREES_QUALIFIER_KIND) { + if ((selectorList != NULL) && (Trees_Symbol(selectorList) == '.')) { + qualidentName = QualidentName(identName, Trees_Name(Trees_Left(selectorList))); + qualidentSym = Table_At(qualidentName); + qualidentSelectorList = Trees_Right(selectorList); + if (qualidentSym == NULL) { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", qualidentName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "'.' expected after qualifier: %s\n", identName); + exit(EXIT_FAILURE); + } + } else { + qualidentSym = identSym; + qualidentSelectorList = selectorList; + } + + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", identName); + exit(EXIT_FAILURE); + } + } + assert(qualidentSym != NULL); + + designator = Trees_NewNode(TREES_DESIGNATOR, qualidentSym, qualidentSelectorList); + + return designator; +} + + +static Trees_Node BaseIdent(Trees_Node designator) +{ + assert(Trees_Symbol(designator) == TREES_DESIGNATOR); + + return Trees_Left(designator); +} + + +static Trees_Node FirstSelector(Trees_Node designator) +{ + assert(Trees_Symbol(designator) == TREES_DESIGNATOR); + + return Trees_Right(designator); +} + + +static void SetSelectorTypes(Trees_Node identType, Trees_Node designator, int *parameterListFound) +{ + Trees_Node currType, currTypeStruct, currSelector, prevSelector, indexExp, lengthNode, pointerNode, expList, extendedType, symbol, varField, typeField, fieldBaseType; + int length, index; + const char *fieldName; + + currType = identType; + currSelector = FirstSelector(designator); + prevSelector = designator; + *parameterListFound = 0; + while ((currSelector != NULL) && ! *parameterListFound) { + currTypeStruct = Types_Structure(currType); + switch (Trees_Symbol(currSelector)) { + case '[': + if ((currTypeStruct != NULL) && (Trees_Symbol(currTypeStruct) == ARRAY)) { + indexExp = Trees_Left(currSelector); + lengthNode = Types_ArrayLength(currTypeStruct); + if ((lengthNode != NULL) && (Trees_Symbol(indexExp) == INTEGER)) { + length = Trees_Integer(lengthNode); + index = Trees_Integer(indexExp); + if ((index < 0) || (index >= length)) { + Oberon_PrintContext(); + fprintf(stderr, "invalid array index: %d not between 0 and %d\n", index, length - 1); + exit(EXIT_FAILURE); + } + } + Trees_SetType(currType, currSelector); + currType = Types_ElementType(currTypeStruct); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: array variable expected in element selector\n"); + exit(EXIT_FAILURE); + } + break; + case '.': + if (currType != NULL) { + switch (Trees_Symbol(currTypeStruct)) { + case POINTER: + pointerNode = Trees_NewNode('^', NULL, currSelector); + Trees_SetType(currType, pointerNode); + Trees_SetRight(pointerNode, prevSelector); + currType = Types_PointerBaseType(currTypeStruct); + /*fall through*/ + case RECORD: + Trees_SetType(currType, currSelector); + varField = Trees_Left(currSelector); + fieldName = Trees_Name(varField); + Types_GetFieldIdent(fieldName, currType, Trees_Imported(BaseIdent(designator)), &typeField, &fieldBaseType); + if (typeField != NULL) { + if (Trees_Exported(typeField)) { + Trees_SetExported(varField); + } + currType = Trees_Type(typeField); + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared field: %s\n", fieldName); + exit(EXIT_FAILURE); + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: record variable expected in field selector\n"); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record variable expected in field selector\n"); + exit(EXIT_FAILURE); + } + break; + case '^': + if ((currType != NULL) && (Trees_Symbol(currTypeStruct) == POINTER)) { + Trees_SetType(currType, currSelector); + currType = Types_PointerBaseType(currTypeStruct); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: pointer variable expected in pointer dereference\n"); + exit(EXIT_FAILURE); + } + break; + case '(': + if (Types_IsProcedure(currTypeStruct)) { + *parameterListFound = 1; + } else if (Types_IsRecord(currTypeStruct) || Types_IsPointer(currTypeStruct)) { + /*type guard*/ + expList = Trees_Left(currSelector); + if (Trees_Right(expList) == NULL) { + if ((Trees_Symbol(Trees_Left(expList)) == TREES_DESIGNATOR) + && (Trees_Right(Trees_Left(expList)) == NULL)) { + extendedType = Trees_Left(Trees_Left(expList)); + symbol = Table_At(Trees_Name(extendedType)); + if (symbol != NULL) { + if (Trees_Kind(symbol) == TREES_TYPE_KIND) { + if ((Types_IsRecord(currType) && Types_IsRecord(Trees_Type(symbol))) + || (Types_IsPointer(currType) && Types_IsPointer(Trees_Type(symbol)))) { + if (Types_Extends(currType, Trees_Type(symbol))) { + Trees_SetLeft(extendedType, currSelector); + Trees_SetType(extendedType, currSelector); + currType = extendedType; + } else { + Oberon_PrintContext(); + fprintf(stderr, "extended type expected: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } else { + if (Types_IsRecord(currType)) { + Oberon_PrintContext(); + fprintf(stderr, "record type expected in type guard: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "pointer type expected in type guard: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "type name expected: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "identifier expected in type guard\n"); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected comma in type guard\n"); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected parenthesis in designator which is not a record, pointer or procedure\n"); + exit(EXIT_FAILURE); + } + break; + default: + assert(0); + } + prevSelector = currSelector; + currSelector = Trees_Right(currSelector); + } + + if (currSelector == NULL) { + Trees_SetType(currType, designator); + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected selector after procedure call\n"); + exit(EXIT_FAILURE); + } +} + + +static void RemoveActualParameters(Trees_Node *designator, Trees_Node *actualParameters) +{ + Trees_Node currSelector; + + currSelector = FirstSelector(*designator); + assert(currSelector != NULL); + if (Trees_Right(currSelector) == NULL) { + *actualParameters = Trees_Left(currSelector); + Trees_SetRight(NULL, *designator); + } else { + while (Trees_Right(Trees_Right(currSelector)) != NULL) { + currSelector = Trees_Right(currSelector); + } + *actualParameters = Trees_Left(Trees_Right(currSelector)); + Trees_SetRight(NULL, currSelector); + } +} + + +static Trees_Node ExpressionConstValue(int relation, Trees_Node expA, Trees_Node expB) +{ + Trees_Node result = NULL; + + switch (relation) { + case '=': + switch (Trees_Symbol(expA)) { + case TRUE: + case FALSE: + if (IsBoolean(expB)) { + result = Trees_NewLeaf((Trees_Symbol(expA) == Trees_Symbol(expB))? TRUE: FALSE); + } + break; + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) == Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) == Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) == Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) == Trees_Real(expB))? TRUE: FALSE); + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewLeaf((Trees_Set(expA) == Trees_Set(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] == Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) == 0)? TRUE: FALSE); + } + break; + } + break; + case '#': + switch (Trees_Symbol(expA)) { + case TRUE: + case FALSE: + if (IsBoolean(expB)) { + result = Trees_NewLeaf((Trees_Symbol(expA) != Trees_Symbol(expB))? TRUE: FALSE); + } + break; + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) != Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) != Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) != Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) != Trees_Real(expB))? TRUE: FALSE); + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewLeaf((Trees_Set(expA) != Trees_Set(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] != Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) != 0)? TRUE: FALSE); + } + break; + } + break; + case '<': + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) < Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) < Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) < Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) < Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] < Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) < 0)? TRUE: FALSE); + } + break; + } + break; + case LE: + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) <= Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) <= Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) <= Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) <= Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] <= Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) <= 0)? TRUE: FALSE); + } + break; + } + break; + case '>': + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) > Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) > Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) > Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) > Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] > Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) > 0)? TRUE: FALSE); + } + break; + } + break; + case GE: + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) >= Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) >= Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) >= Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) >= Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] >= Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) >= 0)? TRUE: FALSE); + } + break; + } + break; + case IN: + if (IsInteger(expA)) { + Range_CheckSetElement(Trees_Integer(expA)); + if (IsSet(expB)) { + result = Trees_NewLeaf(OBNC_IN(Trees_Integer(expA), Trees_Set(expB))? TRUE: FALSE); + } + } + break; + } + if (result != NULL) { + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), result); + } + + return result; +} + + +static Trees_Node SimpleExpressionConstValue(int operator, Trees_Node expA, Trees_Node expB) +{ + Trees_Node result = NULL; + + switch (operator) { + case '+': + switch (Trees_Symbol(expA)) { + case INTEGER: + if (expB == NULL) { + result = expA; + } else if (IsInteger(expB)) { + Range_CheckIntSum(Trees_Integer(expA), Trees_Integer(expB)); + result = Trees_NewInteger(Trees_Integer(expA) + Trees_Integer(expB)); + } + break; + case REAL: + if (expB == NULL) { + result = expA; + } else if (IsReal(expB)) { + Range_CheckRealSum(Trees_Real(expA), Trees_Real(expB)); + result = Trees_NewReal(Trees_Real(expA) + Trees_Real(expB)); + } + break; + case TREES_SET_CONSTANT: + if (expB == NULL) { + result = expA; + } else if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) | Trees_Set(expB)); + } + break; + } + break; + case '-': + switch (Trees_Symbol(expA)) { + case INTEGER: + if (expB == NULL) { + Range_CheckIntDiff(0, Trees_Integer(expA)); + result = Trees_NewInteger(-Trees_Integer(expA)); + } else if (IsInteger(expB)) { + Range_CheckIntDiff(Trees_Integer(expA), Trees_Integer(expB)); + result = Trees_NewInteger(Trees_Integer(expA) - Trees_Integer(expB)); + } + break; + case REAL: + if (expB == NULL) { + Range_CheckRealDiff(0.0, Trees_Real(expA)); + result = Trees_NewReal(-Trees_Real(expA)); + } else if (IsReal(expB)) { + Range_CheckRealDiff(Trees_Real(expA), Trees_Real(expB)); + result = Trees_NewReal(Trees_Real(expA) - Trees_Real(expB)); + } + break; + case TREES_SET_CONSTANT: + if (expB == NULL) { + result = Trees_NewSet(~Trees_Set(expA)); + } else if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) & ~Trees_Set(expB)); + } + break; + } + break; + case OR: + if (IsBoolean(expA) && IsBoolean(expB)) { + result = (Trees_Symbol(expA) == TRUE)? expA: expB; + } + break; + } + + return result; +} + + +static Trees_Node TermConstValue(int operator, Trees_Node expA, Trees_Node expB) +{ + Trees_Node result = NULL; + + switch (operator) { + case '*': + switch (Trees_Symbol(expA)) { + case INTEGER: + if (IsInteger(expB)) { + Range_CheckIntProd(Trees_Integer(expA), Trees_Integer(expB)); + result = Trees_NewInteger(Trees_Integer(expA) * Trees_Integer(expB)); + } + break; + case REAL: + if (IsReal(expB)) { + Range_CheckRealProd(Trees_Real(expA), Trees_Real(expB)); + result = Trees_NewReal(Trees_Real(expA) * Trees_Real(expB)); + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) & Trees_Set(expB)); + } + break; + } + break; + case '/': + switch (Trees_Symbol(expA)) { + case REAL: + if (IsReal(expA) && IsReal(expB)) { + if (Trees_Real(expB) != 0) { + result = Trees_NewReal(Trees_Real(expA) / Trees_Real(expB)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "warning: division by zero\n"); + } + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) ^ Trees_Set(expB)); + } + break; + } + break; + case DIV: + if (IsInteger(expA) && IsInteger(expB)) { + if (Trees_Integer(expB) > 0) { + result = Trees_NewInteger(OBNC_DIV(Trees_Integer(expA), Trees_Integer(expB))); + } else { + Oberon_PrintContext(); + fprintf(stderr, "positive divisor expected in DIV expression: %" OBNC_INT_MOD "d\n", Trees_Integer(expB)); + exit(EXIT_FAILURE); + } + } + break; + case MOD: + if (IsInteger(expA) && IsInteger(expB)) { + if (Trees_Integer(expB) > 0) { + result = Trees_NewInteger(OBNC_MOD(Trees_Integer(expA), Trees_Integer(expB))); + } else { + Oberon_PrintContext(); + fprintf(stderr, "positive divisor expected in MOD expression: %" OBNC_INT_MOD "d\n", Trees_Integer(expB)); + exit(EXIT_FAILURE); + } + } + break; + case '&': + if (IsBoolean(expA) && IsBoolean(expB)) { + if (Trees_Symbol(expA) == TRUE) { + result = expB; + } else { + result = expA; + } + } + break; + } + + return result; +} + + +static const char *DesignatorString(Trees_Node designator) +{ + const char *baseName; + char *result; + + assert(IsDesignator(designator)); + + baseName = Trees_Name(BaseIdent(designator)); + NEW_ARRAY(result, strlen(baseName) + strlen("...") + 1); + if (FirstSelector(designator) != NULL) { + sprintf(result, "%s...", baseName); + } else { + sprintf(result, "%s", baseName); + } + return result; +} + + +static const char *OperatorString(int operator) +{ + const char *result = ""; + + switch (operator) { + case '+': + result = "+"; + break; + case '-': + result = "-"; + break; + case '*': + result = "*"; + break; + case '/': + result = "/"; + break; + case DIV: + result = "DIV"; + break; + case MOD: + result = "MOD"; + break; + case OR: + result = "OR"; + break; + case '&': + result = "&"; + break; + case '~': + result = "~"; + break; + case '=': + result = "="; + break; + case '#': + result = "#"; + break; + case '<': + result = "<"; + break; + case LE: + result = "<="; + break; + case '>': + result = ">"; + break; + case GE: + result = ">="; + break; + case IN: + result = "IN"; + break; + case IS: + result = "IS"; + break; + default: + assert(0); + } + return result; +} + + +/*functions for statement productions*/ + +static int Writable(Trees_Node designator) +{ + Trees_Node ident, type; + int kind, result; + + assert(IsDesignator(designator)); + + ident = BaseIdent(designator); + kind = Trees_Kind(ident); + type = Trees_Type(ident); + result = ((kind == TREES_VARIABLE_KIND) && ! Trees_Imported(ident)) + || (kind == TREES_VAR_PARAM_KIND) + || ((kind == TREES_VALUE_PARAM_KIND) && ! Types_IsArray(type) && ! Types_IsRecord(type)); + return result; +} + + +static char *AssignmentErrorContext(int context, int paramPos) +{ + char *result; + + NEW_ARRAY(result, 64); + switch (context) { + case ASSIGNMENT_CONTEXT: + strcpy(result, "assignment"); + break; + case PARAM_SUBST_CONTEXT: + assert(paramPos >= 0); + sprintf(result, "substitution of parameter %d", paramPos + 1); + break; + case PROC_RESULT_CONTEXT: + strcpy(result, "return clause"); + break; + default: + assert(0); + } + return result; +} + + +static void ValidateAssignment(Trees_Node expression, Trees_Node targetType, int context, int paramPos) +{ + const char *errorContext; + + assert(expression != NULL); + assert(targetType != NULL); + assert(context >= 0); + assert(paramPos >= 0); + if (Types_AssignmentCompatible(expression, targetType)) { + if (Types_IsByte(targetType) && IsInteger(expression)) { + Range_CheckByte(Trees_Integer(expression)); + } + } else { + errorContext = AssignmentErrorContext(context, paramPos); + if (IsString(expression) && Types_IsCharacterArray(targetType) + && !Types_IsOpenArray(targetType)) { + Oberon_PrintContext(); + fprintf(stderr, "destination array to small in %s\n", errorContext); + exit(EXIT_FAILURE); + } else if (Types_IsPredeclaredProcedure(Trees_Type(expression)) + && Types_IsProcedure(targetType)) { + Oberon_PrintContext(); + fprintf(stderr, "non-predeclared procedure expected in %s\n", errorContext); + exit(EXIT_FAILURE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in %s: %s -> %s\n", + errorContext, TypeString(Trees_Type(expression)), TypeString(targetType)); + exit(EXIT_FAILURE); + } + } +} + + +static void ValidateActualParameter(Trees_Node actualParam, Trees_Node formalParam, int paramPos, Trees_Node procDesignator) +{ + Trees_Node formalType, actualType; + + formalType = Trees_Type(formalParam); + actualType = Trees_Type(actualParam); + + if ((Trees_Kind(formalParam) == TREES_VALUE_PARAM_KIND) + || (IsDesignator(actualParam) && Writable(actualParam))) { + if (Types_IsOpenArray(formalType)) { + if (! Types_ArrayCompatible(actualType, formalType)) { + Oberon_PrintContext(); + fprintf(stderr, "array compatible types expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } else if (Trees_Kind(formalParam) == TREES_VALUE_PARAM_KIND) { + if (! Types_AssignmentCompatible(actualParam, formalType)) { + Oberon_PrintContext(); + fprintf(stderr, "assignment compatible types expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } else if (Trees_Kind(formalParam) == TREES_VAR_PARAM_KIND) { + if (Types_IsRecord(formalType)) { + if (Types_IsRecord(actualType)) { + if (! Types_Extends(formalType, actualType)) { + Oberon_PrintContext(); + fprintf(stderr, "incompatible record types in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "record expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + } + } else { + if (! Types_Same(actualType, formalType)) { + Oberon_PrintContext(); + fprintf(stderr, "same types expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable variable expected in substitution of parameter %d in %s\n", + paramPos + 1, DesignatorString(procDesignator)); + exit(EXIT_FAILURE); + } +} + + +static void ValidateProcedureCall(Trees_Node expList, Trees_Node fpList, Trees_Node procDesignator) +{ + Trees_Node exp, formalParam, fpType; + int pos; + + pos = 0; + while ((expList != NULL) && (fpList != NULL)) { + exp = Trees_Left(expList); + CheckIsValueExpression(exp); + formalParam = Trees_Left(fpList); + fpType = Trees_Type(formalParam); + ValidateActualParameter(exp, formalParam, pos, procDesignator); + + if (Types_IsChar(fpType) && (Trees_Symbol(exp) == STRING)) { + Trees_SetLeft(Trees_NewChar(Trees_String(exp)[0]), expList); + } + expList = Trees_Right(expList); + fpList = Trees_Right(fpList); + pos++; + } + if ((expList == NULL) && (fpList != NULL)) { + Oberon_PrintContext(); + fprintf(stderr, "too few actual parameters in procedure call: %s\n", DesignatorString(procDesignator)); + exit(EXIT_FAILURE); + } else if ((expList != NULL) && (fpList == NULL)) { + Oberon_PrintContext(); + fprintf(stderr, "too many actual parameters in procedure call: %s\n", DesignatorString(procDesignator)); + exit(EXIT_FAILURE); + } +} + + +static Trees_Node PredeclaredProcedureAST(const char procName[], Trees_Node expList, int isFunctionCall) +{ + static const struct { const char *name; int symbol; } symbols[] = { + {"ABS", TREES_ABS_PROC}, + {"ASR", TREES_ASR_PROC}, + {"ASSERT", TREES_ASSERT_PROC}, + {"CHR", TREES_CHR_PROC}, + {"DEC", TREES_DEC_PROC}, + {"EXCL", TREES_EXCL_PROC}, + {"FLOOR", TREES_FLOOR_PROC}, + {"FLT", TREES_FLT_PROC}, + {"INC", TREES_INC_PROC}, + {"INCL", TREES_INCL_PROC}, + {"LEN", TREES_LEN_PROC}, + {"LSL", TREES_LSL_PROC}, + {"NEW", TREES_NEW_PROC}, + {"ODD", TREES_ODD_PROC}, + {"ORD", TREES_ORD_PROC}, + {"PACK", TREES_PACK_PROC}, + {"ROR", TREES_ROR_PROC}, + {"UNPK", TREES_UNPK_PROC}}; + + int paramCount, pos, symbol; + Trees_Node curr, resultType, result; + Trees_Node param[2], paramTypes[2]; + + /*set actual parameters*/ + paramCount = 0; + curr = expList; + while ((paramCount < LEN(param)) && (curr != NULL)) { + param[paramCount] = Trees_Left(curr); + paramTypes[paramCount] = Trees_Type(Trees_Left(curr)); + paramCount++; + curr = Trees_Right(curr); + } + + /*find procedure symbol*/ + pos = 0; + while ((pos < LEN(symbols)) && (strcmp(symbols[pos].name, procName) != 0)) { + pos++; + } + assert(pos < LEN(symbols)); + symbol = symbols[pos].symbol; + + /*validate parameters and build syntax tree*/ + result = NULL; + resultType = NULL; + switch (symbol) { + case TREES_ABS_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + switch (Trees_Symbol(Types_Structure(paramTypes[0]))) { + case TREES_INTEGER_TYPE: + if (IsInteger(param[0])) { + result = Trees_NewInteger(OBNC_ABS_INT(Trees_Integer(param[0]))); + } + break; + case TREES_REAL_TYPE: + if (IsReal(param[0])) { + result = Trees_NewReal(OBNC_ABS_FLT(Trees_Real(param[0]))); + } + break; + case TREES_BYTE_TYPE: + /*do nothing*/ + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "numeric parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + resultType = paramTypes[0]; + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_ODD_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsInteger(paramTypes[0])) { + if (IsInteger(param[0])) { + result = Trees_NewInteger(OBNC_ODD(Trees_Integer(param[0]))); + } + resultType = Trees_NewLeaf(TREES_BOOLEAN_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_LEN_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsArray(paramTypes[0])) { + if (! Types_IsOpenArray(paramTypes[0])) { + result = Types_ArrayLength(paramTypes[0]); + } + resultType = Trees_NewLeaf(TREES_INTEGER_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "array parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_LSL_PROC: /*fall through*/ + case TREES_ASR_PROC: /*fall through*/ + case TREES_ROR_PROC: + if (isFunctionCall) { + if (paramCount == 2) { + if (Types_IsInteger(paramTypes[0])) { + if (Types_IsInteger(paramTypes[1])) { + if (IsInteger(param[1])) { + switch (symbol) { + case TREES_LSL_PROC: + Range_CheckLSL(Trees_Integer(param[1])); + break; + case TREES_ASR_PROC: + Range_CheckASR(Trees_Integer(param[1])); + break; + case TREES_ROR_PROC: + Range_CheckROR(Trees_Integer(param[1])); + break; + default: + assert(0); + } + } + if (IsInteger(param[0]) && IsInteger(param[1])) { + switch (symbol) { + case TREES_LSL_PROC: + result = Trees_NewInteger(OBNC_LSL(Trees_Integer(param[0]), Trees_Integer(param[1]))); + break; + case TREES_ASR_PROC: + result = Trees_NewInteger(OBNC_ASR(Trees_Integer(param[0]), Trees_Integer(param[1]))); + break; + case TREES_ROR_PROC: + result = Trees_NewInteger(OBNC_ROR(Trees_Integer(param[0]), Trees_Integer(param[1]))); + break; + default: + assert(0); + } + } + resultType = paramTypes[0]; + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_FLOOR_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsReal(paramTypes[0])) { + if (IsReal(param[0])) { + OBNC_LONGR double x = Trees_Real(param[0]); + Range_CheckFLOOR(x); + result = Trees_NewInteger(OBNC_FLOOR(x)); + } + resultType = Trees_NewLeaf(TREES_INTEGER_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "real-valued parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_FLT_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsInteger(paramTypes[0])) { + if (IsInteger(param[0])) { + result = Trees_NewReal(OBNC_FLT(Trees_Integer(param[0]))); + } + resultType = Trees_NewLeaf(TREES_REAL_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_ORD_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + switch (Trees_Symbol(Types_Structure(paramTypes[0]))) { + case TREES_CHAR_TYPE: + /*do nothing*/ + break; + case TREES_STRING_TYPE: + if (Types_StringLength(paramTypes[0]) <= 1) { + result = Trees_NewInteger(Trees_String(param[0])[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "single-character string parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_BOOLEAN_TYPE: + if (Trees_Symbol(param[0]) == TRUE) { + result = Trees_NewInteger(1); + } else if (Trees_Symbol(param[0]) == FALSE) { + result = Trees_NewInteger(0); + } + break; + case TREES_SET_TYPE: + if (IsSet(param[0])) { + result = Trees_NewInteger(Trees_Set(param[0])); + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "character parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + resultType = Trees_NewLeaf(TREES_INTEGER_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_CHR_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsInteger(paramTypes[0])) { + if (IsInteger(param[0])) { + int i = Trees_Integer(param[0]); + Range_CheckCHR(i); + result = Trees_NewChar(OBNC_CHR(i)); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + resultType = Trees_NewLeaf(TREES_CHAR_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_INC_PROC: /*fall through*/ + case TREES_DEC_PROC: + if (! isFunctionCall) { + if ((paramCount == 1) || (paramCount == 2)) { + if (IsDesignator(param[0])) { + if (Types_IsInteger(paramTypes[0])) { + if (Writable(param[0])) { + if ((paramCount == 2) && ! Types_IsInteger(paramTypes[1])) { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one or two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_INCL_PROC: /*fall through*/ + case TREES_EXCL_PROC: + if (! isFunctionCall) { + if (paramCount == 2) { + if (IsDesignator(param[0])) { + if (Types_IsSet(paramTypes[0])) { + if (Writable(param[0])) { + if (IsInteger(param[1])) { + Range_CheckSetElement(Trees_Integer(param[1])); + } else if (! Types_IsInteger(paramTypes[1])) { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "set expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_ASSERT_PROC: + if (! isFunctionCall) { + if (paramCount == 1) { + if (Types_IsBoolean(paramTypes[0])) { + result = param[0]; + if (Trees_Symbol(param[0]) == TRUE) { + result = Trees_NewLeaf(TRUE); + } else if (Trees_Symbol(param[0]) == FALSE) { + result = Trees_NewLeaf(FALSE); + } + result = Trees_NewNode( + TREES_ASSERT_PROC, + result, + Trees_NewNode(TREES_FILE_POSITION, + Trees_NewString(inputFilename), + Trees_NewInteger(yylineno))); + } else { + Oberon_PrintContext(); + fprintf(stderr, "boolean parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_NEW_PROC: + if (! isFunctionCall) { + if (paramCount == 1) { + if (IsDesignator(param[0])) { + if (Trees_Symbol(Types_Structure(paramTypes[0])) == POINTER) { + if (! Writable(param[0])) { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "pointer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_PACK_PROC: + if (! isFunctionCall) { + if (paramCount == 2) { + if (IsDesignator(param[0])) { + if (Types_IsReal(paramTypes[0])) { + if (Writable(param[0])) { + if (! Types_IsInteger(paramTypes[1])) { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "real-valued expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_UNPK_PROC: + if (! isFunctionCall) { + if (paramCount == 2) { + if (IsDesignator(param[0]) && IsDesignator(param[1])) { + if (Types_IsReal(paramTypes[0])) { + if (Writable(param[0])) { + if (Types_IsInteger(paramTypes[1])) { + if (! Writable(param[1])) { + Oberon_PrintContext(); + fprintf(stderr, "second parameter is read-only: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "first parameter is read-only: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "real-valued expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two variable parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + default: + assert(0); + } + + if (result == NULL) { + if (paramCount == 1) { + result = Trees_NewNode(symbol, param[0], NULL); + } else { + result = Trees_NewNode(symbol, param[0], param[1]); + } + } + Trees_SetType(resultType, result); + + return result; +} + + +static void HandleProcedureCall(Trees_Node designator, Trees_Node expList, int isFunctionCall, Trees_Node *ast) +{ + Trees_Node ident, designatorTypeStruct, fpList, resultType; + + ident = BaseIdent(designator); + if (Types_IsPredeclaredProcedure(Trees_Type(ident))) { + *ast = PredeclaredProcedureAST(Trees_Name(ident), expList, isFunctionCall); + if (*ast == NULL) { + Oberon_PrintContext(); + fprintf(stderr, "error: procedure expected\n"); + exit(EXIT_FAILURE); + } + } else { + /*handle non-predeclared procedure*/ + designatorTypeStruct = Types_Structure(Trees_Type(designator)); + if (Types_IsProcedure(designatorTypeStruct)) { + fpList =Types_Parameters(designatorTypeStruct); + resultType = Types_ResultType(designatorTypeStruct); + ValidateProcedureCall(expList, fpList, designator); + *ast = Trees_NewNode(TREES_PROCEDURE_CALL, designator, expList); + if (isFunctionCall) { + if (resultType != NULL) { + Trees_SetType(resultType, *ast); + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", Trees_Name(ident)); + exit(EXIT_FAILURE); + } + } else if (resultType != NULL) { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", Trees_Name(ident)); + exit(EXIT_FAILURE); + } + } + } + assert(*ast != NULL); +} + + +static void CheckIntegerLabelDisjointness(Trees_Node rangeA, Trees_Node rangeB) +{ + int aMin, aMax, bMin, bMax; + + if (Trees_Symbol(rangeA) == DOTDOT) { + aMin = Trees_Integer(Trees_Left(rangeA)); + aMax = Trees_Integer(Trees_Right(rangeA)); + } else { + aMin = Trees_Integer(rangeA); + aMax = Trees_Integer(rangeA); + } + if (Trees_Symbol(rangeB) == DOTDOT) { + bMin = Trees_Integer(Trees_Left(rangeB)); + bMax = Trees_Integer(Trees_Right(rangeB)); + } else { + bMin = Trees_Integer(rangeB); + bMax = Trees_Integer(rangeB); + } + + if ((aMin >= bMin) && (aMin <= bMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %d\n", aMin); + exit(EXIT_FAILURE); + } else if ((bMin >= aMin) && (bMin <= aMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %d\n", bMin); + exit(EXIT_FAILURE); + } +} + + +static void CheckCharLabelDisjointness(Trees_Node rangeA, Trees_Node rangeB) +{ + char aMin, aMax, bMin, bMax; + + if (Trees_Symbol(rangeA) == DOTDOT) { + aMin = Trees_Char(Trees_Left(rangeA)); + aMax = Trees_Char(Trees_Right(rangeA)); + } else { + aMin = Trees_Char(rangeA); + aMax = Trees_Char(rangeA); + } + if (Trees_Symbol(rangeB) == DOTDOT) { + bMin = Trees_Char(Trees_Left(rangeB)); + bMax = Trees_Char(Trees_Right(rangeB)); + } else { + bMin = Trees_Char(rangeB); + bMax = Trees_Char(rangeB); + } + + if ((aMin >= bMin) && (aMin <= bMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %c\n", aMin); + exit(EXIT_FAILURE); + } else if ((bMin >= aMin) && (bMin <= aMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %c\n", bMin); + exit(EXIT_FAILURE); + } +} + + +static void CheckCaseLabelUniqueness(Trees_Node newLabelRange) +{ + int labelSymbol; + Trees_Node labelList, definedLabelRange; + + if (Trees_Symbol(newLabelRange) == DOTDOT) { + labelSymbol = Trees_Symbol(Trees_Left(newLabelRange)); + } else { + labelSymbol = Trees_Symbol(newLabelRange); + } + + labelList = currentlyDefinedCaseLabels; + while (labelList != NULL) { + definedLabelRange = Trees_Left(labelList); + switch (labelSymbol) { + case INTEGER: + CheckIntegerLabelDisjointness(definedLabelRange, newLabelRange); + break; + case TREES_CHAR_CONSTANT: + CheckCharLabelDisjointness(definedLabelRange, newLabelRange); + break; + case IDENT: + if (Types_Same(definedLabelRange, newLabelRange)) { + Oberon_PrintContext(); + fprintf(stderr, "type label defined twice: %s\n", Trees_Name(newLabelRange)); + exit(EXIT_FAILURE); + } + break; + default: + assert(0); + } + labelList = Trees_Right(labelList); + } +} + + +/*functions for module productions*/ + +static void ExportSymbolTable(const char symfilePath[]) +{ + static char tempSymfilePath[PATH_MAX + 1]; + + if (! Files_Exists(".obnc")) { + Files_CreateDir(".obnc"); + } + sprintf(tempSymfilePath, ".obnc/%s.sym.%d", inputModuleName, getpid()); + Table_Export(tempSymfilePath); + Files_Move(tempSymfilePath, symfilePath); +} diff --git a/src/Path.c b/src/Path.c new file mode 100644 index 0000000..8e6dc36 --- /dev/null +++ b/src/Path.c @@ -0,0 +1,144 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Path.h" +#include "Config.h" +#include "Files.h" +#include "Util.h" +#include +#include +#include +#include +#include +#include + +static void Append(const char extra[], char s[], int sLen) +{ + int sStrLen, extraStrLen; + + sStrLen = strlen(s); + extraStrLen = strlen(extra); + if (sStrLen + extraStrLen < sLen) { + strcpy(s + sStrLen, extra); + } else { + fprintf(stderr, "target array too short for concatenation (length %d): %s\n", sLen, s); + fprintf(stderr, "source string: \"%s\"\n", extra); + exit(EXIT_FAILURE); + } +} + + +static void GetModulePrefix(const char module[], char prefix[], int prefixLen) +{ + int i; + + i = 0; + while (((module[i] >= 'a') && (module[i] <= 'z')) || ((module[i] >= '0') && (module[i] <= '9'))) { + assert(i < prefixLen); + prefix[i] = module[i]; + i++; + } + if ((module[i] >= 'A') && (module[i] <= 'Z')) { + prefix[i] = '\0'; + } else { + prefix[0] = '\0'; + } +} + + +static void HandlePath(const char module[], int level, char dirPath[], int dirPathLen, int *found) +{ + char symfilePath[PATH_MAX + 1], modulePrefix[FILENAME_MAX + 1]; + + *found = 0; + symfilePath[0] = '\0'; + Append(dirPath, symfilePath, LEN(symfilePath)); + Append("/.obnc/", symfilePath, LEN(symfilePath)); + Append(module, symfilePath, LEN(symfilePath)); + Append(".sym", symfilePath, LEN(symfilePath)); + *found = Files_Exists(symfilePath); + if (! *found) { + symfilePath[0] = '\0'; + Append(dirPath, symfilePath, LEN(symfilePath)); + Append("/", symfilePath, LEN(symfilePath)); + Append(module, symfilePath, LEN(symfilePath)); + Append(".sym", symfilePath, LEN(symfilePath)); + *found = Files_Exists(symfilePath); + if (! *found) { + symfilePath[0] = '\0'; + Append(dirPath, symfilePath, LEN(symfilePath)); + Append("/", symfilePath, LEN(symfilePath)); + Append(module, symfilePath, LEN(symfilePath)); + Append(".obn", symfilePath, LEN(symfilePath)); + *found = Files_Exists(symfilePath); + if (! *found & (level == 0)) { + GetModulePrefix(module, modulePrefix, LEN(modulePrefix)); + Append("/", dirPath, dirPathLen); + Append(modulePrefix, dirPath, dirPathLen); + HandlePath(module, 1, dirPath, dirPathLen, found); + } + } + } +} + + +void Path_Get(const char module[], char dirPath[], int dirPathLen) +{ + const char *impPaths; + int found, pathStart, pathEnd; + + assert(module != NULL); + assert(dirPath != NULL); + assert(dirPathLen > 0); + + dirPath[0] = '\0'; + Append(".", dirPath, dirPathLen); + + /*search current directory*/ + HandlePath(module, 0, dirPath, dirPathLen, &found); + if (! found) { + /*search OBNC_IMPORT_PATH*/ + impPaths = getenv("OBNC_IMPORT_PATH"); + if (impPaths != NULL) { + pathStart = 0; + do { + pathEnd = pathStart; + while ((impPaths[pathEnd] != '\0') && (impPaths[pathEnd] != ':')) { + pathEnd++; + } + if (pathEnd > pathStart) { + memcpy(dirPath, impPaths + pathStart, pathEnd - pathStart); + dirPath[pathEnd - pathStart] = '\0'; + HandlePath(module, 0, dirPath, dirPathLen, &found); + } + pathStart = pathEnd + 1; + } while (! found && (impPaths[pathEnd] != '\0')); + } + if (! found) { + /*search install path*/ + dirPath[0] = '\0'; + Append(Config_Prefix(), dirPath, dirPathLen); + Append("/", dirPath, dirPathLen); + Append(Config_LibDir(), dirPath, dirPathLen); + Append("/obnc", dirPath, dirPathLen); + HandlePath(module, 0, dirPath, dirPathLen, &found); + if (! found) { + dirPath[0] = '\0'; + } + } + } +} diff --git a/src/Path.h b/src/Path.h new file mode 100644 index 0000000..9ac8784 --- /dev/null +++ b/src/Path.h @@ -0,0 +1,23 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef PATH_H +#define PATH_H + +void Path_Get(const char module[], char dirPath[], int dirPathLen); + +#endif diff --git a/src/Range.c b/src/Range.c new file mode 100644 index 0000000..ec22d5a --- /dev/null +++ b/src/Range.c @@ -0,0 +1,216 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Range.h" +#include "Oberon.h" +#include +#include +#include + +#ifdef OBNC_CONFIG_USE_LONG_INT + #define INTEGER_MIN LONG_MIN + #define INTEGER_MAX LONG_MAX +#else + #define INTEGER_MIN INT_MIN + #define INTEGER_MAX INT_MAX +#endif + +#ifdef OBNC_CONFIG_USE_LONG_REAL + #define REAL_MAX LDBL_MAX +#else + #define REAL_MAX DBL_MAX +#endif + +#define SHIFT_COUNT_MAX (CHAR_BIT * sizeof (OBNC_LONGI int) - 1) +#define SET_ELEMENT_MAX (CHAR_BIT * sizeof (OBNC_LONGI unsigned int) - 1) + +void Range_CheckIntSum(OBNC_LONGI int a, OBNC_LONGI int b) +{ + if ((b > 0) && (a > INTEGER_MAX - b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: %" OBNC_INT_MOD "d + %" OBNC_INT_MOD "d > %" OBNC_INT_MOD "d\n", a, b, INTEGER_MAX); + } else if ((b < 0) && (a < INTEGER_MIN - b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: %" OBNC_INT_MOD "d + (%" OBNC_INT_MOD "d) < %" OBNC_INT_MOD "d\n", a, b, INTEGER_MIN); + } +} + + +void Range_CheckIntDiff(OBNC_LONGI int a, OBNC_LONGI int b) +{ + if ((b < 0) && (a > INTEGER_MAX + b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: %" OBNC_INT_MOD "d - (%" OBNC_INT_MOD "d) > %" OBNC_INT_MOD "d\n", a, b, INTEGER_MAX); + } else if ((b > 0) && (a < INTEGER_MIN + b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: %" OBNC_INT_MOD "d - %" OBNC_INT_MOD "d < %" OBNC_INT_MOD "d\n", a, b, INTEGER_MIN); + } +} + + +void Range_CheckIntProd(OBNC_LONGI int a, OBNC_LONGI int b) +{ + if (b > 0) { + if ((a > 0) && (a > INTEGER_MAX / b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: %" OBNC_INT_MOD "d * %" OBNC_INT_MOD "d > %" OBNC_INT_MOD "d\n", a, b, INTEGER_MAX); + } else if ((a < 0) && (a < INTEGER_MIN / b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: (%" OBNC_INT_MOD "d) * %" OBNC_INT_MOD "d < %" OBNC_INT_MOD "d\n", a, b, INTEGER_MIN); + } + } else if (b < 0) { + if ((a > 0) && (a < INTEGER_MIN / b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: %" OBNC_INT_MOD "d * (%" OBNC_INT_MOD "d) < %" OBNC_INT_MOD "d\n", a, b, INTEGER_MIN); + } else if ((a < 0) && (a < INTEGER_MAX / b)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: integer overflow: (%" OBNC_INT_MOD "d) * (%" OBNC_INT_MOD "d) > %" OBNC_INT_MOD "d\n", a, b, INTEGER_MAX); + } + } +} + + +void Range_CheckLSL(OBNC_LONGI int n) +{ + if (n < 0) { + Oberon_PrintContext(); + fprintf(stderr, "warning: negative left shift count: %" OBNC_INT_MOD "d < 0\n", n); + } else if (n > SHIFT_COUNT_MAX ) { + Oberon_PrintContext(); + fprintf(stderr, "warning: left shift count exceeds maximum: %" OBNC_INT_MOD "d > %lu\n", n, (long unsigned) SHIFT_COUNT_MAX); + } +} + + +void Range_CheckASR(OBNC_LONGI int n) +{ + if (n < 0) { + Oberon_PrintContext(); + fprintf(stderr, "warning: negative right shift count: %" OBNC_INT_MOD "d < 0\n", n); + } else if (n > SHIFT_COUNT_MAX) { + Oberon_PrintContext(); + fprintf(stderr, "warning: right shift count exceeds maximum: %" OBNC_INT_MOD "d > %lu\n", n, (long unsigned) SHIFT_COUNT_MAX); + } +} + + +void Range_CheckROR(OBNC_LONGI int n) +{ + if (n < 1) { + Oberon_PrintContext(); + fprintf(stderr, "warning: non-positive rotation: %" OBNC_INT_MOD "d < 1\n", n); + } else if (n > SHIFT_COUNT_MAX) { + Oberon_PrintContext(); + fprintf(stderr, "warning: rotation exceeds maximum: %" OBNC_INT_MOD "d > %lu\n", n, (long unsigned) SHIFT_COUNT_MAX); + } +} + + +void Range_CheckFLOOR(OBNC_LONGR double x) +{ + if (x < (OBNC_LONGR double) INT_MIN) { + Oberon_PrintContext(); + fprintf(stderr, "warning: parameter in FLOOR too large for truncation to INTEGER: %" OBNC_REAL_MOD_W "E < %" OBNC_REAL_MOD_W "E\n", x, (OBNC_LONGR double) INT_MIN); + } else if (x >= (OBNC_LONGR double) INT_MAX + 1.0) { + Oberon_PrintContext(); + fprintf(stderr, "warning: parameter in FLOOR too large for truncation to INTEGER: %" OBNC_REAL_MOD_W "E >= %" OBNC_REAL_MOD_W "E\n", x, (OBNC_LONGR double) INT_MAX + 1.0); + } +} + + +void Range_CheckCHR(OBNC_LONGI int n) +{ + if (n < 0) { + Oberon_PrintContext(); + fprintf(stderr, "error: negative parameter in CHR: %" OBNC_INT_MOD "d\n", n); + exit(EXIT_FAILURE); + } else if (n > CHAR_MAX) { + Oberon_PrintContext(); + fprintf(stderr, "warning: parameter in CHR too large for conversion: %" OBNC_INT_MOD "d > %d\n", n, CHAR_MAX); + } +} + + +void Range_CheckRealSum(OBNC_LONGR double x, OBNC_LONGR double y) +{ + if ((y > 0.0) && (x > REAL_MAX - y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: %" OBNC_REAL_MOD_W "G + %" OBNC_REAL_MOD_W "G > %" OBNC_REAL_MOD_W "G\n", x, y, REAL_MAX); + } else if ((y < 0.0) && (x < -REAL_MAX - y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: %" OBNC_REAL_MOD_W "G + (%" OBNC_REAL_MOD_W "G) < %" OBNC_REAL_MOD_W "G\n", x, y, -REAL_MAX); + } +} + + +void Range_CheckRealDiff(OBNC_LONGR double x, OBNC_LONGR double y) +{ + if ((y < 0.0) && (x > REAL_MAX + y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: %" OBNC_REAL_MOD_W "G - (%" OBNC_REAL_MOD_W "G) > %" OBNC_REAL_MOD_W "G\n", x, y, REAL_MAX); + } else if ((y > 0.0) && (x < -REAL_MAX + y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: %" OBNC_REAL_MOD_W "G - %" OBNC_REAL_MOD_W "G < %" OBNC_REAL_MOD_W "G\n", x, y, -REAL_MAX); + } +} + + +void Range_CheckRealProd(OBNC_LONGR double x, OBNC_LONGR double y) +{ + if (y > 0.0) { + if ((x > 0.0) && (x > REAL_MAX / y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: %" OBNC_REAL_MOD_W "G * %" OBNC_REAL_MOD_W "G > %" OBNC_REAL_MOD_W "G\n", x, y, REAL_MAX); + } else if ((x < 0.0) && (x < -REAL_MAX / y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: (%" OBNC_REAL_MOD_W "G) * %" OBNC_REAL_MOD_W "G < %" OBNC_REAL_MOD_W "G\n", x, y, -REAL_MAX); + } + } else if (y < 0.0) { + if ((x > 0.0) && (x < -REAL_MAX / y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: %" OBNC_REAL_MOD_W "G * (%" OBNC_REAL_MOD_W "G) < %" OBNC_REAL_MOD_W "G\n", x, y, -REAL_MAX); + } else if ((x < 0.0) && (x < REAL_MAX / y)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: real number overflow: (%" OBNC_REAL_MOD_W "G) * (%" OBNC_REAL_MOD_W "G) > %" OBNC_REAL_MOD_W "G\n", x, y, REAL_MAX); + } + } +} + + +void Range_CheckByte(OBNC_LONGI int n) +{ + if (n < 0) { + Oberon_PrintContext(); + fprintf(stderr, "warning: negative byte overflow: %" OBNC_INT_MOD "d < 0\n", n); + } else if (n > UCHAR_MAX) { + Oberon_PrintContext(); + fprintf(stderr, "warning: byte overflow: %" OBNC_INT_MOD "d > %d\n", n, UCHAR_MAX); + } +} + + +void Range_CheckSetElement(OBNC_LONGI int x) +{ + if (x < 0) { + Oberon_PrintContext(); + fprintf(stderr, "error: negative set element: %" OBNC_INT_MOD "d\n", x); + exit(EXIT_FAILURE); + } else if (x > SET_ELEMENT_MAX) { + Oberon_PrintContext(); + fprintf(stderr, "warning: set element exceededs maximum: %" OBNC_INT_MOD "d > %lu\n", x, (long unsigned) SET_ELEMENT_MAX); + } +} diff --git a/src/Range.h b/src/Range.h new file mode 100644 index 0000000..71b086c --- /dev/null +++ b/src/Range.h @@ -0,0 +1,49 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef RANGE_H +#define RANGE_H + +#include "../lib/obnc/OBNC.h" + +void Range_CheckIntSum(OBNC_LONGI int a, OBNC_LONGI int b); + +void Range_CheckIntDiff(OBNC_LONGI int a, OBNC_LONGI int b); + +void Range_CheckIntProd(OBNC_LONGI int a, OBNC_LONGI int b); + +void Range_CheckLSL(OBNC_LONGI int n); + +void Range_CheckASR(OBNC_LONGI int n); + +void Range_CheckROR(OBNC_LONGI int n); + +void Range_CheckFLOOR(OBNC_LONGR double x); + +void Range_CheckCHR(OBNC_LONGI int n); + +void Range_CheckRealSum(OBNC_LONGR double x, OBNC_LONGR double y); + +void Range_CheckRealDiff(OBNC_LONGR double x, OBNC_LONGR double y); + +void Range_CheckRealProd(OBNC_LONGR double x, OBNC_LONGR double y); + +void Range_CheckByte(OBNC_LONGI int n); + +void Range_CheckSetElement(OBNC_LONGI int x); + +#endif diff --git a/src/StackTrace.c b/src/StackTrace.c new file mode 100644 index 0000000..7a938a7 --- /dev/null +++ b/src/StackTrace.c @@ -0,0 +1,28 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "StackTrace.h" + +#ifdef __linux__ + #include "StackTraceLinux.c" +#else + +void StackTrace_Init(void) +{ +} + +#endif diff --git a/src/StackTrace.h b/src/StackTrace.h new file mode 100644 index 0000000..82e22a7 --- /dev/null +++ b/src/StackTrace.h @@ -0,0 +1,25 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef STACKTRACE_H +#define STACKTRACE_H + +#define STACKTRACE_MAXLEN 10 + +void StackTrace_Init(void); + +#endif diff --git a/src/StackTraceLinux.c b/src/StackTraceLinux.c new file mode 100644 index 0000000..f230cd0 --- /dev/null +++ b/src/StackTraceLinux.c @@ -0,0 +1,120 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "StackTrace.h" +#include "Oberon.h" +#include "Util.h" +#include /*GNU specific*/ +#include /*POSIX*/ +#include +#include +#include +#include + +static void ScanFilenameAndOffset(const char line[], char **filename, char **offset, int *done) +{ + const char *leftParenPtr, *leftBracketPtr, *rightBracketPtr; + int exeFileLen, offsetLen; + + *filename = NULL; + *offset = NULL; + *done = 0; + leftParenPtr = strrchr(line, '('); + if (leftParenPtr != NULL) { + /*scan filename*/ + exeFileLen = leftParenPtr - line + 1; + NEW_ARRAY(*filename, exeFileLen); + memcpy(*filename, line, exeFileLen - 1); + (*filename)[exeFileLen - 1] = '\0'; + + /*scan file offset*/ + leftBracketPtr = strrchr(line, '['); + if (leftBracketPtr != NULL) { + rightBracketPtr = strrchr(line, ']'); + if (rightBracketPtr != 0) { + offsetLen = rightBracketPtr - leftBracketPtr - 1 + 1; + NEW_ARRAY(*offset, offsetLen); + memcpy(*offset, leftBracketPtr + 1, offsetLen - 1); + (*offset)[offsetLen - 1] = '\0'; + *done = 1; + } + } + } +} + + +static void PrintSourceFilePosition(const char binFilename[], const char offset[]) +{ + const char *commandFormat; + int commandLen, error; + char *command; + + commandFormat = "addr2line -f -e %s %s | grep -v '^?' | sed 's|^/|\t/|' >&2"; + commandLen = strlen(commandFormat) + (strlen(binFilename) - strlen("%s")) + (strlen(offset) - strlen("%s")) + 1; + NEW_ARRAY(command, commandLen); + sprintf(command, commandFormat, binFilename, offset); + error = system(command); + if (error) { + fprintf(stderr, "command to print stack trace failed: %s\n", command); + } +} + + +static void PrintStackTrace(int signum) +{ + void *returnAddresses[STACKTRACE_MAXLEN]; + size_t count; + char **lines; + int lineNum, done; + char *filename, *offset; + + Oberon_PrintContext(); + fprintf(stderr, "\n"); + count = backtrace(returnAddresses, LEN(returnAddresses)); + lines = backtrace_symbols(returnAddresses, count); + fprintf(stderr, "Fatal signal raised, stack trace:\n"); + for (lineNum = 0; lineNum < count; lineNum++) { + ScanFilenameAndOffset(lines[lineNum], &filename, &offset, &done); + if (done) { + PrintSourceFilePosition(filename, offset); + } else { + fprintf(stderr, "warning: failed getting filename and offset from backtrace\n"); + } + } + free(lines); +} + + +void StackTrace_Init(void) +{ + static int initialized = 0; + static const int fatalSignals[] = {SIGABRT, SIGALRM, SIGFPE, SIGHUP, SIGILL, SIGINT, /*SIGKILL,*/ SIGPIPE, SIGQUIT, SIGSEGV, SIGTERM, SIGUSR1, SIGUSR2}; + int i; + void (*prevHandler)(int signum); + + if (! initialized) { + /*register signal handler for fatal signals*/ + for (i = 0; i < LEN(fatalSignals); i++) { + prevHandler = signal(fatalSignals[i], PrintStackTrace); + if (prevHandler == SIG_ERR) { + fprintf(stderr, "warning: setting signal handler for PrintStackTrace failed: signal: %d\n", fatalSignals[i]); + } else if (prevHandler != NULL) { + fprintf(stderr, "replacing previous signal handler with PrintStackTrace\n"); + } + } + } +} diff --git a/src/Table.c b/src/Table.c new file mode 100644 index 0000000..3c29912 --- /dev/null +++ b/src/Table.c @@ -0,0 +1,1161 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Table.h" +#include "Config.h" +#include "Files.h" +#include "Maps.h" +#include "Trees.h" +#include "Types.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" +#include "y.tab.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/*symbol file symbols*/ +#define IDENT_SYM 1 +#define BOOLEAN_SYM 2 +#define CHAR_SYM 3 +#define INTEGER_SYM 4 +#define REAL_SYM 5 +#define STRING_SYM 6 +#define SET_SYM 7 +#define BOOLEAN_TYPE_SYM 8 +#define CHAR_TYPE_SYM 9 +#define INTEGER_TYPE_SYM 10 +#define REAL_TYPE_SYM 11 +#define BYTE_TYPE_SYM 12 +#define SET_TYPE_SYM 13 +#define ARRAY_SYM 14 +#define RECORD_SYM 15 +#define POINTER_SYM 16 +#define PROCEDURE_SYM 17 + +/*symbol file identifier kinds*/ +#define CONST_KIND 1 +#define TYPE_KIND 2 +#define VAR_KIND 3 +#define PROCEDURE_KIND 4 +#define FIELD_KIND 5 +#define VALUE_PARAM_KIND 6 +#define VAR_PARAM_KIND 7 + +typedef struct ScopeDesc *Scope; +struct ScopeDesc { + Maps_Map symbols; + Scope parent; +}; + +static const char *predeclaredNames[24]; +static Trees_Node predeclaredNodes[LEN(predeclaredNames)]; + +static Scope globalScope, currentScope; +static const char *importFilename, *exportFilename; +static FILE *importFile, *exportFile; +static Maps_Map writtenSymbols; + +void Table_Init(void) +{ + static const struct { const char *name; int kind, type; } predecIdents[] = { + {"ABS", TREES_PROCEDURE_KIND, TREES_ABS_PROC}, + {"ASR", TREES_PROCEDURE_KIND, TREES_ASR_PROC}, + {"ASSERT", TREES_PROCEDURE_KIND, TREES_ASSERT_PROC}, + {"BOOLEAN", TREES_TYPE_KIND, TREES_BOOLEAN_TYPE}, + {"BYTE", TREES_TYPE_KIND, TREES_BYTE_TYPE}, + {"CHAR", TREES_TYPE_KIND, TREES_CHAR_TYPE}, + {"CHR", TREES_PROCEDURE_KIND, TREES_CHR_PROC}, + {"DEC", TREES_PROCEDURE_KIND, TREES_DEC_PROC}, + {"EXCL", TREES_PROCEDURE_KIND, TREES_EXCL_PROC}, + {"FLOOR", TREES_PROCEDURE_KIND, TREES_FLOOR_PROC}, + {"FLT", TREES_PROCEDURE_KIND, TREES_FLT_PROC}, + {"INC", TREES_PROCEDURE_KIND, TREES_INC_PROC}, + {"INCL", TREES_PROCEDURE_KIND, TREES_INCL_PROC}, + {"INTEGER", TREES_TYPE_KIND, TREES_INTEGER_TYPE}, + {"LEN", TREES_PROCEDURE_KIND, TREES_LEN_PROC}, + {"LSL", TREES_PROCEDURE_KIND, TREES_LSL_PROC}, + {"NEW", TREES_PROCEDURE_KIND, TREES_NEW_PROC}, + {"ODD", TREES_PROCEDURE_KIND, TREES_ODD_PROC}, + {"ORD", TREES_PROCEDURE_KIND, TREES_ORD_PROC}, + {"PACK", TREES_PROCEDURE_KIND, TREES_PACK_PROC}, + {"REAL", TREES_TYPE_KIND, TREES_REAL_TYPE}, + {"ROR", TREES_PROCEDURE_KIND, TREES_ROR_PROC}, + {"SET", TREES_TYPE_KIND, TREES_SET_TYPE}, + {"UNPK", TREES_PROCEDURE_KIND, TREES_UNPK_PROC}}; + + int i; + Trees_Node node; + + assert(LEN(predecIdents) == LEN(predeclaredNames)); + assert(LEN(predeclaredNodes) == LEN(predeclaredNames)); + + for (i = 0; i < LEN(predeclaredNodes); i++) { + predeclaredNames[i] = predecIdents[i].name; + + node = Trees_NewIdent(predecIdents[i].name); + Trees_SetKind(predecIdents[i].kind, node); + Trees_SetType(Trees_NewLeaf(predecIdents[i].type), node); + predeclaredNodes[i] = node; + } + + NEW(globalScope); + globalScope->symbols = Maps_New(); + globalScope->parent = NULL; + currentScope = globalScope; +} + + +int Table_LocallyDeclared(const char name[]) +{ + return Maps_HasKey(name, currentScope->symbols); +} + + +void Table_Put(Trees_Node identNode) +{ + const char *name; + + assert(identNode != NULL); + assert(Trees_Symbol(identNode) == IDENT); + assert((Trees_Local(identNode) && Table_ScopeLocal()) + || (! Trees_Local(identNode) && ! Table_ScopeLocal())); + name = Trees_Name(identNode); + assert(! Table_LocallyDeclared(name)); + + Maps_Put(name, identNode, &(currentScope->symbols)); +} + + +static int Cmp(const void *name, const void *namePtr) +{ + return strcmp((char *) name, * (char **) namePtr); +} + + +static Trees_Node PredeclaredIdent(const char name[]) +{ + const char **namePtr; + int pos; + Trees_Node result; + + namePtr = bsearch(name, predeclaredNames, LEN(predeclaredNames), sizeof predeclaredNames[0], Cmp); + if (namePtr != NULL) { + pos = namePtr - predeclaredNames; + assert(pos >= 0); + assert(pos < LEN(predeclaredNodes)); + result = predeclaredNodes[pos]; + } else { + result = NULL; + } + return result; +} + + +Trees_Node Table_At(const char name[]) +{ + void *result; + + assert(name != NULL); + + result = Maps_At(name, currentScope->symbols); + if ((result == NULL) && (currentScope != globalScope)) { + result = Maps_At(name, globalScope->symbols); + } + if (result == NULL) { + result = PredeclaredIdent(name); + } + + assert((result == NULL) || Trees_Symbol(result) == IDENT); + + return result; +} + + +void Table_OpenScope(void) +{ + Scope newScope; + + NEW(newScope); + newScope->symbols = Maps_New(); + newScope->parent = currentScope; + currentScope = newScope; + + assert(currentScope != globalScope); +} + + +void Table_CloseScope(void) +{ + assert(currentScope != globalScope); + + currentScope = currentScope->parent; +} + + +int Table_ScopeLocal(void) +{ + return currentScope != globalScope; +} + + +static void GetFilePosition(FILE *file, long int *line, long int *col) +{ + long int savedPos, pos; + int ch; + + *line = 1; + *col = 0; + savedPos = ftell(file); + if (savedPos >= 0) { + rewind(file); + pos = savedPos; + ch = fgetc(file); + while ((pos > 0) && (ch != EOF)) { + if (ch == '\n') { + ++*line; + *col = 0; + } else { + ++*col; + } + pos--; + ch = fgetc(file); + } + fseek(file, savedPos, SEEK_SET); + } + if (ferror(file)) { + fprintf(stderr, "failed getting file position from %s: %s", importFilename, strerror(errno)); + exit(EXIT_FAILURE); + } +} + + +static void PrintImportErrorPrefix(void) +{ + long int line, col; + + assert(importFile != NULL); + GetFilePosition(importFile, &line, &col); + fprintf(stderr, "%s:%ld:%ld: ", importFilename, line, col); +} + + +static int StringLength(FILE *file) +{ + long int savedPos; + int ch, n; + + savedPos = ftell(file); + fscanf(file, " "); + n = -1; + ch = fgetc(file); + if (ch == '"') { + do { + ch = fgetc(file); + n++; + } while ((ch != EOF) && (ch != '\n') && (ch != '"')); + if (ch != '"') { + n = -1; + } + } + fseek(file, savedPos, SEEK_SET); + return n; +} + + +static int IdentLength(FILE *file) +{ + long int savedPos; + int ch, n; + + savedPos = ftell(file); + fscanf(file, " "); + n = 0; + ch = fgetc(file); + if (isalpha(ch)) { + do { + n++; + ch = fgetc(file); + } while (isalnum(ch)); + if (ch == '.') { + n++; + ch = fgetc(file); + if (isalpha(ch)) { + do { + n++; + ch = fgetc(file); + } while (isalnum(ch)); + } + } + } + fseek(file, savedPos, SEEK_SET); + return n; +} + + +static void ReadSExp(int isRoot, FILE *file, Trees_Node *resultPtr); + +static void ReadIdent(int isRoot, FILE *file, Trees_Node *resultPtr) +{ + char *name; + int len, sfKind, kind, n, exported; + + fscanf(file, " "); + len = IdentLength(file); + NEW_ARRAY(name, len + 1); + fgets(name, len + 1, file); + + *resultPtr = Trees_NewIdent(name); + + /*kind*/ + n = fscanf(file, "%d", &sfKind); + if (n == 1) { + switch (sfKind) { + case CONST_KIND: + kind = TREES_CONSTANT_KIND; + break; + case TYPE_KIND: + kind = TREES_TYPE_KIND; + break; + case VAR_KIND: + kind = TREES_VARIABLE_KIND; + break; + case PROCEDURE_KIND: + kind = TREES_PROCEDURE_KIND; + break; + case FIELD_KIND: + kind = TREES_FIELD_KIND; + break; + case VALUE_PARAM_KIND: + kind = TREES_VALUE_PARAM_KIND; + break; + case VAR_PARAM_KIND: + kind = TREES_VAR_PARAM_KIND; + break; + default: + PrintImportErrorPrefix(); + fprintf(stderr, "error: invalid identifier kind: %d\n", sfKind); + exit(EXIT_FAILURE); + } + Trees_SetKind(kind, *resultPtr); + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading identifier kind failed\n"); + exit(EXIT_FAILURE); + } + + /*exported flag*/ + if ((sfKind == TYPE_KIND) && isRoot) { + n = fscanf(file, "%d", &exported); + if (n == 1) { + if (exported) { + Trees_SetImported(*resultPtr); + } + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading export status failed\n"); + exit(EXIT_FAILURE); + } + } else if (sfKind == FIELD_KIND) { + Trees_SetExported(*resultPtr); + } else if ((sfKind != VALUE_PARAM_KIND) && (sfKind != VAR_PARAM_KIND)) { + Trees_SetImported(*resultPtr); + } + + /*value or type*/ + switch (sfKind) { + case CONST_KIND: + { + Trees_Node constValue; + + ReadSExp(0, file, &constValue); + Trees_SetValue(constValue, *resultPtr); + } + break; + case TYPE_KIND: + { + Trees_Node type; + + if (isRoot) { + ReadSExp(0, file, &type); + Trees_SetType(type, *resultPtr); + } + } + break; + case VAR_KIND: + case PROCEDURE_KIND: + case FIELD_KIND: + case VALUE_PARAM_KIND: + case VAR_PARAM_KIND: + { + Trees_Node type; + + ReadSExp(0, file, &type); + Trees_SetType(type, *resultPtr); + } + break; + default: + assert(0); + } +} + + +static int AtEndOfList(FILE *file) +{ + int ch, result; + + ch = getc(file); + result = ch == ')'; + ungetc(ch, file); + return result; +} + + +static void ReadFieldList(int isRoot, FILE *file, Trees_Node *resultPtr) +{ + Trees_Node field; + int ch; + + assert(resultPtr != NULL); + + *resultPtr = NULL; + fscanf(file, " "); + ch = fgetc(file); + if (ch == '(') { + while (! AtEndOfList(file)) { + ReadSExp(0, file, &field); + *resultPtr = Trees_NewNode(TREES_IDENT_LIST, field, *resultPtr); + fscanf(file, " "); + } + Trees_ReverseList(resultPtr); /*correct order*/ + fscanf(file, " "); + ch = fgetc(file); + if (ch != ')') { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected end of field list s-expression\n"); + exit(EXIT_FAILURE); + } + fscanf(file, " "); + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected start of field list s-expression\n"); + exit(EXIT_FAILURE); + } +} + + +static void ReadRecord(int isRoot, FILE *file, Trees_Node *resultPtr) +{ + Trees_Node baseType, fieldListSeq, fieldList; + int ch; + + ReadSExp(0, file, &baseType); + + fieldListSeq = NULL; + fscanf(file, " "); + ch = fgetc(file); + if (ch == '(') { + while (! AtEndOfList(file)) { + ReadFieldList(isRoot, file, &fieldList); + fieldListSeq = Trees_NewNode(TREES_FIELD_LIST_SEQUENCE, fieldList, fieldListSeq); + } + Trees_ReverseList(&fieldListSeq); /*correct order*/ + fscanf(file, " "); + ch = fgetc(file); + if (ch != ')') { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected end of field list sequence s-expression\n"); + exit(EXIT_FAILURE); + } + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected start of field list sequence s-expression\n"); + exit(EXIT_FAILURE); + } + + *resultPtr = Types_NewRecord(baseType, fieldListSeq); +} + + +static void ReadSymbol(int symbol, int isRoot, FILE *file, Trees_Node *resultPtr) +{ + assert(resultPtr != NULL); + + *resultPtr = NULL; + switch (symbol) { + case IDENT_SYM: + ReadIdent(isRoot, file, resultPtr); + break; + case BOOLEAN_SYM: + { + int b, n; + + n = fscanf(file, "%d", &b); + if (n == 1) { + if (b) { + *resultPtr = Trees_NewLeaf(TRUE); + + } else { + *resultPtr = Trees_NewLeaf(FALSE); + } + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), *resultPtr); + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading boolean value failed\n"); + exit(EXIT_FAILURE); + } + } + break; + case CHAR_SYM: + { + int i, n; + + n = fscanf(file, "%d", &i); + if (n == 1) { + if (i < CHAR_MIN) { + PrintImportErrorPrefix(); + fprintf(stderr, "warning: character constant out of range: %d < %d", i, CHAR_MIN); + } else if (i > CHAR_MAX) { + PrintImportErrorPrefix(); + fprintf(stderr, "warning: character constant out of range: %d > %d", i, CHAR_MAX); + } + *resultPtr = Trees_NewChar((char) i); + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading character constant failed\n"); + exit(EXIT_FAILURE); + } + } + break; + case INTEGER_SYM: + { + OBNC_LONGI int i, n; + + n = fscanf(file, "%" OBNC_INT_MOD "d", &i); + if (n == 1) { + *resultPtr = Trees_NewInteger(i); + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading integer failed\n"); + exit(EXIT_FAILURE); + } + } + break; + case REAL_SYM: + { + OBNC_LONGR double x; + int n; + + n = fscanf(file, "%" OBNC_REAL_MOD_R "f", &x); + if (n == 1) { + *resultPtr = Trees_NewReal(x); + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading real number failed\n"); + exit(EXIT_FAILURE); + } + } + break; + case STRING_SYM: + { + int done, len, ch; + char *s; + + done = 0; + len = StringLength(file); + NEW_ARRAY(s, len + 1); + fscanf(file, " "); + ch = fgetc(file); + if (ch == '"') { + fgets(s, len + 1, file); + ch = fgetc(file); + if (ch == '"') { + *resultPtr = Trees_NewString(s); + done = 1; + } + } + if (! done) { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading string failed\n"); + exit(EXIT_FAILURE); + } + } + break; + case SET_SYM: + { + unsigned int u; + int n; + + n = fscanf(file, "%u", &u); + if (n == 1) { + *resultPtr = Trees_NewSet(u); + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: reading set constant failed\n"); + exit(EXIT_FAILURE); + } + } + break; + case BOOLEAN_TYPE_SYM: + *resultPtr = Trees_NewNode(TREES_BOOLEAN_TYPE, NULL, NULL); + break; + case CHAR_TYPE_SYM: + *resultPtr = Trees_NewNode(TREES_CHAR_TYPE, NULL, NULL); + break; + case INTEGER_TYPE_SYM: + *resultPtr = Trees_NewNode(TREES_INTEGER_TYPE, NULL, NULL); + break; + case REAL_TYPE_SYM: + *resultPtr = Trees_NewNode(TREES_REAL_TYPE, NULL, NULL); + break; + case BYTE_TYPE_SYM: + *resultPtr = Trees_NewNode(TREES_BYTE_TYPE, NULL, NULL); + break; + case SET_TYPE_SYM: + *resultPtr = Trees_NewNode(TREES_SET_TYPE, NULL, NULL); + break; + case ARRAY_SYM: + { + Trees_Node length, elemType; + + ReadSExp(0, file, &length); + if (length != NULL) { + if (Trees_Symbol(length) == INTEGER) { + if (Trees_Integer(length) < 0) { + PrintImportErrorPrefix(); + fprintf(stderr, "error: negative array length: %" OBNC_INT_MOD "d\n", Trees_Integer(length)); + exit(EXIT_FAILURE); + } + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: non-integer array length read\n"); + exit(EXIT_FAILURE); + } + } + ReadSExp(0, file, &elemType); + *resultPtr = Types_NewArray(length, elemType); + } + break; + case RECORD_SYM: + ReadRecord(isRoot, file, resultPtr); + break; + case POINTER_SYM: + { + Trees_Node ptrBaseType; + + ReadSExp(0, file, &ptrBaseType); + *resultPtr = Types_NewPointer(ptrBaseType); + } + break; + case PROCEDURE_SYM: + { + Trees_Node resultType, par, params; + int ch; + + ReadSExp(0, file, &resultType); + + params = NULL; + fscanf(file, " "); + ch = fgetc(file); + if (ch == '(') { + while (! AtEndOfList(file)) { + ReadSExp(0, file, &par); + params = Trees_NewNode(TREES_IDENT_LIST, par, params); + } + Trees_ReverseList(¶ms); /*correct order*/ + fscanf(file, " "); + ch = fgetc(file); + if (ch != ')') { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected end of parameter list s-expression\n"); + exit(EXIT_FAILURE); + } + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected start of parameter list s-expression\n"); + exit(EXIT_FAILURE); + } + *resultPtr = Types_NewProcedure(params, resultType); + } + break; + default: + assert(0); + } + + assert(*resultPtr != NULL); +} + + +static void ReadSExp(int isRoot, FILE *file, Trees_Node *resultPtr) +{ + int symbol, n, ch; + + assert(resultPtr != NULL); + + *resultPtr = NULL; + fscanf(file, " "); + if (! feof(file)) { + ch = fgetc(file); + if (ch == '(') { + ch = fgetc(file); + if (ch != ')') { + ungetc(ch, file); + n = fscanf(file, "%d", &symbol); + if (n == 1) { + ReadSymbol(symbol, isRoot, file, resultPtr); + ch = fgetc(file); + if (ch != ')') { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected end of s-expression, read '%c'\n", ch); + exit(EXIT_FAILURE); + } + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: failed reading symbol\n"); + exit(EXIT_FAILURE); + } + } + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: expected start of s-expression, read '%c'\n", ch); + exit(EXIT_FAILURE); + } + } else { + PrintImportErrorPrefix(); + fprintf(stderr, "error: unexpected end of file\n"); + exit(EXIT_FAILURE); + } +} + + +static void ResolveTypesRec(Trees_Node node, Maps_Map symfileEntries) +{ + Trees_Node type, typeDef; + + if (node != NULL) { + type = NULL; + switch (Trees_Symbol(node)) { + case IDENT: + switch (Trees_Kind(node)) { + case TREES_TYPE_KIND: + case TREES_VARIABLE_KIND: + case TREES_FIELD_KIND: + case TREES_VALUE_PARAM_KIND: + case TREES_VAR_PARAM_KIND: + type = Trees_Type(node); + break; + } + break; + case ARRAY: + type = Types_ElementType(node); + break; + case RECORD: + type = Types_RecordBaseType(node); + break; + case POINTER: + type = Types_PointerBaseType(node); + break; + case PROCEDURE: + type = Types_ResultType(node); + break; + } + + if ((type != NULL) && (Trees_Symbol(type) == IDENT)) { + typeDef = Maps_At(Trees_Name(type), symfileEntries); + if (typeDef != NULL) { + switch (Trees_Symbol(node)) { + case IDENT: + Trees_SetType(typeDef, node); + break; + case ARRAY: + Types_SetElementType(typeDef, node); + break; + case RECORD: + Types_SetRecordBaseType(typeDef, node); + ResolveTypesRec(Types_Fields(node), symfileEntries); + break; + case POINTER: + Types_SetPointerBaseType(typeDef, node); + break; + case PROCEDURE: + Types_SetResultType(typeDef, node); + ResolveTypesRec(Types_Parameters(node), symfileEntries); + break; + default: + assert(0); + } + } else { + fprintf(stderr, "missing type name in symbol file: %s\n", Trees_Name(type)); + exit(EXIT_FAILURE); + } + } else if (Trees_Symbol(node) == IDENT) { + ResolveTypesRec(Trees_Type(node), symfileEntries); + } else { + ResolveTypesRec(Trees_Left(node), symfileEntries); + ResolveTypesRec(Trees_Right(node), symfileEntries); + } + } +} + + +static void ResolveTypes(const char identName[], void *identNode, void *symbolFileEntriesMap) +{ + Trees_Node ident = identNode; + Maps_Map symbolFileEntries = symbolFileEntriesMap; + + ResolveTypesRec(ident, symbolFileEntries); +} + + +static char *QualifiedName(const char qualifier[], const char name[]) +{ + int resultLen; + char *result; + + resultLen = strlen(qualifier) + strlen(".") + strlen(name) + strlen("\0"); + NEW_ARRAY(result, resultLen); + strcpy(result, qualifier); + strcat(result, "."); + strcat(result, name); + return result; +} + + +static const char *importModule, *importQualifier; + +static void SetQualifiers(const char identName[], void *identNode, void *data) +{ + const char *name; + int isReExportedType; + + name = Trees_Name(identNode); + isReExportedType = strchr(name, '.') != NULL; + if (! isReExportedType) { + Trees_SetName(QualifiedName(importQualifier, name), identNode); + Trees_SetUnaliasedName(QualifiedName(importModule, name), identNode); + } +} + + +void Table_Import(const char filename[], const char module[], const char qualifier[]) +{ + int ch; + Maps_Map symbolFileEntries; + Trees_Node ident, importEntries, p; + + importFilename = filename; + importFile = Files_Old(filename, FILES_READ); + + /*skip version line*/ + do { + ch = fgetc(importFile); + } while ((ch != EOF) && (ch != '\n')); + + /*read entries*/ + symbolFileEntries = Maps_New(); + importEntries = NULL; + fscanf(importFile, " "); + while (! feof(importFile)) { + ReadSExp(1, importFile, &ident); + if (ident != NULL) { + Maps_Put(Trees_Name(ident), ident, &symbolFileEntries); + if (Trees_Imported(ident)) { + importEntries = Trees_NewNode(TREES_NOSYM, ident, importEntries); + } + } else { + fprintf(stderr, "unexpected null entry in symbol file\n"); + exit(EXIT_FAILURE); + } + fscanf(importFile, " "); + } + + /*resolve types*/ + Maps_Apply(ResolveTypes, symbolFileEntries, symbolFileEntries); + + /*qualify identifiers*/ + importModule = module; + importQualifier = qualifier; + Maps_Apply(SetQualifiers, symbolFileEntries, NULL); + + /*import*/ + p = importEntries; + while (p != NULL) { + ident = Trees_Left(p); + Table_Put(ident); + p = Trees_Right(p); + } + + Files_Close(importFile); + importFile = NULL; + importFilename = NULL; +} + + +static int SFKind(Trees_Node ident) +{ + int result = -1; + + switch (Trees_Kind(ident)) { + case TREES_CONSTANT_KIND: + result = CONST_KIND; + break; + case TREES_TYPE_KIND: + result = TYPE_KIND; + break; + case TREES_VARIABLE_KIND: + result = VAR_KIND; + break; + case TREES_PROCEDURE_KIND: + result = PROCEDURE_KIND; + break; + case TREES_FIELD_KIND: + result = FIELD_KIND; + break; + case TREES_VALUE_PARAM_KIND: + result = VALUE_PARAM_KIND; + break; + case TREES_VAR_PARAM_KIND: + result = VAR_PARAM_KIND; + break; + default: + assert(0); + break; + } + return result; +} + + +static void Write(Trees_Node node, int isRoot, FILE *file, Maps_Map *indirectlyExportedTypes) +{ + if (node == NULL) { + fprintf(file, "()"); + } else { + switch (Trees_Symbol(node)) { + case IDENT: + fprintf(file, "(%d %s %d", IDENT_SYM, Trees_UnaliasedName(node), SFKind(node)); + if ((SFKind(node) == TYPE_KIND) && isRoot) { + fprintf(file, " %d", Trees_Exported(node)); + } + switch (SFKind(node)) { + case CONST_KIND: + fprintf(file, " "); + Write(Trees_Value(node), 0, file, indirectlyExportedTypes); + break; + case TYPE_KIND: + if (isRoot) { + fprintf(file, " "); + Write(Trees_Type(node), 0, file, indirectlyExportedTypes); + } else if (! Trees_Exported(node)) { + Maps_Put(Trees_Name(node), node, indirectlyExportedTypes); + } + break; + case VAR_KIND: + case PROCEDURE_KIND: + case FIELD_KIND: + case VALUE_PARAM_KIND: + case VAR_PARAM_KIND: + fprintf(file, " "); + Write(Trees_Type(node), 0, file, indirectlyExportedTypes); + break; + default: + assert(0); + } + fprintf(file, ")"); + break; + case FALSE: + fprintf(file, "(%d 0)", BOOLEAN_SYM); + break; + case TRUE: + fprintf(file, "(%d 1)", BOOLEAN_SYM); + break; + case TREES_CHAR_CONSTANT: + fprintf(file, "(%d %d)", CHAR_SYM, Trees_Char(node)); + break; + case INTEGER: + fprintf(file, "(%d %" OBNC_INT_MOD "d)", INTEGER_SYM, Trees_Integer(node)); + break; + case REAL: + fprintf(file, "(%d %.*" OBNC_REAL_MOD_W "g)", REAL_SYM, DBL_DIG, Trees_Real(node)); + break; + case STRING: + fprintf(file, "(%d \"%s\")", STRING_SYM, Trees_String(node)); + break; + case TREES_SET_CONSTANT: + fprintf(file, "(%d %" OBNC_INT_MOD "u)", SET_SYM, Trees_Set(node)); + break; + case TREES_BOOLEAN_TYPE: + fprintf(file, "(%d)", BOOLEAN_TYPE_SYM); + break; + case TREES_CHAR_TYPE: + fprintf(file, "(%d)", CHAR_TYPE_SYM); + break; + case TREES_INTEGER_TYPE: + fprintf(file, "(%d)", INTEGER_TYPE_SYM); + break; + case TREES_REAL_TYPE: + fprintf(file, "(%d)", REAL_TYPE_SYM); + break; + case TREES_BYTE_TYPE: + fprintf(file, "(%d)", BYTE_TYPE_SYM); + break; + case TREES_SET_TYPE: + fprintf(file, "(%d)", SET_TYPE_SYM); + break; + case ARRAY: + fprintf(file, "(%d ", ARRAY_SYM); + Write(Types_ArrayLength(node), 0, file, indirectlyExportedTypes); + fprintf(file, " "); + Write(Types_ElementType(node), 0, file, indirectlyExportedTypes); + fprintf(file, ")"); + break; + case RECORD: + { + Trees_Node fieldListSeq, fieldList, field; + int fieldListSeqEmpty, fieldListEmpty; + + fprintf(file, "(%d ", RECORD_SYM); + Write(Types_RecordBaseType(node), 0, file, indirectlyExportedTypes); + fieldListSeq = Types_Fields(node); + fprintf(file, " ("); + fieldListSeqEmpty = 1; + while (fieldListSeq != NULL) { + assert(Trees_Symbol(fieldListSeq) == TREES_FIELD_LIST_SEQUENCE); + fieldList = Trees_Left(fieldListSeq); + fieldListEmpty = 1; + while (fieldList != NULL) { + assert(Trees_Symbol(fieldList) == TREES_IDENT_LIST); + field = Trees_Left(fieldList); + if (Trees_Exported(field)) { + if (fieldListEmpty) { + if (! fieldListSeqEmpty) { + fprintf(file, " "); + } + fprintf(file, "("); + } else { + fprintf(file, " "); + } + Write(field, 0, file, indirectlyExportedTypes); + fieldListSeqEmpty = 0; + fieldListEmpty = 0; + } + fieldList = Trees_Right(fieldList); + } + if (! fieldListEmpty) { + fprintf(file, ")"); + } + fieldListSeq = Trees_Right(fieldListSeq); + } + fprintf(file, "))"); + } + break; + case POINTER: + fprintf(file, "(%d ", POINTER_SYM); + Write(Types_PointerBaseType(node), 0, file, indirectlyExportedTypes); + fprintf(file, ")"); + break; + case PROCEDURE: + { + Trees_Node params = Types_Parameters(node); + + fprintf(file, "(%d ", PROCEDURE_SYM); + Write(Types_ResultType(node), 0, file, indirectlyExportedTypes); + fprintf(file, " ("); + while (params != NULL) { + Write(Trees_Left(params), 0, file, indirectlyExportedTypes); + if (Trees_Right(params) != NULL) { + fprintf(file, " "); + } + params = Trees_Right(params); + } + fprintf(file, "))"); + } + break; + default: + assert(0); + } + } +} + + +static void WriteExportedSymbol(const char identName[], void *identNode, void *indirectlyExportedTypesMapPtr) +{ + Trees_Node ident = identNode; + Maps_Map *indirectlyExportedTypes = indirectlyExportedTypesMapPtr; + + assert(Trees_Symbol(ident) == IDENT); + if (Trees_Exported(ident)) { + assert(indirectlyExportedTypesMapPtr != NULL); + Write(ident, 1, exportFile, indirectlyExportedTypes); + Maps_Put(Trees_Name(ident), NULL, &writtenSymbols); + fputc('\n', exportFile); + } +} + + +static void WriteSymbol(const char identName[], void *identNode, void *indirectlyExportedTypesMapPtr) +{ + Trees_Node ident = identNode; + Maps_Map *indirectlyExportedTypes = indirectlyExportedTypesMapPtr; + + assert(Trees_Symbol(ident) == IDENT); + if (! Maps_HasKey(Trees_Name(ident), writtenSymbols)) { + Write(ident, 1, exportFile, indirectlyExportedTypes); + Maps_Put(Trees_Name(ident), NULL, &writtenSymbols); + fputc('\n', exportFile); + } +} + + +void Table_Export(const char filename[]) +{ + Maps_Map indirectlyExportedTypes, nextIndirectlyExportedTypes; + int i; + + assert(filename != NULL); + + exportFilename = filename; + exportFile = Files_New(filename); + if (strcmp(CONFIG_VERSION, "") != 0) { + fprintf(exportFile, "%s", CONFIG_VERSION); + } + fputc('\n', exportFile); + + writtenSymbols = Maps_New(); + + indirectlyExportedTypes = Maps_New(); + Maps_Apply(WriteExportedSymbol, globalScope->symbols, &indirectlyExportedTypes); + i = 0; + while (! Maps_IsEmpty(indirectlyExportedTypes) && (i < 10)) { + nextIndirectlyExportedTypes = Maps_New(); + Maps_Apply(WriteSymbol, indirectlyExportedTypes, &nextIndirectlyExportedTypes); + indirectlyExportedTypes = nextIndirectlyExportedTypes; + i++; + } + if (i < 10) { + Files_Close(exportFile); + exportFile = NULL; + exportFilename = NULL; + } else { + fprintf(stderr, "too many levels of indirectly exported types when exporting symbols to %s\n", exportFilename); + exit(EXIT_FAILURE); + } +} diff --git a/src/Table.h b/src/Table.h new file mode 100644 index 0000000..00e43e9 --- /dev/null +++ b/src/Table.h @@ -0,0 +1,42 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef TABLE_H +#define TABLE_H + +#include "Trees.h" +#include + +void Table_Init(void); + +void Table_Put(Trees_Node identNode); + +Trees_Node Table_At(const char name[]); + +int Table_LocallyDeclared(const char name[]); + +void Table_OpenScope(void); + +void Table_CloseScope(void); + +int Table_ScopeLocal(void); + +void Table_Import(const char filename[], const char module[], const char qualifier[]); + +void Table_Export(const char filename[]); + +#endif diff --git a/src/TableTest.c b/src/TableTest.c new file mode 100644 index 0000000..c4fcb67 --- /dev/null +++ b/src/TableTest.c @@ -0,0 +1,147 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Files.h" +#include "StackTrace.h" +#include "Table.h" +#include "Trees.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" /*needed by YYSTYPE in y.tab.h*/ +#include "y.tab.h" +#include /*POSIX*/ +#include +#include +#include +#include + +static char symfilename[PATH_MAX + 1] = ""; + +static void DeleteSymbolFile(void) +{ + int error; + + if (strcmp(symfilename, "") != 0) { + error = remove(symfilename); + if (error) { + perror("error: remove failed: "); + exit(EXIT_FAILURE); + } + } +} + + +static void Test(void) +{ + Trees_Node symbol, value, result; + + Table_Init(); + assert(! Table_ScopeLocal()); + + symbol = Trees_NewIdent("a"); + Trees_SetKind(TREES_CONSTANT_KIND, symbol); + Trees_SetValue(Trees_NewInteger(37), symbol); + Trees_SetExported(symbol); + Table_Put(symbol); + + symbol = Trees_NewIdent("X"); + Trees_SetKind(TREES_PROCEDURE_KIND, symbol); + Trees_SetExported(symbol); + Table_Put(symbol); + + Table_OpenScope(); + assert(Table_ScopeLocal()); + Table_CloseScope(); + + symbol = Trees_NewIdent("Y"); + Trees_SetKind(TREES_PROCEDURE_KIND, symbol); + Table_Put(symbol); + assert(Table_LocallyDeclared("Y")); + + Table_OpenScope(); + assert(! Table_LocallyDeclared("Y")); + + symbol = Trees_NewIdent("X"); + Trees_SetKind(TREES_VARIABLE_KIND, symbol); + Trees_SetLocal(symbol); + Table_Put(symbol); + + result = Table_At("X"); + assert(result != NULL); + assert(Trees_Kind(result) == TREES_VARIABLE_KIND); + assert(! Trees_Exported(result)); + + Table_OpenScope(); + result = Table_At("X"); /*shall return global object*/ + assert(result != NULL); + assert(Trees_Kind(result) == TREES_PROCEDURE_KIND); + assert(Trees_Exported(result)); + Table_CloseScope(); + Table_CloseScope(); + + result = Table_At("X"); + assert(result != NULL); + assert(Trees_Kind(result) == TREES_PROCEDURE_KIND); + assert(Trees_Exported(result)); + + result = Table_At("foo"); + assert(result == NULL); + + /*export symbols*/ + Table_Export(symfilename); + + /*clear table*/ + Table_Init(); + + /*import symbols*/ + Table_Import(symfilename, "Test", "Test"); + + result = Table_At("Test.a"); + assert(result != NULL); + assert(Trees_Kind(result) == TREES_CONSTANT_KIND); + value = Trees_Value(result); + assert(Trees_Symbol(value) == INTEGER); + assert(Trees_Integer(value) == 37); + + result = Table_At("Test.X"); + assert(result != NULL); + assert(Trees_Kind(result) == TREES_PROCEDURE_KIND); +} + + +int main(void) +{ + int error; + const char *tmpdir; + + Util_Init(); + StackTrace_Init(); + + tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) { + tmpdir = "/tmp"; + } + sprintf(symfilename, "%s/TableTest.%d", tmpdir, getpid()); + + error = atexit(DeleteSymbolFile); + if (error) { + fprintf(stderr, "error: atexit failed with error: %d\n", error); + exit(EXIT_FAILURE); + } + + Test(); + return 0; +} diff --git a/src/Trees.c b/src/Trees.c new file mode 100644 index 0000000..0baa417 --- /dev/null +++ b/src/Trees.c @@ -0,0 +1,745 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Trees.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" +#include "y.tab.h" +#include +#include +#include +#include +#include +#include + +/*value types*/ +enum { + NO_VALUE, + IDENT_VALUE, + INTEGER_VALUE, + REAL_VALUE, + STRING_VALUE, + CHAR_VALUE, + SET_VALUE, + VALUE_TYPE_COUNT +}; + +struct Trees_NodeDesc { + int valueType; + int symbol; + int marked; + union { + struct { + const char *name, *unaliasedName; + int kind; + unsigned int local:1, imported:1, exported:1, internal:1; + Trees_Node value; + } ident; + OBNC_LONGI int integer; + OBNC_LONGR double real; + char *string; + char ch; + OBNC_LONGI unsigned int set; + } value; + Trees_Node type; + Trees_Node left, right; +}; + +Trees_Node Trees_NewNode(int symbol, Trees_Node left, Trees_Node right) +{ + Trees_Node result; + + NEW(result); + result->valueType = NO_VALUE; + result->symbol = symbol; + result->marked = 0; + result->type = NULL; + result->left = left; + result->right = right; + return result; +} + + +Trees_Node Trees_NewLeaf(int symbol) +{ + return Trees_NewNode(symbol, NULL, NULL); +} + + +int Trees_IsLeaf(Trees_Node node) +{ + assert(node != NULL); + + return (node->left == NULL) && (node->right == NULL); +} + + +void Trees_SetType(Trees_Node type, Trees_Node node) +{ + assert(node != NULL); + + node->type = type; +} + + +Trees_Node Trees_Type(Trees_Node node) +{ + assert(node != NULL); + + return node->type; +} + + +int Trees_Symbol(Trees_Node node) +{ + assert(node != NULL); + + return node->symbol; +} + + +void Trees_SetLeft(Trees_Node newLeft, Trees_Node tree) +{ + assert(tree != NULL); + tree->left = newLeft; +} + + +void Trees_SetRight(Trees_Node newRight, Trees_Node tree) +{ + assert(tree != NULL); + tree->right = newRight; +} + + +Trees_Node Trees_Left(Trees_Node tree) +{ + assert(tree != NULL); + + return tree->left; +} + + +Trees_Node Trees_Right(Trees_Node tree) +{ + assert(tree != NULL); + + return tree->right; +} + + +void Trees_ReverseList(Trees_Node *list) +{ + Trees_Node current, previous, next; + + current = *list; + previous = NULL; + while (current != NULL) { + next = current->right; /*save next node*/ + current->right = previous; /*reverse pointer*/ + previous = current; /*save current node*/ + current = next; /*advance current*/ + } + *list = previous; +} + + +static void PrintLabel(int symbol) +{ + if (symbol < 128) { + putchar(symbol); + } else { + switch (symbol) { + case ARRAY: + printf("array"); + break; + case BECOMES: + printf(":="); + break; + case BY: + printf("BY"); + break; + case CASE: + printf("CASE"); + break; + case DO: + printf("DO"); + break; + case DOTDOT: + printf(".."); + break; + case DIV: + printf("DIV"); + break; + case FALSE: + printf("FALSE"); + break; + case FOR: + printf("FOR"); + break; + case GE: + printf(">="); + break; + case ELSE: + printf("ELSE"); + break; + case ELSIF: + printf("ELSIF"); + break; + case IN: + printf("IN"); + break; + case IF: + printf("IF"); + break; + case IS: + printf("IS"); + break; + case LE: + printf("<="); + break; + case MOD: + printf("MOD"); + break; + case MODULE: + printf("MODULE"); + break; + case NIL: + printf("NIL"); + break; + case OR: + printf("OR"); + break; + case POINTER: + printf("POINTER"); + break; + case RECORD: + printf("RECORD"); + break; + case REPEAT: + printf("REPEAT"); + break; + case THEN: + printf("THEN"); + break; + case TO: + printf("TO"); + break; + case TREES_NOSYM: + printf("(none)"); + break; + case TREES_ABS_PROC: + printf("ABS"); + break; + case TREES_ASR_PROC: + printf("ASR"); + break; + case TREES_ASSERT_PROC: + printf("ASSERT"); + break; + case TREES_BOOLEAN_TYPE: + printf("BooleanType"); + break; + case TREES_BYTE_TYPE: + printf("ByteType"); + break; + case TREES_CASE: + printf("case"); + break; + case TREES_CASE_LABEL_LIST: + printf("CaseLabelList"); + break; + case TREES_CASE_REP: + printf("CaseRep"); + break; + case TREES_CHAR_TYPE: + printf("CharType"); + break; + case TREES_CHR_PROC: + printf("CHR"); + break; + case TREES_DEC_PROC: + printf("DEC"); + break; + case TREES_DESIGNATOR: + printf("designator"); + break; + case TREES_EXCL_PROC: + printf("EXCL"); + break; + case TREES_EXP_LIST: + printf("ExpList"); + break; + case TREES_FIELD_LIST_SEQUENCE: + printf("FieldListSequence"); + break; + case TREES_FILE_POSITION: + printf("FilePosition"); + break; + case TREES_FLOOR_PROC: + printf("FLOOR"); + break; + case TREES_FLT_PROC: + printf("FLT"); + break; + case TREES_IDENT_LIST: + printf("IdentList"); + break; + case TREES_INC_PROC: + printf("INC"); + break; + case TREES_INCL_PROC: + printf("INCL"); + break; + case TREES_INTEGER_TYPE: + printf("IntegerType"); + break; + case TREES_LEN_PROC: + printf("LEN"); + break; + case TREES_LSL_PROC: + printf("LSL"); + break; + case TREES_NEW_PROC: + printf("NEW"); + break; + case TREES_NIL_TYPE: + printf("NilType"); + break; + case TREES_ODD_PROC: + printf("ODD"); + break; + case TREES_ORD_PROC: + printf("ORD"); + break; + case TREES_PACK_PROC: + printf("PACK"); + break; + case TREES_PROCEDURE_CALL: + printf("ProcedureCall"); + break; + case PROCEDURE: + printf("Procedure"); + break; + case TREES_RANGE_SET: + printf("RangeSet"); + break; + case TREES_REAL_TYPE: + printf("RealType"); + break; + case TREES_ROR_PROC: + printf("ROR"); + break; + case TREES_SET_TYPE: + printf("SetType"); + break; + case TREES_SINGLE_ELEMENT_SET: + printf("SingleElementSet"); + break; + case TREES_STATEMENT_SEQUENCE: + printf("StatementSequence"); + break; + case TREES_STRING_TYPE: + printf("StringType"); + break; + case TREES_UNPK_PROC: + printf("UNPK"); + break; + case TRUE: + printf("TRUE"); + break; + case WHILE: + printf("WHILE"); + break; + default: + fprintf(stderr, "error: no label for symbol: %d\n", symbol); + assert(0); + } + } +} + + +static void Indent(int level) +{ + int i; + + for (i = 0; i < level; i++) { + putchar('\t'); + } +} + +static void PrintRec(Trees_Node tree, int height) +{ + if (tree == NULL) { + puts("(nil)"); + } else { + Indent(height); + switch (tree->valueType) { + case NO_VALUE: + PrintLabel(tree->symbol); + putchar('\n'); + break; + case IDENT_VALUE: + printf("ident %s", tree->value.ident.name); + if (tree->value.ident.unaliasedName != NULL + && (strcmp(tree->value.ident.unaliasedName, tree->value.ident.name) != 0)) { + printf(" (%s)", tree->value.ident.unaliasedName); + } + /*printf(" (exp: %d, imp: %d)\n", tree->value.ident.exported, tree->value.ident.imported);*/ + putchar('\n'); + break; + case INTEGER_VALUE: + printf("%" OBNC_INT_MOD "d\n", tree->value.integer); + break; + case REAL_VALUE: + printf("%.*" OBNC_REAL_MOD_W "g\n", DBL_DIG, tree->value.real); + break; + case STRING_VALUE: + printf("\"%s\"\n", tree->value.string); + break; + case CHAR_VALUE: + printf("'%c'\n", tree->value.ch); + break; + case SET_VALUE: + printf("0x%" OBNC_INT_MOD "xu\n", tree->value.set); + break; + default: + assert(0); + } + if ((tree->left != NULL) && (tree->right != NULL)) { + PrintRec(tree->left, height + 1); + PrintRec(tree->right, height + 1); + } else if ((tree->left != NULL) && (tree->right == NULL)) { + PrintRec(tree->left, height + 1); + Indent(height + 1); + puts("(nil)"); + } else if ((tree->left == NULL) && (tree->right != NULL)) { + Indent(height + 1); + puts("(nil)"); + PrintRec(tree->right, height + 1); + } + } +} + + +void Trees_Print(Trees_Node tree) +{ + PrintRec(tree, 0); +} + + +/*Identifiers*/ + +Trees_Node Trees_NewIdent(const char name[]) +{ + Trees_Node result; + + assert(name != NULL); + + result = Trees_NewLeaf(IDENT); + result->valueType = IDENT_VALUE; + result->value.ident.name = name; + result->value.ident.unaliasedName = name; + result->value.ident.kind = TREES_UNSPECIFIED_KIND; + result->value.ident.local = 0; + result->value.ident.imported = 0; + result->value.ident.exported = 0; + result->value.ident.internal = 0; + result->value.ident.value = NULL; + return result; +} + + +const char *Trees_Name(Trees_Node node) +{ + assert(node != NULL); + assert(node->valueType == IDENT_VALUE); + + return node->value.ident.name; +} + + +void Trees_SetName(const char name[], Trees_Node identNode) +{ + assert(name != NULL); + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + identNode->value.ident.name = name; +} + + +const char *Trees_UnaliasedName(Trees_Node node) +{ + assert(node != NULL); + assert(node->valueType == IDENT_VALUE); + + return node->value.ident.unaliasedName; +} + + +void Trees_SetUnaliasedName(const char name[], Trees_Node identNode) +{ + assert(name != NULL); + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + identNode->value.ident.unaliasedName = name; +} + + +int Trees_Kind(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + return identNode->value.ident.kind; +} + + +void Trees_SetKind(int kind, Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + assert(kind >= 0); + assert(kind < TREES_KIND_COUNT); + + identNode->value.ident.kind = kind; +} + + +int Trees_Local(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + return identNode->value.ident.local; +} + + +void Trees_SetLocal(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + identNode->value.ident.local = 1; +} + + +int Trees_Imported(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + return identNode->value.ident.imported; +} + + +void Trees_SetImported(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + identNode->value.ident.imported = 1; +} + + +int Trees_Exported(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + return identNode->value.ident.exported; +} + + +void Trees_SetExported(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + identNode->value.ident.exported = 1; +} + + +int Trees_Internal(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + return identNode->value.ident.internal; +} + + +void Trees_SetInternal(Trees_Node identNode) +{ + assert(identNode != NULL); + assert(identNode->valueType == IDENT_VALUE); + + identNode->value.ident.internal = 1; +} + + +Trees_Node Trees_Value(Trees_Node node) +{ + Trees_Node value; + + assert(node != NULL); + assert(node->valueType == IDENT_VALUE); + assert(node->value.ident.kind == TREES_CONSTANT_KIND); + + value = node->value.ident.value; + if (Trees_Symbol(value) == STRING) { + /*string constants are sometimes put in char context so we cannot reuse the same node*/ + value = Trees_NewString(Trees_String(value)); + } + return value; +} + + +void Trees_SetValue(Trees_Node valueNode, Trees_Node constNode) +{ + assert(valueNode != NULL); + assert((valueNode->valueType != NO_VALUE) + || (valueNode->symbol == NIL) + || (valueNode->symbol == TRUE) + || (valueNode->symbol == FALSE)); + assert(valueNode->valueType != IDENT_VALUE); + assert(constNode != NULL); + assert(constNode->valueType == IDENT_VALUE); + assert(constNode->value.ident.kind == TREES_CONSTANT_KIND); + + constNode->value.ident.value = valueNode; +} + + +/*Integers*/ + +Trees_Node Trees_NewInteger(OBNC_LONGI int value) +{ + Trees_Node result; + + result = Trees_NewLeaf(INTEGER); + result->valueType = INTEGER_VALUE; + result->value.integer = value; + result->type = Trees_NewLeaf(TREES_INTEGER_TYPE); + return result; +} + + +OBNC_LONGI int Trees_Integer(Trees_Node integerNode) +{ + assert(integerNode != NULL); + assert(integerNode->valueType == INTEGER_VALUE); + + return integerNode->value.integer; +} + + +/*Real numbers*/ + +Trees_Node Trees_NewReal(OBNC_LONGR double value) +{ + Trees_Node result; + + result = Trees_NewLeaf(REAL); + result->valueType = REAL_VALUE; + result->value.real = value; + result->type = Trees_NewLeaf(TREES_REAL_TYPE); + return result; +} + + +OBNC_LONGR double Trees_Real(Trees_Node realNode) +{ + assert(realNode != NULL); + assert(realNode->valueType == REAL_VALUE); + + return realNode->value.real; +} + + +/*Strings*/ + +Trees_Node Trees_NewString(const char string[]) +{ + Trees_Node result; + + assert(string != NULL); + + result = Trees_NewLeaf(STRING); + result->valueType = STRING_VALUE; + NEW_ARRAY(result->value.string, strlen(string) + 1); + strcpy(result->value.string, string); + result->type = Trees_NewNode(TREES_STRING_TYPE, Trees_NewInteger(strlen(string)), NULL); + return result; +} + + +const char *Trees_String(Trees_Node stringNode) +{ + assert(stringNode != NULL); + assert(stringNode->valueType == STRING_VALUE); + + return stringNode->value.string; +} + + +/*Characters*/ + +Trees_Node Trees_NewChar(char value) +{ + Trees_Node result; + + result = Trees_NewLeaf(TREES_CHAR_CONSTANT); + result->valueType = CHAR_VALUE; + result->value.ch = value; + result->type = Trees_NewLeaf(TREES_CHAR_TYPE); + return result; +} + + +char Trees_Char(Trees_Node charNode) +{ + assert(charNode != NULL); + assert(charNode->valueType == CHAR_VALUE); + + return charNode->value.ch; +} + + +/*Set constants*/ + +Trees_Node Trees_NewSet(OBNC_LONGI unsigned int value) +{ + Trees_Node result; + + result = Trees_NewLeaf(TREES_SET_CONSTANT); + result->valueType = SET_VALUE; + result->value.set = value; + result->type = Trees_NewLeaf(TREES_SET_TYPE); + return result; +} + + +OBNC_LONGI unsigned int Trees_Set(Trees_Node setNode) +{ + assert(setNode != NULL); + assert(setNode->valueType == SET_VALUE); + + return setNode->value.set; +} diff --git a/src/Trees.h b/src/Trees.h new file mode 100644 index 0000000..ec7c0a0 --- /dev/null +++ b/src/Trees.h @@ -0,0 +1,179 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef TREES_H +#define TREES_H + +#include "../lib/obnc/OBNC.h" +#include + +/*node symbols*/ +enum { + TREES_NOSYM = 1000, /*larger than largest terminal symbol*/ + + TREES_CHAR_CONSTANT, + TREES_SET_CONSTANT, + + TREES_BOOLEAN_TYPE, + TREES_CHAR_TYPE, + TREES_STRING_TYPE, + TREES_INTEGER_TYPE, + TREES_REAL_TYPE, + TREES_SET_TYPE, + TREES_BYTE_TYPE, + TREES_NIL_TYPE, + + TREES_ABS_PROC, + TREES_ASR_PROC, + TREES_ASSERT_PROC, + TREES_CHR_PROC, + TREES_DEC_PROC, + TREES_EXCL_PROC, + TREES_FLOOR_PROC, + TREES_FLT_PROC, + TREES_INC_PROC, + TREES_INCL_PROC, + TREES_LEN_PROC, + TREES_LSL_PROC, + TREES_NEW_PROC, + TREES_ODD_PROC, + TREES_ORD_PROC, + TREES_PACK_PROC, + TREES_ROR_PROC, + TREES_UNPK_PROC, + + TREES_CASE, + TREES_CASE_LABEL_LIST, + TREES_CASE_REP, + TREES_DESIGNATOR, + TREES_EXP_LIST, + TREES_FIELD_LIST_SEQUENCE, + TREES_IDENT_LIST, + TREES_PROCEDURE_CALL, + TREES_RANGE_SET, + TREES_SINGLE_ELEMENT_SET, + TREES_STATEMENT_SEQUENCE, + TREES_FILE_POSITION, + + TREES_SYMBOL_END +}; + +/*identifier kinds*/ +enum { + TREES_UNSPECIFIED_KIND, + TREES_QUALIFIER_KIND, + TREES_CONSTANT_KIND, + TREES_TYPE_KIND, + TREES_FIELD_KIND, + TREES_VARIABLE_KIND, + TREES_PROCEDURE_KIND, + TREES_VALUE_PARAM_KIND, + TREES_VAR_PARAM_KIND, + TREES_KIND_COUNT +}; + +typedef struct Trees_NodeDesc *Trees_Node; + +Trees_Node Trees_NewNode(int symbol, Trees_Node left, Trees_Node right); + +Trees_Node Trees_NewLeaf(int symbol); + +int Trees_IsLeaf(Trees_Node node); + +void Trees_SetType(Trees_Node type, Trees_Node ident); + +Trees_Node Trees_Type(Trees_Node node); + +int Trees_Symbol(Trees_Node node); + +void Trees_SetLeft(Trees_Node newLeft, Trees_Node tree); + +void Trees_SetRight(Trees_Node newRight, Trees_Node tree); + +Trees_Node Trees_Left(Trees_Node tree); + +Trees_Node Trees_Right(Trees_Node tree); + +void Trees_ReverseList(Trees_Node *list); + +void Trees_Print(Trees_Node tree); + + +/*Identifiers*/ + +Trees_Node Trees_NewIdent(const char name[]); + +const char *Trees_Name(Trees_Node ident); +void Trees_SetName(const char name[], Trees_Node ident); + +const char *Trees_UnaliasedName(Trees_Node ident); +void Trees_SetUnaliasedName(const char name[], Trees_Node ident); + +int Trees_Kind(Trees_Node ident); +void Trees_SetKind(int kind, Trees_Node ident); + +int Trees_Local(Trees_Node ident); +void Trees_SetLocal(Trees_Node ident); + +int Trees_Imported(Trees_Node ident); +void Trees_SetImported(Trees_Node ident); + +int Trees_Exported(Trees_Node ident); +void Trees_SetExported(Trees_Node ident); + +int Trees_Internal(Trees_Node ident); +void Trees_SetInternal(Trees_Node ident); + +Trees_Node Trees_Value(Trees_Node constIdent); +void Trees_SetValue(Trees_Node value, Trees_Node constIdent); + + +/*Integers*/ + +Trees_Node Trees_NewInteger(OBNC_LONGI int value); + +OBNC_LONGI int Trees_Integer(Trees_Node integerNode); + + +/*Real numbers*/ + +Trees_Node Trees_NewReal(OBNC_LONGR double value); + +OBNC_LONGR double Trees_Real(Trees_Node realNode); + + +/*Strings*/ + +Trees_Node Trees_NewString(const char string[]); + +const char *Trees_String(Trees_Node stringNode); + + +/*Characters*/ + +Trees_Node Trees_NewChar(char ch); + +char Trees_Char(Trees_Node charNode); + + +/*Set constants*/ + +Trees_Node Trees_NewSet(OBNC_LONGI unsigned int value); + +OBNC_LONGI unsigned int Trees_Set(Trees_Node setNode); + +#endif diff --git a/src/Types.c b/src/Types.c new file mode 100644 index 0000000..1c1b955 --- /dev/null +++ b/src/Types.c @@ -0,0 +1,770 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Types.h" +#include "Oberon.h" +#include "../lib/obnc/OBNC.h" +#include "y.tab.h" +#include +#include + +int Types_IsType(Trees_Node node) +{ + int result; + + assert(node != NULL); + + result = 0; + switch (Trees_Symbol(node)) { + case TREES_BOOLEAN_TYPE: + case TREES_CHAR_TYPE: + case TREES_INTEGER_TYPE: + case TREES_REAL_TYPE: + case TREES_BYTE_TYPE: + case TREES_SET_TYPE: + case TREES_STRING_TYPE: + case TREES_ABS_PROC: + case TREES_ASR_PROC: + case TREES_ASSERT_PROC: + case TREES_CHR_PROC: + case TREES_DEC_PROC: + case TREES_EXCL_PROC: + case TREES_FLOOR_PROC: + case TREES_FLT_PROC: + case TREES_INC_PROC: + case TREES_INCL_PROC: + case TREES_LEN_PROC: + case TREES_LSL_PROC: + case TREES_NEW_PROC: + case TREES_NIL_TYPE: + case TREES_ODD_PROC: + case TREES_ORD_PROC: + case TREES_PACK_PROC: + case TREES_ROR_PROC: + case TREES_UNPK_PROC: + case ARRAY: + case RECORD: + case POINTER: + case PROCEDURE: + result = 1; + break; + case IDENT: + result = Trees_Kind(node) == TREES_TYPE_KIND; + break; + } + return result; +} + + +int Types_IsBoolean(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Trees_Symbol(Types_Structure(type)) == TREES_BOOLEAN_TYPE; +} + + +int Types_IsChar(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Trees_Symbol(Types_Structure(type)) == TREES_CHAR_TYPE; +} + + +int Types_IsInteger(Trees_Node type) +{ + int sym; + + assert(Types_IsType(type)); + sym = Trees_Symbol(Types_Structure(type)); + return (sym == TREES_INTEGER_TYPE) || (sym == TREES_BYTE_TYPE); +} + + +int Types_IsReal(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Trees_Symbol(Types_Structure(type)) == TREES_REAL_TYPE; +} + + +int Types_IsByte(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Trees_Symbol(Types_Structure(type)) == TREES_BYTE_TYPE; +} + + +int Types_IsSet(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Trees_Symbol(Types_Structure(type)) == TREES_SET_TYPE; +} + + +int Types_IsString(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Trees_Symbol(type) == TREES_STRING_TYPE; +} + + +int Types_StringLength(Trees_Node stringType) +{ + assert(Types_IsString(stringType)); + + return Trees_Integer(Trees_Left(stringType)); +} + + +int Types_Basic(Trees_Node type) +{ + int basic; + + assert(Types_IsType(type)); + + basic = 0; + switch (Trees_Symbol(type)) { + case TREES_BOOLEAN_TYPE: + case TREES_CHAR_TYPE: + case TREES_INTEGER_TYPE: + case TREES_REAL_TYPE: + case TREES_BYTE_TYPE: + case TREES_SET_TYPE: + basic = 1; + break; + } + + return basic; +} + + +int Types_Scalar(Trees_Node type) +{ + assert(Types_IsType(type)); + + return ! Types_IsArray(type) && ! Types_IsRecord(type); +} + + +Trees_Node Types_Structure(Trees_Node type) +{ + Trees_Node result; + + assert(type != NULL); + assert(Types_IsType(type)); + + /*NOTE: the type of an unresolved type identifier may be NULL*/ + result = type; + while ((result != NULL) && (Trees_Symbol(result) == IDENT)) { + result = Trees_Type(result); + } + return result; +} + + +Trees_Node Types_UnaliasedIdent(Trees_Node type) +{ + Trees_Node result; + + assert(type != NULL); + assert(Trees_Symbol(type) == IDENT); + + result = type; + while (Trees_Symbol(Trees_Type(result)) == IDENT) { + result = Trees_Type(result); + } + return result; +} + + +int Types_IsArray(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Trees_Symbol(Types_Structure(type)) == ARRAY; +} + + +Trees_Node Types_NewArray(Trees_Node length, Trees_Node elemType) +{ + assert((length == NULL) || Types_IsInteger(Trees_Type(length))); + assert(Types_IsType(elemType)); + + return Trees_NewNode(ARRAY, length, elemType); +} + + +int Types_IsOpenArray(Trees_Node type) +{ + assert(Types_IsType(type)); + + return (Trees_Symbol(type) == ARRAY) && (Types_ArrayLength(type) == NULL); +} + + +int Types_IsCharacterArray(Trees_Node type) +{ + assert(Types_IsType(type)); + + return Types_IsArray(type) && Types_IsChar(Types_ElementType(type)); +} + + +Trees_Node Types_ArrayLength(Trees_Node arrayType) +{ + assert(Types_IsArray(arrayType)); + + return Trees_Left(Types_Structure(arrayType)); +} + + +Trees_Node Types_ElementType(Trees_Node arrayType) +{ + assert(Types_IsArray(arrayType)); + + return Trees_Right(Types_Structure(arrayType)); +} + + +void Types_SetElementType(Trees_Node elemType, Trees_Node arrayType) +{ + assert(Types_IsType(elemType)); + assert(Types_IsArray(arrayType)); + + Trees_SetRight(elemType, Types_Structure(arrayType)); +} + + +Trees_Node Types_NewRecord(Trees_Node recBaseType, Trees_Node fields) +{ + assert((recBaseType == NULL) || (Trees_Type(recBaseType) == NULL) + || Types_IsRecord(recBaseType) || Types_IsPointer(recBaseType)); + assert((fields == NULL) || (Trees_Symbol(fields) == TREES_FIELD_LIST_SEQUENCE)); + + return Trees_NewNode(RECORD, recBaseType, fields); +} + + +int Types_IsRecord(Trees_Node type) +{ + return Trees_Symbol(Types_Structure(type)) == RECORD; +} + + +Trees_Node Types_RecordBaseType(Trees_Node type) +{ + Trees_Node typeStruct, result, record; + + typeStruct = Types_Structure(type); + result = NULL; + switch (Trees_Symbol(typeStruct)) { + case RECORD: + result = Trees_Left(typeStruct); + break; + case POINTER: + record = Types_PointerBaseType(typeStruct); + result = Trees_Left(Types_Structure(record)); + break; + default: + assert(0); + } + return result; +} + + +void Types_SetRecordBaseType(Trees_Node recBaseType, Trees_Node recordType) +{ + assert(Types_IsRecord(recBaseType)); + assert(Types_IsRecord(recordType)); + + Trees_SetLeft(recBaseType, Types_Structure(recordType)); +} + + +Trees_Node Types_Fields(Trees_Node record) +{ + Trees_Node typeStruct; + + typeStruct = Types_Structure(record); + assert(Trees_Symbol(typeStruct) == RECORD); + return Trees_Right(typeStruct); +} + + +int Types_Extensible(Trees_Node type) +{ + int sym; + + sym = Trees_Symbol(Types_Structure(type)); + return (sym == RECORD) || (sym == POINTER); +} + + +int Types_Extends(Trees_Node baseType, Trees_Node extendedType) +{ + int result; + Trees_Node baseTypeRecord, intermediateType; + + assert(baseType != NULL); + assert(extendedType != NULL); + + if (Types_Same(Types_Descriptor(baseType), Types_Descriptor(extendedType))) { + result = 1; + } else { + baseTypeRecord = Types_Descriptor(baseType); + intermediateType = Types_RecordBaseType(extendedType); + while ((intermediateType != NULL) + && ! Types_Same(Types_Descriptor(intermediateType), baseTypeRecord)) { + intermediateType = Types_RecordBaseType(intermediateType); + } + + result = intermediateType != NULL; + } + return result; +} + + +int Types_ExtensionLevel(Trees_Node type) +{ + int n = -1; + + do { + n++; + type = Types_RecordBaseType(type); + } while (type != NULL); + return n; +} + + +Trees_Node Types_Descriptor(Trees_Node type) +{ + Trees_Node result, typeStruct; + + assert(type != NULL); + + result = NULL; + typeStruct = Types_Structure(type); + switch (Trees_Symbol(typeStruct)) { + case RECORD: + result = type; + break; + case POINTER: + result = Types_PointerBaseType(typeStruct); + break; + default: + assert(0); + } + assert(result != NULL); + return result; +} + + +void Types_GetFieldIdent(const char fieldName[], Trees_Node type, int varImported, Trees_Node *fieldIdent, Trees_Node *fieldBaseType) +{ + Trees_Node baseType, baseTypeDesc, fieldListSeq, identList, ident; + int imported; + + assert(Types_IsRecord(type) || Types_IsPointer(type)); + + *fieldIdent = NULL; + *fieldBaseType = NULL; + baseType = type; + imported = varImported; + do { + if (! imported && (Trees_Symbol(baseType) == IDENT) && Trees_Imported(baseType)) { + imported = 1; + } + baseTypeDesc = Types_Descriptor(baseType); + /*search current base type for field name*/ + fieldListSeq = Types_Fields(baseTypeDesc); + while ((fieldListSeq != NULL) && (*fieldIdent == NULL)) { + identList = Trees_Left(fieldListSeq); + do { + ident = Trees_Left(identList); + if ((! imported || Trees_Exported(ident)) && (strcmp(Trees_Name(ident), fieldName) == 0)) { + *fieldIdent = ident; + *fieldBaseType = baseType; + } + identList = Trees_Right(identList); + } while (identList != NULL); + fieldListSeq = Trees_Right(fieldListSeq); + } + + baseType = Types_RecordBaseType(baseTypeDesc); + } while ((baseType != NULL) && (*fieldIdent == NULL)); + + assert((*fieldIdent == NULL) || (*fieldBaseType != NULL)); +} + + +Trees_Node Types_NewPointer(Trees_Node ptrBaseType) +{ + assert((ptrBaseType == NULL) || (Trees_Type(ptrBaseType) == NULL) || Types_IsRecord(ptrBaseType)); + + return Trees_NewNode(POINTER, ptrBaseType, NULL); +} + + +int Types_IsPointer(Trees_Node type) +{ + return Trees_Symbol(Types_Structure(type)) == POINTER; +} + + +Trees_Node Types_PointerBaseType(Trees_Node ptrType) +{ + assert(Types_IsPointer(ptrType)); + + return Trees_Left(Types_Structure(ptrType)); +} + + +void Types_SetPointerBaseType(Trees_Node ptrBaseType, Trees_Node ptrType) +{ + assert(Types_IsRecord(ptrBaseType)); + assert(Types_IsPointer(ptrType)); + + Trees_SetLeft(ptrBaseType, Types_Structure(ptrType)); +} + + +Trees_Node Types_NewProcedure(Trees_Node fields, Trees_Node resultType) +{ + assert((fields == NULL) || (Trees_Symbol(fields) == TREES_IDENT_LIST)); + assert((resultType == NULL) || Types_IsType(resultType)); + + return Trees_NewNode(PROCEDURE, fields, resultType); +} + + +int Types_IsProcedure(Trees_Node type) +{ + Trees_Node typeStruct; + + assert(Types_IsType(type)); + + typeStruct = Types_Structure(type); + return (Trees_Symbol(typeStruct) == PROCEDURE) || Types_IsPredeclaredProcedure(typeStruct); +} + + +int Types_IsPredeclaredProcedure(Trees_Node type) +{ + int predeclared; + + assert(Types_IsType(type)); + + predeclared = 0; + switch (Trees_Symbol(Types_Structure(type))) { + case TREES_ABS_PROC: + case TREES_ASR_PROC: + case TREES_ASSERT_PROC: + case TREES_CHR_PROC: + case TREES_DEC_PROC: + case TREES_EXCL_PROC: + case TREES_FLOOR_PROC: + case TREES_FLT_PROC: + case TREES_INC_PROC: + case TREES_INCL_PROC: + case TREES_LEN_PROC: + case TREES_LSL_PROC: + case TREES_NEW_PROC: + case TREES_ODD_PROC: + case TREES_ORD_PROC: + case TREES_PACK_PROC: + case TREES_ROR_PROC: + case TREES_UNPK_PROC: + predeclared = 1; + break; + } + return predeclared; +} + + +Trees_Node Types_Parameters(Trees_Node procType) +{ + assert(Types_IsProcedure(procType)); + + return Trees_Left(Types_Structure(procType)); +} + + +Trees_Node Types_ResultType(Trees_Node procType) +{ + assert(Types_IsProcedure(procType)); + + return Trees_Right(Types_Structure(procType)); +} + + +void Types_SetResultType(Trees_Node resultType, Trees_Node procType) +{ + assert((resultType == NULL) || Types_IsType(resultType)); + assert(Types_IsProcedure(procType)); + + Trees_SetRight(resultType, Types_Structure(procType)); +} + + +int Types_Same(Trees_Node typeA, Trees_Node typeB) +{ + assert(Types_IsType(typeA)); + assert(Types_IsType(typeB)); + + return (Types_Structure(typeA) == Types_Structure(typeB)) + || (Types_Basic(typeA) && Types_Basic(typeB) && (Trees_Symbol(typeA) == Trees_Symbol(typeB))) + || ((Trees_Symbol(typeA) == IDENT) && (Trees_Symbol(typeB) == IDENT) + && (strcmp(Trees_UnaliasedName(typeA), Trees_UnaliasedName(typeB)) == 0)); +} + + +static int FormalParametersMatch(Trees_Node procTypeA, Trees_Node procTypeB); + +static int TypesEqual(Trees_Node typeA, Trees_Node typeB) +{ + return Types_Same(typeA, typeB) + || (Types_IsOpenArray(typeA) && Types_IsOpenArray(typeB) + && TypesEqual(Types_ElementType(typeA), Types_ElementType(typeB))) + || (Types_IsProcedure(typeA) && Types_IsProcedure(typeB) + && FormalParametersMatch(typeA, typeB)); +} + + +int Types_AssignmentCompatible(Trees_Node sourceExp, Trees_Node targetType) +{ + int result; + Trees_Node sourceType; + + assert(sourceExp != NULL); + assert(Types_IsType(targetType)); + + result = 0; + sourceType = Trees_Type(sourceExp); + if ((sourceType != NULL) && (Types_Same(sourceType, targetType))) { + result = ! Types_IsString(targetType); + } else { + switch (Trees_Symbol(Types_Structure(targetType))) { + case TREES_CHAR_TYPE: + result = Types_IsString(sourceType) && (Types_StringLength(sourceType) <= 1); + break; + case TREES_INTEGER_TYPE: + case TREES_BYTE_TYPE: + result = Types_IsInteger(sourceType); + break; + case ARRAY: + if (Types_IsString(sourceType)) { + result = Types_IsCharacterArray(targetType) + && (Types_IsOpenArray(targetType) + || (Types_StringLength(sourceType) < Trees_Integer(Types_ArrayLength(targetType)))); + } else if (Types_IsOpenArray(sourceType)) { + result = ! Types_IsOpenArray(targetType) + && Types_Same(Types_ElementType(sourceType), Types_ElementType(targetType)); + } + break; + case RECORD: + result = Types_IsRecord(sourceType) && Types_Extends(targetType, sourceType); + break; + case POINTER: + result = (Trees_Symbol(sourceExp) == NIL) + || (Types_IsPointer(sourceType) && Types_Extends(targetType, sourceType)); + break; + case PROCEDURE: + result = (Trees_Symbol(sourceExp) == NIL) + || ((Trees_Symbol(sourceExp) == TREES_DESIGNATOR) + && (Trees_Kind(Trees_Left(sourceExp)) == TREES_PROCEDURE_KIND) + && ! Types_IsPredeclaredProcedure(sourceType) + && FormalParametersMatch(sourceType, targetType)); + break; + } + } + return result; +} + + +int Types_ArrayCompatible(Trees_Node actualType, Trees_Node formalType) +{ + return Types_Same(formalType, actualType) + || (Types_IsOpenArray(formalType) && Types_IsArray(actualType) + && Types_ArrayCompatible(Types_ElementType(formalType), Types_ElementType(actualType))) + || (Types_IsCharacterArray(formalType) && Types_IsString(actualType)); +} + + +int Types_ExpressionCompatible(int operator, Trees_Node firstType, Trees_Node secondType) +{ + int firstTypeSym, result; + + firstTypeSym = Trees_Symbol(Types_Structure(firstType)); + result = 0; + switch (operator) { + case '+': + case '-': + switch (firstTypeSym) { + case TREES_INTEGER_TYPE: + case TREES_BYTE_TYPE: + result = (secondType == NULL) || Types_IsInteger(secondType); + break; + case TREES_REAL_TYPE: + case TREES_SET_TYPE: + result = (secondType == NULL) || Types_Same(firstType, secondType); + break; + } + break; + case '*': + switch (firstTypeSym) { + case TREES_INTEGER_TYPE: + case TREES_BYTE_TYPE: + result = Types_IsInteger(secondType); + break; + case TREES_REAL_TYPE: + case TREES_SET_TYPE: + result = Types_Same(firstType, secondType); + break; + } + break; + case '/': + switch (firstTypeSym) { + case TREES_REAL_TYPE: + case TREES_SET_TYPE: + result = Types_Same(firstType, secondType); + break; + } + break; + case DIV: + case MOD: + result = Types_IsInteger(firstType) && Types_IsInteger(secondType); + break; + case OR: + case '&': + result = (firstTypeSym == TREES_BOOLEAN_TYPE) && Types_Same(firstType, secondType); + break; + case '~': + result = firstTypeSym == TREES_BOOLEAN_TYPE; + break; + case '=': + case '#': + switch (firstTypeSym) { + case TREES_BOOLEAN_TYPE: + case TREES_REAL_TYPE: + case TREES_SET_TYPE: + result = Types_Same(firstType, secondType); + break; + case TREES_INTEGER_TYPE: + case TREES_BYTE_TYPE: + result = Types_IsInteger(secondType); + break; + case TREES_CHAR_TYPE: + result = Types_IsChar(secondType) + || (Types_IsString(secondType) && (Types_StringLength(secondType) <= 1)); + break; + case ARRAY: + result = Types_IsCharacterArray(firstType) + && (Types_IsCharacterArray(secondType) || Types_IsString(secondType)); + break; + case TREES_STRING_TYPE: + result = Types_IsCharacterArray(secondType) || Types_IsString(secondType) + || (Types_IsChar(secondType) && (Types_StringLength(firstType) <= 1)); + break; + case POINTER: + result = (Trees_Symbol(secondType) == TREES_NIL_TYPE) + || (Types_IsPointer(secondType) + && (Types_Extends(firstType, secondType) || Types_Extends(secondType, firstType))); + break; + case PROCEDURE: + result = (Trees_Symbol(secondType) == TREES_NIL_TYPE) + || Types_Same(firstType, secondType); + break; + case TREES_NIL_TYPE: + result = Types_IsPointer(secondType) || Types_IsProcedure(secondType); + break; + } + break; + case '<': + case LE: + case '>': + case GE: + switch (firstTypeSym) { + case TREES_INTEGER_TYPE: + case TREES_BYTE_TYPE: + result = Types_IsInteger(secondType); + break; + case TREES_CHAR_TYPE: + result = Types_IsChar(secondType) + || (Types_IsString(secondType) && (Types_StringLength(secondType) <= 1)); + break; + case TREES_REAL_TYPE: + result = Types_Same(firstType, secondType); + break; + case ARRAY: + result = Types_IsCharacterArray(firstType) + && (Types_IsCharacterArray(secondType) || Types_IsString(secondType)); + break; + case TREES_STRING_TYPE: + result = Types_IsCharacterArray(secondType) || Types_IsString(secondType) + || (Types_IsChar(secondType) && (Types_StringLength(firstType) <= 1)); + break; + } + break; + case IN: + result = Types_IsInteger(firstType) && Types_IsSet(secondType); + break; + case IS: + result = (Types_IsRecord(firstType) || Types_IsPointer(firstType)) + && (Types_IsRecord(secondType) || Types_IsPointer(secondType)) + && Types_Extends(firstType, secondType); + break; + } + return result; +} + + +static int FormalParametersMatch(Trees_Node procTypeA, Trees_Node procTypeB) +{ + Trees_Node resultTypeA, resultTypeB, paramListA, paramListB, paramA, paramB; + int match; + + assert(Types_IsProcedure(procTypeA)); + assert(Types_IsProcedure(procTypeB)); + + resultTypeA = Types_ResultType(procTypeA); + resultTypeB = Types_ResultType(procTypeB); + + match = 0; + if (((resultTypeA == NULL) && (resultTypeB == NULL)) + || ((resultTypeA != NULL) && (resultTypeB != NULL) + && Types_Same(resultTypeA, resultTypeB))) { + match = 1; + paramListA = Types_Parameters(procTypeA); + paramListB = Types_Parameters(procTypeB); + while (match && (paramListA != NULL) && (paramListB != NULL)) { + paramA = Trees_Left(paramListA); + paramB = Trees_Left(paramListB); + match = match && TypesEqual(Trees_Type(paramA), Trees_Type(paramB)) + && (Trees_Kind(paramA) == Trees_Kind(paramB)); + paramListA = Trees_Right(paramListA); + paramListB = Trees_Right(paramListB); + } + match = match && (paramListA == NULL) && (paramListB == NULL); + } + return match; +} diff --git a/src/Types.h b/src/Types.h new file mode 100644 index 0000000..c6816e0 --- /dev/null +++ b/src/Types.h @@ -0,0 +1,121 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef TYPES_H +#define TYPES_H + +#include "Trees.h" + +int Types_IsType(Trees_Node node); + +int Types_IsBoolean(Trees_Node type); + +int Types_IsChar(Trees_Node type); + +int Types_IsInteger(Trees_Node type); + +int Types_IsReal(Trees_Node type); + +int Types_IsByte(Trees_Node type); + +int Types_IsSet(Trees_Node type); + +int Types_IsString(Trees_Node type); + +int Types_StringLength(Trees_Node stringType); + +int Types_Basic(Trees_Node type); + +int Types_Scalar(Trees_Node type); + +Trees_Node Types_Structure(Trees_Node type); + +Trees_Node Types_UnaliasedIdent(Trees_Node type); + +/*arrays*/ + +Trees_Node Types_NewArray(Trees_Node length, Trees_Node elemType); + +int Types_IsArray(Trees_Node type); + +int Types_IsOpenArray(Trees_Node type); + +int Types_IsCharacterArray(Trees_Node type); + +Trees_Node Types_ArrayLength(Trees_Node arrayType); + +Trees_Node Types_ElementType(Trees_Node arrayType); + +void Types_SetElementType(Trees_Node elemType, Trees_Node arrayType); + +/*records*/ + +Trees_Node Types_NewRecord(Trees_Node recBaseType, Trees_Node fields); + +int Types_IsRecord(Trees_Node type); + +Trees_Node Types_RecordBaseType(Trees_Node type); + +void Types_SetRecordBaseType(Trees_Node recBaseType, Trees_Node recordType); + +Trees_Node Types_Fields(Trees_Node record); + +Trees_Node Types_Descriptor(Trees_Node recordOrPointerType); + +int Types_Extensible(Trees_Node type); + +int Types_Extends(Trees_Node baseType, Trees_Node extendedType); + +int Types_ExtensionLevel(Trees_Node type); + +void Types_GetFieldIdent(const char fieldName[], Trees_Node type, int varImported, Trees_Node *ident, Trees_Node *baseType); + +/*pointers*/ + +Trees_Node Types_NewPointer(Trees_Node ptrBaseType); + +int Types_IsPointer(Trees_Node type); + +Trees_Node Types_PointerBaseType(Trees_Node ptrType); + +void Types_SetPointerBaseType(Trees_Node ptrBaseType, Trees_Node ptrType); + +/*procedures*/ + +Trees_Node Types_NewProcedure(Trees_Node fields, Trees_Node resultType); + +int Types_IsProcedure(Trees_Node type); + +int Types_IsPredeclaredProcedure(Trees_Node type); + +Trees_Node Types_Parameters(Trees_Node procType); + +Trees_Node Types_ResultType(Trees_Node procType); + +void Types_SetResultType(Trees_Node resultType, Trees_Node procType); + +/*type compatibility*/ + +int Types_Same(Trees_Node typeA, Trees_Node typeB); + +int Types_AssignmentCompatible(Trees_Node sourceExp, Trees_Node targetType); + +int Types_ArrayCompatible(Trees_Node actualType, Trees_Node formalType); + +int Types_ExpressionCompatible(int operator, Trees_Node typeA, Trees_Node typeB); + +#endif diff --git a/src/Util.c b/src/Util.c new file mode 100644 index 0000000..4f2054b --- /dev/null +++ b/src/Util.c @@ -0,0 +1,29 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Util.h" +#include + +void Util_Init(void) +{ + static int initialized = 0; + + if (! initialized) { + GC_INIT(); + initialized = 1; + } +} diff --git a/src/Util.env b/src/Util.env new file mode 100644 index 0000000..6f6deba --- /dev/null +++ b/src/Util.env @@ -0,0 +1 @@ +LDLIBS=-lgc diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 0000000..7f5d1fc --- /dev/null +++ b/src/Util.h @@ -0,0 +1,40 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include +#include + +#define LEN(arr) ((int) (sizeof (arr) / sizeof (arr)[0])) + +#define NEW_ARRAY(ptr, n) \ + { \ + (ptr) = GC_MALLOC((n) * sizeof (ptr)[0]); \ + if ((ptr) == NULL) { \ + fprintf(stderr, "error: Memory exhausted\n"); \ + exit(EXIT_FAILURE); \ + } \ + } + +#define NEW(ptr) NEW_ARRAY((ptr), 1) + +void Util_Init(void); + +#endif diff --git a/src/UtilTest.c b/src/UtilTest.c new file mode 100644 index 0000000..321fa7d --- /dev/null +++ b/src/UtilTest.c @@ -0,0 +1,37 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Util.h" +#include +#include + +static void TestNewArray(void) +{ + int *a; + + NEW_ARRAY(a, 10); + a[9] = 37; + assert(a[9] == 37); +} + + +int main(void) +{ + Util_Init(); + TestNewArray(); + return 0; +} diff --git a/src/lex.yy.c b/src/lex.yy.c new file mode 100644 index 0000000..e0d2610 --- /dev/null +++ b/src/lex.yy.c @@ -0,0 +1,2044 @@ + +#line 3 "lex.yy.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 1 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +static void yyensure_buffer_stack (void ); +static void yy_load_buffer_state (void ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; + +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yynoreturn yy_fatal_error (yyconst char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 15 +#define YY_END_OF_BUFFER 16 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_acclist[53] = + { 0, + 16, 14, 15, 1, 14, 15, 2, 15, 14, 15, + 7, 14, 15, 7, 14, 15, 7, 14, 15, 8201, + 14, 15,16393, 7, 14, 15, 7, 14, 15, 7, + 14, 15, 8, 14, 15, 1, 11, 13, 4, 10, + 8201,16393, 8201,16393, 12, 3, 5, 6, 8, 8201, + 10, 10 + } ; + +static yyconst flex_int16_t yy_accept[38] = + { 0, + 1, 1, 1, 2, 4, 7, 9, 11, 14, 17, + 20, 24, 27, 30, 33, 36, 37, 37, 38, 39, + 40, 41, 43, 43, 45, 46, 47, 48, 49, 50, + 51, 52, 52, 52, 52, 53, 53 + } ; + +static yyconst YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 5, 1, 1, 5, 1, 6, + 5, 7, 8, 5, 8, 9, 5, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 11, 5, 12, + 13, 14, 1, 1, 15, 15, 15, 15, 16, 15, + 17, 18, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 19, 17, 17, + 5, 1, 5, 5, 1, 1, 17, 17, 17, 17, + + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 5, 5, 5, 5, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst YY_CHAR yy_meta[20] = + { 0, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, + 1, 1, 1, 1, 3, 3, 3, 3, 3 + } ; + +static yyconst flex_uint16_t yy_base[39] = + { 0, + 0, 0, 51, 52, 48, 52, 45, 52, 41, 38, + 11, 33, 32, 31, 0, 41, 38, 52, 52, 52, + 22, 0, 13, 31, 52, 52, 52, 52, 0, 52, + 18, 25, 28, 26, 15, 52, 38, 21 + } ; + +static yyconst flex_int16_t yy_def[39] = + { 0, + 36, 1, 36, 36, 36, 36, 37, 36, 36, 36, + 36, 36, 36, 36, 38, 36, 37, 36, 36, 36, + 36, 11, 11, 36, 36, 36, 36, 36, 38, 36, + 36, 36, 36, 36, 36, 0, 36, 36 + } ; + +static yyconst flex_uint16_t yy_nxt[72] = + { 0, + 4, 5, 6, 7, 8, 9, 8, 8, 10, 11, + 12, 13, 8, 14, 15, 15, 15, 15, 15, 21, + 22, 36, 23, 29, 35, 23, 23, 31, 24, 25, + 30, 31, 34, 32, 35, 35, 30, 32, 17, 33, + 17, 18, 16, 28, 27, 26, 20, 19, 18, 16, + 36, 3, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36 + } ; + +static yyconst flex_int16_t yy_chk[72] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, + 11, 23, 23, 38, 35, 11, 11, 31, 11, 11, + 21, 21, 32, 31, 32, 34, 33, 21, 37, 24, + 37, 17, 16, 14, 13, 12, 10, 9, 7, 5, + 3, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36 + } ; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +static yy_state_type *yy_state_buf=0, *yy_state_ptr=0; +static char *yy_full_match; +static int yy_lp; +static int yy_looking_for_trail_begin = 0; +static int yy_full_lp; +static int *yy_full_state; +#define YY_TRAILING_MASK 0x2000 +#define YY_TRAILING_HEAD_MASK 0x4000 +#define REJECT \ +{ \ +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ \ +yy_cp = (yy_full_match); /* restore poss. backed-over text */ \ +(yy_lp) = (yy_full_lp); /* restore orig. accepting pos. */ \ +(yy_state_ptr) = (yy_full_state); /* restore orig. state */ \ +yy_current_state = *(yy_state_ptr); /* restore curr. state */ \ +++(yy_lp); \ +goto find_rule; \ +} + +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "Oberon.l" +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ +#line 21 "Oberon.l" +#include "Oberon.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" +#include "Trees.h" /*needed by YYSTYPE in y.tab.h*/ +#include "y.tab.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static int KeywordToken(const char word[]); + +#line 529 "lex.yy.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (void ); + +int yyget_debug (void ); + +void yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE yyget_extra (void ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in (void ); + +void yyset_in (FILE * _in_str ); + +FILE *yyget_out (void ); + +void yyset_out (FILE * _out_str ); + + int yyget_leng (void ); + +char *yyget_text (void ); + +int yyget_lineno (void ); + +void yyset_lineno (int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput (int c,char *buf_ptr ); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + /* Create the reject buffer large enough to save one state per allowed character. */ + if ( ! (yy_state_buf) ) + (yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE ); + if ( ! (yy_state_buf) ) + YY_FATAL_ERROR( "out of dynamic memory in yylex()" ); + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { +#line 49 "Oberon.l" + + +#line 756 "lex.yy.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); + + (yy_state_ptr) = (yy_state_buf); + *(yy_state_ptr)++ = yy_current_state; + +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 37 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; + *(yy_state_ptr)++ = yy_current_state; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 52 ); + +yy_find_action: + yy_current_state = *--(yy_state_ptr); + (yy_lp) = yy_accept[yy_current_state]; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( (yy_lp) && (yy_lp) < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[(yy_lp)]; + if ( yy_act & YY_TRAILING_HEAD_MASK || + (yy_looking_for_trail_begin) ) + { + if ( yy_act == (yy_looking_for_trail_begin) ) + { + (yy_looking_for_trail_begin) = 0; + yy_act &= ~YY_TRAILING_HEAD_MASK; + break; + } + } + else if ( yy_act & YY_TRAILING_MASK ) + { + (yy_looking_for_trail_begin) = yy_act & ~YY_TRAILING_MASK; + (yy_looking_for_trail_begin) |= YY_TRAILING_HEAD_MASK; + } + else + { + (yy_full_match) = yy_cp; + (yy_full_state) = (yy_state_ptr); + (yy_full_lp) = (yy_lp); + break; + } + ++(yy_lp); + goto find_rule; + } + --yy_cp; + yy_current_state = *--(yy_state_ptr); + (yy_lp) = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +YY_RULE_SETUP +#line 51 "Oberon.l" + + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 53 "Oberon.l" +{ + yylineno++; +} + YY_BREAK +case 3: +YY_RULE_SETUP +#line 57 "Oberon.l" +return BECOMES; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 59 "Oberon.l" +return DOTDOT; + YY_BREAK +case 5: +YY_RULE_SETUP +#line 61 "Oberon.l" +return LE; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 63 "Oberon.l" +return GE; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 65 "Oberon.l" +return yytext[0]; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 67 "Oberon.l" +{ + int token; + char *lexeme; + + token = KeywordToken(yytext); + if (token < 0) { + token = IDENT; + NEW_ARRAY(lexeme, yyleng + 1); + strcpy(lexeme, yytext); + yylval.ident = lexeme; + } + return token; +} + YY_BREAK +case 9: +YY_RULE_SETUP +#line 81 "Oberon.l" +{ +#ifdef OBNC_CONFIG_USE_LONG_INT + const long int max = LONG_MAX; +#else + const int max = INT_MAX; +#endif + int base; + long lexeme; + + base = (yytext[yyleng - 1] == 'H')? 16: 10; + errno = 0; + lexeme = strtol(yytext, NULL, base); + if ((errno != 0) || (lexeme > max)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > %" OBNC_INT_MOD "d\n", strerror(ERANGE), yytext, max); + } + yylval.integer = (OBNC_LONGI int) lexeme; + return INTEGER; +} + YY_BREAK +case 10: +YY_RULE_SETUP +#line 101 "Oberon.l" +{ +#ifdef OBNC_CONFIG_USE_LONG_REAL + int n = sscanf(yytext, "%Lf", &yylval.real); + if (n != 1) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > %LG\n", strerror(ERANGE), yytext, LDBL_MAX); + } +#else + errno = 0; + yylval.real = strtod(yytext, NULL); + if (errno != 0) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > %G\n", strerror(ERANGE), yytext, DBL_MAX); + } +#endif + return REAL; +} + YY_BREAK +case 11: +YY_RULE_SETUP +#line 119 "Oberon.l" +{ + int lexemeLen; + char *lexeme; + + lexemeLen = yyleng - 1; + NEW_ARRAY(lexeme, lexemeLen); + memcpy(lexeme, yytext + 1, lexemeLen - 1); + lexeme[lexemeLen - 1] = '\0'; + yylval.string = lexeme; + return STRING; +} + YY_BREAK +case 12: +YY_RULE_SETUP +#line 131 "Oberon.l" +{ + long ordinalNumber; + char *lexeme; + + if (strcmp(yytext, "0X") == 0) { + ordinalNumber = 0; + } else { + errno = 0; + ordinalNumber = strtol(yytext, NULL, 16); + if ((errno != 0) || (ordinalNumber > UCHAR_MAX)) { + Oberon_PrintContext(); + fprintf(stderr, "warning: %s: %s > 0%XX\n", strerror(ERANGE), yytext, UCHAR_MAX); + } + } + NEW_ARRAY(lexeme, 2); + lexeme[0] = (char) ordinalNumber; + lexeme[1] = '\0'; + yylval.string = lexeme; + return STRING; +} + YY_BREAK +case 13: +YY_RULE_SETUP +#line 152 "Oberon.l" +{ + int level, ch; + + level = 1; + do { + ch = input(); + switch (ch) { + case '(': + ch = input(); + if (ch == '*') { + level++; + } else { + unput(ch); + } + break; + case '*': + ch = input(); + if (ch == ')') { + level--; + } else { + unput(ch); + } + break; + case '\n': + yylineno++; + break; + } + } while ((level > 0) && (ch != EOF)); + + if (level > 0) { + Oberon_PrintContext(); + fprintf(stderr, "error: unterminated comment\n"); + exit(EXIT_FAILURE); + } +} + YY_BREAK +case 14: +YY_RULE_SETUP +#line 188 "Oberon.l" +{ + if (isprint(yytext[0])) { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected character: %c\n", yytext[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected character: %02X (hex)\n", yytext[0]); + } + return -1; +} + YY_BREAK +case 15: +YY_RULE_SETUP +#line 199 "Oberon.l" +ECHO; + YY_BREAK +#line 1032 "lex.yy.c" + case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + + (yy_state_ptr) = (yy_state_buf); + *(yy_state_ptr)++ = yy_current_state; + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 37 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; + *(yy_state_ptr)++ = yy_current_state; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + + YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 37 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; + yy_is_jam = (yy_current_state == 36); + if ( ! yy_is_jam ) + *(yy_state_ptr)++ = yy_current_state; + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp ) +{ + char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + int number_to_move = (yy_n_chars) + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = (yy_size_t)size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ); + + yyfree((void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 1; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) +{ + + return yy_scan_bytes(yystr,(int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + + (yy_state_buf) = 0; + (yy_state_ptr) = 0; + (yy_full_match) = 0; + (yy_lp) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + yyfree ( (yy_state_buf) ); + (yy_state_buf) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 199 "Oberon.l" + + + +static int Cmp(const void *word, const void *keywordPtr) +{ + return strcmp((char *) word, * (char **) keywordPtr); +} + + +static int KeywordToken(const char word[]) +{ + static const char *keywords[] = {"ARRAY", "BEGIN", "BY", "CASE", "CONST", "DIV", "DO", "ELSE", "ELSIF", "END", "FALSE", "FOR", "IF", "IMPORT", "IN", "IS", "MOD", "MODULE", "NIL", "OF", "OR", "POINTER", "PROCEDURE", "RECORD", "REPEAT", "RETURN", "THEN", "TO", "TRUE", "TYPE", "UNTIL", "VAR", "WHILE"}; + + static int keywordTokens[] = {ARRAY, BEGIN_, BY, CASE, CONST, DIV, DO, ELSE, ELSIF, END, FALSE, FOR, IF, IMPORT, IN, IS, MOD, MODULE, NIL, OF, OR, POINTER, PROCEDURE, RECORD, REPEAT, RETURN, THEN, TO, TRUE, TYPE, UNTIL, VAR, WHILE}; + + const char **keywordPtr; + int pos, token; + + keywordPtr = bsearch(word, keywords, LEN(keywords), sizeof keywords[0], Cmp); + if (keywordPtr != NULL) { + pos = keywordPtr - keywords; + assert(pos >= 0); + assert(pos < LEN(keywordTokens)); + token = keywordTokens[pos]; + } else { + token = -1; + } + return token; +} + + +int yywrap(void) +{ + const int done = 1; + + return done; +} + diff --git a/src/lex.yy.h b/src/lex.yy.h new file mode 100644 index 0000000..3c08f6d --- /dev/null +++ b/src/lex.yy.h @@ -0,0 +1,321 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 6 "lex.yy.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 1 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart (FILE *input_file ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); +void yy_delete_buffer (YY_BUFFER_STATE b ); +void yy_flush_buffer (YY_BUFFER_STATE b ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state (void ); + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + +void *yyalloc (yy_size_t ); +void *yyrealloc (void *,yy_size_t ); +void yyfree (void * ); + +/* Begin user sect3 */ + +extern int yylineno; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (void ); + +int yyget_debug (void ); + +void yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE yyget_extra (void ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in (void ); + +void yyset_in (FILE * _in_str ); + +FILE *yyget_out (void ); + +void yyset_out (FILE * _out_str ); + + int yyget_leng (void ); + +char *yyget_text (void ); + +int yyget_lineno (void ); + +void yyset_lineno (int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (void ); +#else +extern int yywrap (void ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#line 199 "Oberon.l" + + +#line 320 "lex.yy.h" +#undef yyIN_HEADER +#endif /* yyHEADER_H */ diff --git a/src/lex.yyTest.c b/src/lex.yyTest.c new file mode 100644 index 0000000..9a36eea --- /dev/null +++ b/src/lex.yyTest.c @@ -0,0 +1,175 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Files.h" +#include "lex.yy.h" +#include "Trees.h" /*symbol type in y.tab.h needs tree node declaration*/ +#include "Util.h" +#include "y.tab.h" +#include +#include +#include + +static FILE *inputFile; + +static struct { int token; const char *value; } expectedOutput[] = { + {IDENT, "x"}, + {IDENT, "scan"}, + {IDENT, "Oberon"}, + {IDENT, "GetSymbol"}, + {IDENT, "firstLetter"}, + {INTEGER, "2147483647"}, + {INTEGER, "256"}, + {REAL, "340282346638528859811704183484516925440.0"}, + {REAL, "4.567E+6"}, + {REAL, "4.567E-6"}, + {REAL, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0"}, + {INTEGER, "1"}, + {DOTDOT, ""}, + {INTEGER, "10"}, + {STRING, ""}, + {STRING, ""}, + {STRING, "*"}, + {STRING, "*"}, + {STRING, "Don't worry!"}, + {'+', ""}, + {'-', ""}, + {'*', ""}, + {'/', ""}, + {'~', ""}, + {'&', ""}, + {'.', ""}, + {',', ""}, + {';', ""}, + {'|', ""}, + {'(', ""}, + {'[', ""}, + {'{', ""}, + {BECOMES, ""}, + {'^', ""}, + {'=', ""}, + {'#', ""}, + {'<', ""}, + {'>', ""}, + {LE, ""}, + {GE, ""}, + {DOTDOT, ""}, + {':', ""}, + {')', ""}, + {']', ""}, + {'}', ""}, + {ARRAY, ""}, + {BEGIN_, ""}, + {BY, ""}, + {CASE, ""}, + {CONST, ""}, + {DIV, ""}, + {DO, ""}, + {ELSE, ""}, + {ELSIF, ""}, + {END, ""}, + {FALSE, ""}, + {FOR, ""}, + {IF, ""}, + {IMPORT, ""}, + {IN, ""}, + {IS, ""}, + {MOD, ""}, + {MODULE, ""}, + {NIL, ""}, + {OF, ""}, + {OR, ""}, + {POINTER, ""}, + {PROCEDURE, ""}, + {RECORD, ""}, + {REPEAT, ""}, + {RETURN, ""}, + {THEN, ""}, + {TO, ""}, + {TRUE, ""}, + {TYPE, ""}, + {UNTIL, ""}, + {VAR, ""}, + {WHILE, ""} +}; + +static void CompareTokens(int token, int i) +{ + int expectedToken; + const char *expectedValue; + union { + OBNC_LONGI int integer; + OBNC_LONGR double real; + } value; + + assert((i >= 0) && (i < LEN(expectedOutput))); + + expectedToken = expectedOutput[i].token; + expectedValue = expectedOutput[i].value; + + /*compare token IDs*/ + assert(token == expectedToken); + + /*compare semantic values*/ + switch (token) { + case IDENT: + assert(strcmp(yylval.ident, expectedValue) == 0); + break; + case INTEGER: + sscanf(expectedValue, "%" OBNC_INT_MOD "d", &value.integer); + assert(yylval.integer == value.integer); + break; + case REAL: + sscanf(expectedValue, "%" OBNC_REAL_MOD_R "f", &value.real); + assert(yylval.real == value.real); + break; + case STRING: + assert(strcmp(yylval.string, expectedValue) == 0); + break; + } +} + + +static void TestYYLex(void) +{ + int token, i; + + token = yylex(); + i = 0; + while (token > 0) { + CompareTokens(token, i); + token = yylex(); + i++; + } + assert(i == LEN(expectedOutput)); +} + + +int main(void) +{ + const char *inputFilename = "../tests/scanner/tokens.txt"; + int exitStatus; + + Util_Init(); + inputFile = Files_Old(inputFilename, FILES_READ); + assert(inputFile != NULL); + yyin = inputFile; + TestYYLex(); + Files_Close(inputFile); + exitStatus = EXIT_SUCCESS; + return exitStatus; +} diff --git a/src/obnc-compile.c b/src/obnc-compile.c new file mode 100644 index 0000000..83146e4 --- /dev/null +++ b/src/obnc-compile.c @@ -0,0 +1,108 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Config.h" +#include "Oberon.h" +#include "StackTrace.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" /*needed by YYSTYPE in y.tab.h*/ +#include "Trees.h" /*needed by YYSTYPE in y.tab.h*/ +#include "y.tab.h" +#include +#include +#include + +static int HasFileSuffix(const char suffix[], const char filename[]) +{ + int suffixLen = strlen(suffix); + int filenameLen = strlen(filename); + + return (filenameLen > suffixLen) && (strcmp(filename + filenameLen - suffixLen, suffix) == 0); +} + + +static void ExitInvalidCommand(void) +{ + fprintf(stderr, "obnc-compile: invalid command. Try 'obnc-compile -h' for more information.\n"); + exit(1); +} + + +static void PrintHelp(void) +{ + puts("obnc-compile - compile an Oberon module to C"); + puts(""); + puts("usage:"); + puts("\tobnc-compile [-e | -l] MODULE.obn"); + puts("\tobnc-compile (-h | -v)"); + puts(""); + puts("\t-e\tcreate entry point function (main)"); + puts("\t-h\tdisplay help and exit"); + puts("\t-l\tprint names of imported modules and exit"); + puts("\t-v\tdisplay version and exit"); +} + + +static void PrintVersion(void) +{ + if (strcmp(CONFIG_VERSION, "") != 0) { + printf("OBNC %s\n", CONFIG_VERSION); + } else { + puts("OBNC (unknown version)"); + } +} + + +int main(int argc, char *argv[]) +{ + int i; + int helpWanted = 0; + int versionWanted = 0; + int mode = OBERON_NORMAL_MODE; + const char *inputFile = NULL; + + Util_Init(); + StackTrace_Init(); + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + helpWanted = 1; + } else if (strcmp(argv[i], "-v") == 0) { + versionWanted = 1; + } else if (strcmp(argv[i], "-e") == 0) { + mode = OBERON_ENTRY_POINT_MODE; + } else if (strcmp(argv[i], "-l") == 0) { + mode = OBERON_IMPORT_LIST_MODE; + } else if ((argv[i][0] != '-') && (inputFile == NULL) && HasFileSuffix(".obn", argv[i])) { + inputFile = argv[i]; + } else { + ExitInvalidCommand(); + } + } + + if (helpWanted) { + PrintHelp(); + } else if (versionWanted) { + PrintVersion(); + } else if (inputFile != NULL) { + Oberon_Parse(inputFile, mode); + } else { + ExitInvalidCommand(); + } + + return 0; +} diff --git a/src/obnc-path.c b/src/obnc-path.c new file mode 100644 index 0000000..a02e415 --- /dev/null +++ b/src/obnc-path.c @@ -0,0 +1,100 @@ +/*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*/ + +#include "Config.h" +#include "Path.h" +#include "Util.h" +#include +#include +#include +#include + +static void ExitInvalidCommand(void) +{ + fprintf(stderr, "obnc-path: invalid command. Try 'obnc-path -h' for more information.\n"); + exit(1); +} + + +static void PrintHelp(void) +{ + puts("obnc-path - print directory path for Oberon module"); + puts(""); + puts("usage:"); + puts("\tobnc-path MODULE"); + puts("\tobnc-path (-h | -v)"); + puts(""); + puts("\t-h\tdisplay help and exit"); + puts("\t-v\tdisplay version and exit"); +} + + +static void PrintVersion(void) +{ + if (strcmp(CONFIG_VERSION, "") != 0) { + printf("OBNC %s\n", CONFIG_VERSION); + } else { + puts("OBNC (unknown version)"); + } +} + + +static void PrintPath(const char module[]) +{ + char dir[PATH_MAX + 1]; + + Path_Get(module, dir, LEN(dir)); + if (strcmp(dir, "") != 0) { + puts(dir); + } else { + fprintf(stderr, "obnc-path: %s.obn or %s.sym not found\n", module, module); + exit(1); + } +} + + +int main(int argc, char *argv[]) +{ + int i; + int helpWanted = 0; + int versionWanted = 0; + const char *module = NULL; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + helpWanted = 1; + } else if (strcmp(argv[i], "-v") == 0) { + versionWanted = 1; + } else if ((argv[i][0] != '-') && (module == NULL)) { + module = argv[i]; + } else { + ExitInvalidCommand(); + } + } + + if (helpWanted) { + PrintHelp(); + } else if (versionWanted) { + PrintVersion(); + } else if (module != NULL) { + PrintPath(module); + } else { + ExitInvalidCommand(); + } + + return 0; +} diff --git a/src/y.tab.c b/src/y.tab.c new file mode 100644 index 0000000..f83944d --- /dev/null +++ b/src/y.tab.c @@ -0,0 +1,5955 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0.4" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* Copy the first part of user declarations. */ +#line 18 "Oberon.y" /* yacc.c:339 */ + +#include "Config.h" +#include "Files.h" +#include "Generate.h" +#include "lex.yy.h" +#include "Maps.h" +#include "Oberon.h" +#include "Path.h" +#include "Range.h" +#include "Table.h" +#include "Types.h" +#include "Trees.h" +#include "Util.h" +#include "../lib/obnc/OBNC.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/*assignment contexts*/ +#define ASSIGNMENT_CONTEXT 0 +#define PARAM_SUBST_CONTEXT 1 +#define PROC_RESULT_CONTEXT 2 + +static const char *inputFilename; +static int parseMode; +static char *inputModuleName; + +static Trees_Node unresolvedPointerTypes; +static Trees_Node currentTypeIdentdef; +static Trees_Node currentCaseExpression; +static Trees_Node caseExpressionType; +static Trees_Node currentlyDefinedCaseLabels; +static Trees_Node procedureDeclarationStack; + +void yyerror(const char format[], ...); + +static char *QualidentName(const char qualifier[], const char ident[]); + +/*constant predicate functions*/ + +static int IsBoolean(Trees_Node node); +static int IsChar(Trees_Node node); +static int IsInteger(Trees_Node node); +static int IsReal(Trees_Node node); +static int IsString(Trees_Node node); +static int IsSet(Trees_Node node); + +/*functions for type declaration productions*/ + +static Trees_Node ResolvedType(Trees_Node type, int isTypeDecl); +static void ResolvePointerTypes(Trees_Node baseType); +static const char *TypeString(Trees_Node type); + +/*functions for expression productions*/ + +static Trees_Node Designator(const char ident[], Trees_Node selectorList); +static int IsDesignator(Trees_Node exp); +static Trees_Node BaseIdent(Trees_Node designator); +static Trees_Node FirstSelector(Trees_Node designator); +static const char *DesignatorString(Trees_Node designator); +static void CheckIsValueExpression(Trees_Node exp); +static void SetSelectorTypes(Trees_Node identType, Trees_Node designator, int *parameterListFound); +static void RemoveActualParameters(Trees_Node *designator, Trees_Node *actualParameters); +static Trees_Node ExpressionConstValue(int relation, Trees_Node expA, Trees_Node expB); +static Trees_Node SimpleExpressionConstValue(int addOperator, Trees_Node expA, Trees_Node expB); +static Trees_Node TermConstValue(int mulOperator, Trees_Node expA, Trees_Node expB); +static const char *OperatorString(int operator); + +/*functions for statement productions*/ + +static int Writable(Trees_Node designator); +static void ValidateAssignment(Trees_Node expression, Trees_Node targetType, int context, int paramPos); +static void HandleProcedureCall(Trees_Node designator, Trees_Node actualParameters, int isFunctionCall, Trees_Node *ast); +static void CheckCaseLabelUniqueness(Trees_Node label); + +/*functions for module productions*/ + +static void ExportSymbolTable(const char symfilePath[]); + +#line 151 "y.tab.c" /* yacc.c:339 */ + +# ifndef YY_NULLPTR +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "y.tab.h". */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + TOKEN_START = 258, + ARRAY = 259, + BEGIN_ = 260, + BY = 261, + CASE = 262, + CONST = 263, + DIV = 264, + DO = 265, + ELSE = 266, + ELSIF = 267, + END = 268, + FALSE = 269, + FOR = 270, + IF = 271, + IMPORT = 272, + IN = 273, + IS = 274, + MOD = 275, + MODULE = 276, + NIL = 277, + OF = 278, + OR = 279, + POINTER = 280, + PROCEDURE = 281, + RECORD = 282, + REPEAT = 283, + RETURN = 284, + THEN = 285, + TO = 286, + TRUE = 287, + TYPE = 288, + UNTIL = 289, + VAR = 290, + WHILE = 291, + BECOMES = 292, + DOTDOT = 293, + GE = 294, + LE = 295, + IDENT = 296, + INTEGER = 297, + REAL = 298, + STRING = 299, + TOKEN_END = 300 + }; +#endif +/* Tokens. */ +#define TOKEN_START 258 +#define ARRAY 259 +#define BEGIN_ 260 +#define BY 261 +#define CASE 262 +#define CONST 263 +#define DIV 264 +#define DO 265 +#define ELSE 266 +#define ELSIF 267 +#define END 268 +#define FALSE 269 +#define FOR 270 +#define IF 271 +#define IMPORT 272 +#define IN 273 +#define IS 274 +#define MOD 275 +#define MODULE 276 +#define NIL 277 +#define OF 278 +#define OR 279 +#define POINTER 280 +#define PROCEDURE 281 +#define RECORD 282 +#define REPEAT 283 +#define RETURN 284 +#define THEN 285 +#define TO 286 +#define TRUE 287 +#define TYPE 288 +#define UNTIL 289 +#define VAR 290 +#define WHILE 291 +#define BECOMES 292 +#define DOTDOT 293 +#define GE 294 +#define LE 295 +#define IDENT 296 +#define INTEGER 297 +#define REAL 298 +#define STRING 299 +#define TOKEN_END 300 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED + +union YYSTYPE +{ +#line 103 "Oberon.y" /* yacc.c:355 */ + + const char *ident; + OBNC_LONGI int integer; + OBNC_LONGR double real; + const char *string; + Trees_Node node; + +#line 289 "y.tab.c" /* yacc.c:355 */ +}; + +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + +int yyparse (void); + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + +#line 306 "y.tab.c" /* yacc.c:358 */ + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE +# if (defined __GNUC__ \ + && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ + || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C +# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +# else +# define YY_ATTRIBUTE(Spec) /* empty */ +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) +#endif + +#if !defined _Noreturn \ + && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) +# if defined _MSC_VER && 1200 <= _MSC_VER +# define _Noreturn __declspec (noreturn) +# else +# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 5 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 236 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 68 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 97 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 181 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 284 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 300 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 54, 2, 2, 60, 2, + 50, 51, 47, 57, 49, 58, 46, 59, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 53, 52, + 55, 48, 56, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 62, 2, 63, 64, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 65, 67, 66, 61, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 217, 217, 221, 228, 247, 252, 261, 265, 275, + 292, 317, 342, 352, 353, 354, 355, 356, 360, 381, + 391, 395, 402, 421, 429, 440, 445, 451, 485, 491, + 497, 501, 508, 547, 551, 561, 599, 608, 616, 624, + 626, 635, 676, 677, 728, 732, 736, 740, 744, 748, + 752, 756, 763, 785, 813, 817, 822, 828, 832, 836, + 843, 844, 873, 877, 881, 885, 889, 896, 897, 901, + 906, 911, 916, 922, 942, 947, 974, 994, 1013, 1019, + 1027, 1046, 1051, 1056, 1063, 1067, 1074, 1075, 1087, 1108, + 1133, 1138, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1160, + 1166, 1212, 1230, 1238, 1246, 1257, 1280, 1294, 1299, 1305, + 1310, 1316, 1334, 1376, 1384, 1399, 1405, 1411, 1415, 1432, + 1438, 1487, 1497, 1513, 1597, 1604, 1609, 1615, 1631, 1649, + 1678, 1690, 1706, 1715, 1767, 1791, 1807, 1812, 1818, 1823, + 1829, 1833, 1834, 1838, 1839, 1843, 1856, 1862, 1869, 1870, + 1874, 1875, 1879, 1880, 1884, 1885, 1889, 1896, 1902, 1908, + 1913, 1929, 1952, 1958, 1984, 1989, 1995, 1999, 2006, 2022, + 2027, 2036, 2063, 2078, 2085, 2093, 2131, 2135, 2142, 2221, + 2226, 2232 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 0 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "TOKEN_START", "ARRAY", "BEGIN_", "BY", + "CASE", "CONST", "DIV", "DO", "ELSE", "ELSIF", "END", "FALSE", "FOR", + "IF", "IMPORT", "IN", "IS", "MOD", "MODULE", "NIL", "OF", "OR", + "POINTER", "PROCEDURE", "RECORD", "REPEAT", "RETURN", "THEN", "TO", + "TRUE", "TYPE", "UNTIL", "VAR", "WHILE", "BECOMES", "DOTDOT", "GE", "LE", + "IDENT", "INTEGER", "REAL", "STRING", "TOKEN_END", "'.'", "'*'", "'='", + "','", "'('", "')'", "';'", "':'", "'#'", "'<'", "'>'", "'+'", "'-'", + "'/'", "'&'", "'~'", "'['", "']'", "'^'", "'{'", "'}'", "'|'", "$accept", + "qualident", "identdef", "ExportMarkOpt", "number", "ConstDeclaration", + "ConstExpression", "TypeDeclaration", "TypeIdentDef", "type", + "ArrayType", "ArrayLengthOf", "LengthRep", "length", "RecordType", + "RecordHeading", "BaseTypeOpt", "BaseType", "FieldListSequenceOpt", + "FieldListSequence", "FieldList", "IdentList", "PointerType", + "PointerTo", "ProcedureType", "ProcedureTypeSansParam", + "FormalParametersOpt", "VariableDeclaration", "expression", "relation", + "SimpleExpression", "SignOpt", "AddOperator", "term", "MulOperator", + "factor", "designator", "SelectorOptRep", "selector", "set", + "ElementRep", "element", "ExpList", "statement", "assignment", + "ProcedureCall", "StatementSequence", "StatementSequenceReversed", + "IfStatement", "guard", "ElseIfThenOptRep", "ElseOpt", "CaseStatement", + "CaseExpression", "CaseRep", "case", "CaseLabelList", "LabelRange", + "label", "WhileStatement", "ElseIfDoOptRep", "RepeatStatement", + "ForStatement", "ForInit", "ForLimit", "ByOpt", "ProcedureDeclaration", + "ProcedureHeading", "ProcedureHeadingSansParam", "StatementSequenceOpt", + "ReturnExpressionOpt", "DeclarationSequence", "ConstSectionOpt", + "ConstDeclarationOptRep", "TypeSectionOpt", "TypeKeyword", + "TypeDeclarationOptRep", "VariableSectionOpt", + "VariableDeclarationOptRep", "ProcedureDeclarationOptRep", + "FormalParameters", "FPSectionsOpt", "FPSectionRep", "ResultTypeOpt", + "FPSection", "ParameterKindOpt", "IdentRep", "FormalType", + "OpenArrayOpt", "module", "ModuleHeading", "ImportListOpt", "ImportList", + "ImportRep", "import", "BecomesIdentOpt", "ModuleStatements", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 46, 42, 61, 44, + 40, 41, 59, 58, 35, 60, 62, 43, 45, 47, + 38, 126, 91, 93, 94, 123, 125, 124 +}; +# endif + +#define YYPACT_NINF -133 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-133))) + +#define YYTABLE_NINF -166 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 18, -24, 41, -3, -133, -133, 34, 50, 98, -133, + 75, -21, -133, -133, 111, 85, 79, -133, 50, -133, + 80, 20, -133, 109, -133, 88, -133, -133, -133, 77, + 83, 78, 25, 91, 25, 20, 25, -133, 96, -133, + -133, -133, -133, 84, -133, -133, -133, -133, -133, 93, + -133, -133, 80, -133, -133, 25, -133, -133, -133, -133, + 71, 2, 112, 100, 107, -133, 110, 105, 131, -32, + 25, 20, 97, 80, 116, 99, 92, 72, -133, -133, + -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, + -133, 25, 2, -133, -133, -133, -133, -133, -133, 25, + 2, 0, -133, 13, -133, -133, -133, 73, 25, 25, + 20, 25, 20, 104, 29, 25, -133, -133, -133, -133, + -133, -133, 6, 94, 80, 101, 102, 106, -133, -133, + 25, 117, -133, 108, 103, -133, -133, -133, 72, -133, + 80, -133, 72, -133, 106, -4, 13, 113, -133, -133, + 114, -40, -133, -133, -133, -133, -133, -133, 2, -133, + -133, -133, -5, -133, 15, -133, 121, -133, -133, 144, + -133, -133, -133, -133, -133, -133, -28, -34, 80, 72, + -133, -133, -133, 98, -22, -133, -133, -133, -11, -133, + -133, 119, -133, 124, -133, 138, 115, -133, 21, -133, + -133, -133, 25, 25, -133, -133, -133, 73, 73, 20, + 73, 25, 145, 82, 90, 25, -133, -133, -133, -133, + 111, -133, 118, 125, -133, 127, -133, 25, -133, 120, + -133, -133, 80, 72, -133, -133, -133, -133, -133, -133, + -133, 20, 20, 25, 149, 25, -133, -133, 137, 123, + 135, -133, 43, -133, -133, -133, -133, 165, -133, 150, + -133, 169, 25, 170, 119, -133, -133, 141, 180, -133, + 20, 20, -133, 146, -133, -133, 163, -133, 119, -133, + -133, -133, -133, -133 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 172, 1, 174, 0, 142, 173, + 180, 0, 176, 144, 137, 146, 0, 178, 0, 175, + 141, 99, 181, 0, 147, 151, 149, 179, 177, 6, + 0, 0, 56, 0, 56, 99, 56, 78, 101, 103, + 92, 93, 136, 102, 94, 95, 96, 97, 98, 0, + 153, 155, 145, 5, 4, 56, 143, 54, 55, 112, + 42, 0, 0, 0, 0, 106, 0, 0, 0, 76, + 56, 99, 0, 150, 140, 0, 0, 0, 9, 10, + 50, 51, 59, 49, 47, 44, 45, 46, 48, 57, + 58, 56, 0, 71, 69, 70, 7, 8, 68, 56, + 0, 56, 67, 52, 60, 73, 72, 116, 56, 56, + 99, 56, 99, 0, 56, 56, 81, 77, 100, 104, + 171, 33, 0, 0, 0, 0, 0, 40, 12, 148, + 56, 0, 38, 26, 2, 13, 11, 14, 0, 15, + 29, 16, 0, 17, 40, 43, 53, 0, 75, 84, + 88, 0, 86, 64, 65, 62, 63, 66, 0, 121, + 122, 123, 0, 113, 0, 117, 119, 129, 130, 132, + 108, 127, 126, 79, 83, 90, 0, 0, 0, 0, + 152, 135, 154, 142, 158, 134, 39, 22, 0, 20, + 36, 0, 24, 0, 18, 0, 28, 30, 0, 35, + 37, 74, 56, 56, 85, 61, 111, 116, 0, 99, + 0, 56, 0, 110, 0, 56, 82, 80, 34, 41, + 137, 164, 0, 157, 159, 0, 19, 56, 27, 0, + 3, 23, 0, 0, 89, 87, 114, 118, 115, 120, + 131, 99, 99, 56, 0, 56, 124, 91, 139, 162, + 165, 166, 0, 21, 25, 31, 32, 0, 109, 0, + 105, 0, 56, 0, 0, 156, 160, 0, 170, 128, + 99, 99, 138, 0, 161, 167, 0, 163, 0, 107, + 125, 133, 169, 168 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -133, -103, -15, -133, -133, -133, -54, -133, -133, -132, + -133, -133, -133, -39, -133, -133, -133, -133, -133, -133, + -43, 122, -133, -133, -133, -133, 46, -133, -30, -133, + 126, -133, -133, 128, -133, -93, -50, -133, -133, -133, + -133, -12, 81, 129, -133, -133, -35, -133, -133, -33, + -133, -133, -133, -133, -133, -14, -133, -16, -9, -133, + -133, -133, -133, -133, -133, -133, -133, -133, -133, -26, + -133, 16, -133, -133, -133, -133, -133, -133, -133, -133, + -133, -133, -133, -133, -52, -133, -133, -133, -133, -133, + -133, -133, -133, -133, 184, -133, -133 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 135, 121, 54, 102, 31, 187, 76, 77, 136, + 137, 138, 188, 189, 139, 140, 192, 229, 195, 196, + 197, 198, 141, 142, 143, 144, 185, 123, 65, 91, + 60, 61, 92, 103, 158, 104, 38, 69, 117, 106, + 151, 152, 176, 39, 40, 41, 42, 43, 44, 66, + 213, 244, 45, 62, 162, 163, 164, 165, 166, 46, + 214, 47, 48, 64, 169, 212, 125, 126, 127, 22, + 263, 14, 15, 20, 25, 26, 52, 51, 73, 74, + 186, 222, 223, 265, 224, 225, 252, 277, 278, 2, + 3, 8, 9, 11, 12, 17, 23 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 67, 78, 59, 68, 161, 30, 194, 148, 206, 203, + 199, 105, 226, 221, 113, 215, 93, 4, 114, -165, + 82, 215, 153, 216, 94, 79, 204, 32, 18, 217, + 115, 19, 116, 154, 95, 33, 34, 75, 227, 1, + 118, 5, 105, 37, 96, 97, 98, 219, 35, 6, + 105, 7, 99, 89, 90, 178, 36, 57, 58, 179, + 155, 37, 207, 100, 208, 205, 149, 101, 209, 147, + 178, 150, 156, 157, 233, 170, 130, 172, 167, 168, + 174, 171, 57, 58, 175, 175, 57, 58, 228, 80, + 81, 10, 267, 242, 243, 82, 268, 131, 132, 133, + 79, 256, 245, 246, 161, 161, 13, 161, 105, 181, + 83, 84, 16, 134, 134, 159, 21, 160, 24, 85, + 27, 29, 49, 50, 53, 86, 87, 88, 89, 90, + 56, 55, 63, 70, 72, 107, 71, 108, 109, 111, + 110, 112, 124, 120, 129, 173, 180, 128, 190, 193, + 211, 231, 202, 182, 183, 241, 184, 240, 191, 210, + 134, 274, 260, 218, 201, 230, 262, 232, 251, 249, + 221, 254, 234, 150, 238, 283, 264, 250, 269, 271, + 270, 79, 275, 273, 276, 247, 282, 281, 253, 255, + 200, 235, 237, 236, 248, 122, 177, 79, 266, 220, + 119, 239, 28, 0, 0, 0, 257, 258, 0, 0, + 259, 0, 261, 0, 0, 0, 0, 145, 0, 0, + 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 272, 0, 0, 279, 280 +}; + +static const yytype_int16 yycheck[] = +{ + 35, 55, 32, 36, 107, 20, 138, 100, 13, 49, + 142, 61, 23, 35, 46, 49, 14, 41, 50, 41, + 24, 49, 9, 51, 22, 55, 66, 7, 49, 63, + 62, 52, 64, 20, 32, 15, 16, 52, 49, 21, + 70, 0, 92, 41, 42, 43, 44, 179, 28, 52, + 100, 17, 50, 57, 58, 49, 36, 57, 58, 53, + 47, 41, 67, 61, 49, 158, 66, 65, 53, 99, + 49, 101, 59, 60, 53, 110, 4, 112, 108, 109, + 51, 111, 57, 58, 114, 115, 57, 58, 191, 18, + 19, 41, 49, 11, 12, 24, 53, 25, 26, 27, + 130, 233, 12, 13, 207, 208, 8, 210, 158, 124, + 39, 40, 37, 41, 41, 42, 5, 44, 33, 48, + 41, 41, 13, 35, 47, 54, 55, 56, 57, 58, + 52, 48, 41, 37, 41, 23, 52, 37, 31, 34, + 30, 10, 26, 46, 52, 41, 52, 48, 31, 46, + 6, 13, 38, 52, 52, 10, 50, 211, 50, 38, + 41, 264, 13, 178, 51, 41, 29, 52, 41, 51, + 35, 51, 202, 203, 209, 278, 53, 52, 13, 10, + 30, 211, 41, 13, 4, 215, 23, 41, 227, 232, + 144, 203, 208, 207, 220, 73, 115, 227, 250, 183, + 71, 210, 18, -1, -1, -1, 241, 242, -1, -1, + 243, -1, 245, -1, -1, -1, -1, 91, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 262, -1, -1, 270, 271 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 21, 157, 158, 41, 0, 52, 17, 159, 160, + 41, 161, 162, 8, 139, 140, 37, 163, 49, 52, + 141, 5, 137, 164, 33, 142, 143, 41, 162, 41, + 70, 73, 7, 15, 16, 28, 36, 41, 104, 111, + 112, 113, 114, 115, 116, 120, 127, 129, 130, 13, + 35, 145, 144, 47, 71, 48, 52, 57, 58, 96, + 98, 99, 121, 41, 131, 96, 117, 114, 117, 105, + 37, 52, 41, 146, 147, 70, 75, 76, 74, 96, + 18, 19, 24, 39, 40, 48, 54, 55, 56, 57, + 58, 97, 100, 14, 22, 32, 42, 43, 44, 50, + 61, 65, 72, 101, 103, 104, 107, 23, 37, 31, + 30, 34, 10, 46, 50, 62, 64, 106, 96, 111, + 46, 70, 89, 95, 26, 134, 135, 136, 48, 52, + 4, 25, 26, 27, 41, 69, 77, 78, 79, 82, + 83, 90, 91, 92, 93, 98, 101, 96, 103, 66, + 96, 108, 109, 9, 20, 47, 59, 60, 102, 42, + 44, 69, 122, 123, 124, 125, 126, 96, 96, 132, + 114, 96, 114, 41, 51, 96, 110, 110, 49, 53, + 52, 70, 52, 52, 50, 94, 148, 74, 80, 81, + 31, 50, 84, 46, 77, 86, 87, 88, 89, 77, + 94, 51, 38, 49, 66, 103, 13, 67, 49, 53, + 38, 6, 133, 118, 128, 49, 51, 63, 70, 77, + 139, 35, 149, 150, 152, 153, 23, 49, 69, 85, + 41, 13, 52, 53, 96, 109, 123, 125, 114, 126, + 74, 10, 11, 12, 119, 12, 13, 96, 137, 51, + 52, 41, 154, 81, 51, 88, 77, 114, 114, 117, + 13, 117, 29, 138, 53, 151, 152, 49, 53, 13, + 30, 10, 96, 13, 69, 41, 4, 155, 156, 114, + 114, 41, 23, 69 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 68, 69, 69, 70, 71, 71, 72, 72, 73, + 74, 75, 76, 77, 77, 77, 77, 77, 78, 79, + 80, 80, 81, 82, 83, 84, 84, 85, 86, 86, + 87, 87, 88, 89, 89, 90, 91, 92, 93, 94, + 94, 95, 96, 96, 97, 97, 97, 97, 97, 97, + 97, 97, 98, 98, 99, 99, 99, 100, 100, 100, + 101, 101, 102, 102, 102, 102, 102, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 104, 105, 105, 106, + 106, 106, 106, 106, 107, 107, 108, 108, 109, 109, + 110, 110, 111, 111, 111, 111, 111, 111, 111, 111, + 112, 113, 114, 115, 115, 116, 117, 118, 118, 119, + 119, 120, 121, 122, 122, 123, 123, 124, 124, 125, + 125, 126, 126, 126, 127, 128, 128, 129, 130, 131, + 132, 133, 133, 134, 135, 136, 137, 137, 138, 138, + 139, 140, 140, 141, 141, 142, 142, 143, 144, 144, + 145, 145, 146, 146, 147, 147, 148, 149, 149, 150, + 150, 151, 151, 152, 153, 153, 154, 154, 155, 156, + 156, 157, 158, 159, 159, 160, 161, 161, 162, 163, + 163, 164 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 3, 2, 1, 0, 1, 1, 3, + 1, 2, 2, 1, 1, 1, 1, 1, 2, 3, + 1, 3, 1, 3, 2, 3, 0, 1, 1, 0, + 1, 3, 3, 1, 3, 2, 2, 2, 1, 1, + 0, 3, 1, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 3, 1, 1, 0, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 2, 2, 2, 0, 2, + 3, 1, 3, 2, 2, 3, 1, 3, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, + 3, 1, 1, 1, 3, 7, 1, 5, 0, 2, + 0, 5, 1, 1, 3, 3, 0, 1, 3, 1, + 3, 1, 1, 1, 6, 5, 0, 4, 8, 3, + 1, 2, 0, 7, 2, 2, 2, 0, 2, 0, + 4, 2, 0, 3, 0, 2, 0, 1, 3, 0, + 2, 0, 3, 0, 3, 0, 4, 1, 0, 1, + 3, 2, 0, 4, 1, 0, 1, 3, 2, 2, + 0, 8, 2, 1, 0, 3, 1, 3, 2, 2, + 0, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +{ + YYUSE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 218 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewIdent((yyvsp[0].ident)); + } +#line 1616 "y.tab.c" /* yacc.c:1646 */ + break; + + case 3: +#line 222 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewIdent(QualidentName((yyvsp[-2].ident), (yyvsp[0].ident))); + } +#line 1624 "y.tab.c" /* yacc.c:1646 */ + break; + + case 4: +#line 229 "Oberon.y" /* yacc.c:1646 */ + { + if (! Table_LocallyDeclared((yyvsp[-1].ident))) { + (yyval.node) = Trees_NewIdent((yyvsp[-1].ident)); + if ((yyvsp[0].integer)) { + Trees_SetExported((yyval.node)); + } + if (Table_ScopeLocal()) { + Trees_SetLocal((yyval.node)); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: redeclaration of identifier: %s\n", (yyvsp[-1].ident)); + YYABORT; + } + } +#line 1644 "y.tab.c" /* yacc.c:1646 */ + break; + + case 5: +#line 248 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = 1; + } +#line 1652 "y.tab.c" /* yacc.c:1646 */ + break; + + case 6: +#line 252 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = 0; + } +#line 1660 "y.tab.c" /* yacc.c:1646 */ + break; + + case 7: +#line 262 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewInteger((yyvsp[0].integer)); + } +#line 1668 "y.tab.c" /* yacc.c:1646 */ + break; + + case 8: +#line 266 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewReal((yyvsp[0].real)); + } +#line 1676 "y.tab.c" /* yacc.c:1646 */ + break; + + case 9: +#line 276 "Oberon.y" /* yacc.c:1646 */ + { + if (! (Trees_Exported((yyvsp[-2].node)) && Trees_Local((yyvsp[-2].node)))) { + Trees_SetKind(TREES_CONSTANT_KIND, (yyvsp[-2].node)); + Trees_SetType(Trees_Type((yyvsp[0].node)), (yyvsp[-2].node)); + Trees_SetValue((yyvsp[0].node), (yyvsp[-2].node)); + Table_Put((yyvsp[-2].node)); + Generate_ConstDeclaration((yyvsp[-2].node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: cannot export local constant: %s\n", Trees_Name((yyvsp[-2].node))); + YYABORT; + } + } +#line 1694 "y.tab.c" /* yacc.c:1646 */ + break; + + case 10: +#line 293 "Oberon.y" /* yacc.c:1646 */ + { + switch (Trees_Symbol((yyvsp[0].node))) { + case TRUE: + case FALSE: + case STRING: + case TREES_CHAR_CONSTANT: + case INTEGER: + case REAL: + case TREES_SET_CONSTANT: + case NIL: + (yyval.node) = (yyvsp[0].node); + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: constant expression expected\n"); + YYABORT; + } + } +#line 1717 "y.tab.c" /* yacc.c:1646 */ + break; + + case 11: +#line 318 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node sourceType; + + sourceType = ResolvedType((yyvsp[0].node), 1); + if (sourceType != NULL) { + if (! (Trees_Exported((yyvsp[-1].node)) && Trees_Local((yyvsp[-1].node)))) { + Trees_SetType(sourceType, (yyvsp[-1].node)); + ResolvePointerTypes((yyvsp[-1].node)); + currentTypeIdentdef = NULL; + Generate_TypeDeclaration((yyvsp[-1].node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: cannot export local type: %s\n", Trees_Name((yyvsp[-1].node))); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + } +#line 1743 "y.tab.c" /* yacc.c:1646 */ + break; + + case 12: +#line 343 "Oberon.y" /* yacc.c:1646 */ + { + Trees_SetKind(TREES_TYPE_KIND, (yyvsp[-1].node)); + currentTypeIdentdef = (yyvsp[-1].node); + Table_Put((yyvsp[-1].node)); + (yyval.node) = (yyvsp[-1].node); + } +#line 1754 "y.tab.c" /* yacc.c:1646 */ + break; + + case 18: +#line 361 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node reversedLengths, length; + + (yyval.node) = ResolvedType((yyvsp[0].node), 0); + if ((yyval.node) != NULL) { + reversedLengths = (yyvsp[-1].node); + do { + length = Trees_Left(reversedLengths); + (yyval.node) = Types_NewArray(length, (yyval.node)); + reversedLengths = Trees_Right(reversedLengths); + } while (reversedLengths != NULL); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", Trees_Name((yyvsp[0].node))); + exit(EXIT_FAILURE); + } + } +#line 1776 "y.tab.c" /* yacc.c:1646 */ + break; + + case 19: +#line 382 "Oberon.y" /* yacc.c:1646 */ + { + if ((currentTypeIdentdef != NULL) && (Trees_Type(currentTypeIdentdef) == NULL)) { + Trees_SetType(Trees_NewLeaf(ARRAY), currentTypeIdentdef); /*incomplete type*/ + } + (yyval.node) = (yyvsp[-1].node); + } +#line 1787 "y.tab.c" /* yacc.c:1646 */ + break; + + case 20: +#line 392 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_NOSYM, (yyvsp[0].node), NULL); + } +#line 1795 "y.tab.c" /* yacc.c:1646 */ + break; + + case 21: +#line 396 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_NOSYM, (yyvsp[0].node), (yyvsp[-2].node)); + } +#line 1803 "y.tab.c" /* yacc.c:1646 */ + break; + + case 22: +#line 403 "Oberon.y" /* yacc.c:1646 */ + { + if (IsInteger((yyvsp[0].node))) { + if (Trees_Integer((yyvsp[0].node)) > 0) { + (yyval.node) = (yyvsp[0].node); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: positive length expected: %" OBNC_INT_MOD "d" OBNC_INT_MOD "\n", Trees_Integer((yyvsp[0].node))); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer length expected\n"); + YYABORT; + } + } +#line 1823 "y.tab.c" /* yacc.c:1646 */ + break; + + case 23: +#line 422 "Oberon.y" /* yacc.c:1646 */ + { + Table_CloseScope(); + (yyval.node) = Types_NewRecord(Types_RecordBaseType((yyvsp[-2].node)), (yyvsp[-1].node)); + } +#line 1832 "y.tab.c" /* yacc.c:1646 */ + break; + + case 24: +#line 430 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Types_NewRecord((yyvsp[0].node), NULL); + if ((currentTypeIdentdef != NULL) && (Trees_Type(currentTypeIdentdef) == NULL)) { + Trees_SetType((yyval.node), currentTypeIdentdef); + } + Table_OpenScope(); + } +#line 1844 "y.tab.c" /* yacc.c:1646 */ + break; + + case 25: +#line 441 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[-1].node); + } +#line 1852 "y.tab.c" /* yacc.c:1646 */ + break; + + case 26: +#line 445 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 1860 "y.tab.c" /* yacc.c:1646 */ + break; + + case 27: +#line 452 "Oberon.y" /* yacc.c:1646 */ + { + const char *name; + Trees_Node symbol; + + (yyval.node) = NULL; + name = Trees_Name((yyvsp[0].node)); + symbol = Table_At(name); + if (symbol != NULL) { + if (Trees_Kind(symbol) == TREES_TYPE_KIND) { + switch (Trees_Symbol(Types_Structure(symbol))) { + case RECORD: + case POINTER: + (yyval.node) = symbol; + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: record or pointer base type expected: %s\n", name); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: type name expected: %s\n", name); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", name); + YYABORT; + } + } +#line 1895 "y.tab.c" /* yacc.c:1646 */ + break; + + case 28: +#line 486 "Oberon.y" /* yacc.c:1646 */ + { + Trees_ReverseList(&(yyvsp[0].node)); /*correct order*/ + (yyval.node) = (yyvsp[0].node); + } +#line 1904 "y.tab.c" /* yacc.c:1646 */ + break; + + case 29: +#line 491 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 1912 "y.tab.c" /* yacc.c:1646 */ + break; + + case 30: +#line 498 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_FIELD_LIST_SEQUENCE, (yyvsp[0].node), NULL); + } +#line 1920 "y.tab.c" /* yacc.c:1646 */ + break; + + case 31: +#line 502 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_FIELD_LIST_SEQUENCE, (yyvsp[0].node), (yyvsp[-2].node)); + } +#line 1928 "y.tab.c" /* yacc.c:1646 */ + break; + + case 32: +#line 509 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node type, identList, ident; + + (yyval.node) = NULL; + type = ResolvedType((yyvsp[0].node), 0); + if (type != NULL) { + if (! ((type == currentTypeIdentdef) && ! Types_IsPointer(type))) { + Trees_ReverseList(&(yyvsp[-2].node)); /*correct order*/ + identList = (yyvsp[-2].node); + do { + ident = Trees_Left(identList); + if (! Table_LocallyDeclared(Trees_Name(ident))) { + Trees_SetKind(TREES_FIELD_KIND, ident); + Trees_SetType(type, ident); + Table_Put(ident); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: redeclaration of identifier with the same name: %s\n", Trees_Name(ident)); + YYABORT; + } + identList = Trees_Right(identList); + } while (identList != NULL); + + (yyval.node) = (yyvsp[-2].node); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: recursive field type must be a pointer: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared type: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + } +#line 1968 "y.tab.c" /* yacc.c:1646 */ + break; + + case 33: +#line 548 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_IDENT_LIST, (yyvsp[0].node), NULL); + } +#line 1976 "y.tab.c" /* yacc.c:1646 */ + break; + + case 34: +#line 552 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node reversedIdents; + + reversedIdents = Trees_NewNode(TREES_IDENT_LIST, (yyvsp[0].node), (yyvsp[-2].node)); + (yyval.node) = reversedIdents; + } +#line 1987 "y.tab.c" /* yacc.c:1646 */ + break; + + case 35: +#line 562 "Oberon.y" /* yacc.c:1646 */ + { + const char *baseTypeName; + Trees_Node declaredBaseType; + + (yyval.node) = NULL; + if (Trees_Symbol((yyvsp[0].node)) == IDENT) { + baseTypeName = Trees_Name((yyvsp[0].node)); + declaredBaseType = Table_At(baseTypeName); + if (declaredBaseType != NULL) { + if (Types_IsRecord(declaredBaseType)) { + (yyval.node) = Types_NewPointer(declaredBaseType); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record expected as pointer base type: %s\n", baseTypeName); + YYABORT; + } + } else if (currentTypeIdentdef != NULL) { + Trees_SetKind(TREES_TYPE_KIND, (yyvsp[0].node)); + Trees_SetType(Types_NewRecord(NULL, NULL), (yyvsp[0].node)); + (yyval.node) = Types_NewPointer((yyvsp[0].node)); + unresolvedPointerTypes = Trees_NewNode(TREES_NOSYM, (yyval.node), unresolvedPointerTypes); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared type: %s\n", baseTypeName); + YYABORT; + } + } else if(Trees_Symbol((yyvsp[0].node)) == RECORD) { + (yyval.node) = Types_NewPointer((yyvsp[0].node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record expected as pointer base type\n"); + YYABORT; + } + } +#line 2026 "y.tab.c" /* yacc.c:1646 */ + break; + + case 36: +#line 600 "Oberon.y" /* yacc.c:1646 */ + { + if ((currentTypeIdentdef != NULL) && (Trees_Type(currentTypeIdentdef) == NULL)) { + Trees_SetType(Types_NewPointer(NULL), currentTypeIdentdef); /*incomplete type*/ + } + } +#line 2036 "y.tab.c" /* yacc.c:1646 */ + break; + + case 37: +#line 609 "Oberon.y" /* yacc.c:1646 */ + { + Table_CloseScope(); + (yyval.node) = (yyvsp[0].node); + } +#line 2045 "y.tab.c" /* yacc.c:1646 */ + break; + + case 38: +#line 617 "Oberon.y" /* yacc.c:1646 */ + { + Table_OpenScope(); + (yyval.node) = NULL; + } +#line 2054 "y.tab.c" /* yacc.c:1646 */ + break; + + case 40: +#line 626 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewLeaf(PROCEDURE); + } +#line 2062 "y.tab.c" /* yacc.c:1646 */ + break; + + case 41: +#line 636 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node type, identList, ident; + + type = ResolvedType((yyvsp[0].node), 0); + if (type != NULL) { + Trees_ReverseList(&(yyvsp[-2].node)); /*correct order*/ + identList = (yyvsp[-2].node); + do { + ident = Trees_Left(identList); + if (! (Trees_Exported(ident) && Trees_Local(ident))) { + if (! Table_LocallyDeclared(Trees_Name(ident))) { + Trees_SetKind(TREES_VARIABLE_KIND, ident); + Trees_SetType(type, ident); + Table_Put(ident); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: redeclaration of identifier with the same name: %s\n", Trees_Name(ident)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: cannot export local variable: %s\n", Trees_Name(ident)); + YYABORT; + } + identList = Trees_Right(identList); + } while (identList != NULL); + + Generate_VariableDeclaration((yyvsp[-2].node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: undeclared identifier: %s\n", Trees_Name((yyvsp[0].node))); + exit(EXIT_FAILURE); + } + } +#line 2101 "y.tab.c" /* yacc.c:1646 */ + break; + + case 43: +#line 678 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node expA, expB, typeA, typeB; + + expA = (yyvsp[-2].node); + expB = (yyvsp[0].node); + typeA = Trees_Type((yyvsp[-2].node)); + typeB = Trees_Type((yyvsp[0].node)); + + CheckIsValueExpression((yyvsp[-2].node)); + if ((yyvsp[-1].integer) == IS) { + if (! Types_IsRecord(typeA) + || (IsDesignator((yyvsp[-2].node)) && (Trees_Kind(BaseIdent((yyvsp[-2].node))) == TREES_VAR_PARAM_KIND))) { + if (IsDesignator((yyvsp[0].node))) { + expB = BaseIdent((yyvsp[0].node)); + typeB = BaseIdent((yyvsp[0].node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: identifier expected as first operand of IS\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: variable parameter expected as first operand of IS\n"); + YYABORT; + } + } else { + CheckIsValueExpression((yyvsp[0].node)); + } + + if (Types_ExpressionCompatible((yyvsp[-1].integer), typeA, typeB)) { + (yyval.node) = ExpressionConstValue((yyvsp[-1].integer), expA, expB); + if ((yyval.node) == NULL) { + if (IsString(expA) && Types_IsChar(typeB)) { + expA = Trees_NewChar(Trees_String(expA)[0]); + } else if (Types_IsChar(typeA) && IsString(expB)) { + expB = Trees_NewChar(Trees_String(expB)[0]); + } + (yyval.node) = Trees_NewNode((yyvsp[-1].integer), expA, expB); + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), (yyval.node)); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in relation \"%s\": %s, %s\n", + OperatorString((yyvsp[-1].integer)), TypeString(typeA), TypeString(typeB)); + YYABORT; + } + } +#line 2153 "y.tab.c" /* yacc.c:1646 */ + break; + + case 44: +#line 729 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '='; + } +#line 2161 "y.tab.c" /* yacc.c:1646 */ + break; + + case 45: +#line 733 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '#'; + } +#line 2169 "y.tab.c" /* yacc.c:1646 */ + break; + + case 46: +#line 737 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '<'; + } +#line 2177 "y.tab.c" /* yacc.c:1646 */ + break; + + case 47: +#line 741 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = LE; + } +#line 2185 "y.tab.c" /* yacc.c:1646 */ + break; + + case 48: +#line 745 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '>'; + } +#line 2193 "y.tab.c" /* yacc.c:1646 */ + break; + + case 49: +#line 749 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = GE; + } +#line 2201 "y.tab.c" /* yacc.c:1646 */ + break; + + case 50: +#line 753 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = IN; + } +#line 2209 "y.tab.c" /* yacc.c:1646 */ + break; + + case 51: +#line 757 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = IS; + } +#line 2217 "y.tab.c" /* yacc.c:1646 */ + break; + + case 52: +#line 764 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[0].node); + if ((yyvsp[-1].integer) >= 0) { + CheckIsValueExpression((yyvsp[0].node)); + if (Types_ExpressionCompatible((yyvsp[-1].integer), Trees_Type((yyvsp[0].node)), NULL)) { + (yyval.node) = SimpleExpressionConstValue((yyvsp[-1].integer), (yyvsp[0].node), NULL); + if ((yyval.node) == NULL) { + (yyval.node) = Trees_NewNode((yyvsp[-1].integer), (yyvsp[0].node), NULL); + if (Types_IsByte(Trees_Type((yyvsp[0].node)))) { + Trees_SetType(Trees_NewLeaf(TREES_INTEGER_TYPE), (yyval.node)); + } else { + Trees_SetType(Trees_Type((yyvsp[0].node)), (yyval.node)); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible type in unary operation \"%s\": %s\n", OperatorString((yyvsp[-1].integer)), TypeString(Trees_Type((yyvsp[0].node)))); + YYABORT; + } + } + } +#line 2243 "y.tab.c" /* yacc.c:1646 */ + break; + + case 53: +#line 786 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + + CheckIsValueExpression((yyvsp[-2].node)); + CheckIsValueExpression((yyvsp[0].node)); + + if (Types_ExpressionCompatible((yyvsp[-1].integer), Trees_Type((yyvsp[-2].node)), Trees_Type((yyvsp[0].node)))) { + (yyval.node) = SimpleExpressionConstValue((yyvsp[-1].integer), (yyvsp[-2].node), (yyvsp[0].node)); + if ((yyval.node) == NULL) { + (yyval.node) = Trees_NewNode((yyvsp[-1].integer), (yyvsp[-2].node), (yyvsp[0].node)); + if (Types_IsByte(Trees_Type((yyvsp[-2].node))) || Types_IsByte(Trees_Type((yyvsp[0].node)))) { + Trees_SetType(Trees_NewLeaf(TREES_INTEGER_TYPE), (yyval.node)); + } else { + Trees_SetType(Trees_Type((yyvsp[-2].node)), (yyval.node)); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in operation \"%s\": %s, %s\n", + OperatorString((yyvsp[-1].integer)), TypeString(Trees_Type((yyvsp[-2].node))), TypeString(Trees_Type((yyvsp[0].node)))); + YYABORT; + } + assert((yyval.node) != NULL); + } +#line 2272 "y.tab.c" /* yacc.c:1646 */ + break; + + case 54: +#line 814 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '+'; + } +#line 2280 "y.tab.c" /* yacc.c:1646 */ + break; + + case 55: +#line 818 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '-'; + } +#line 2288 "y.tab.c" /* yacc.c:1646 */ + break; + + case 56: +#line 822 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = -1; + } +#line 2296 "y.tab.c" /* yacc.c:1646 */ + break; + + case 57: +#line 829 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '+'; + } +#line 2304 "y.tab.c" /* yacc.c:1646 */ + break; + + case 58: +#line 833 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '-'; + } +#line 2312 "y.tab.c" /* yacc.c:1646 */ + break; + + case 59: +#line 837 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = OR; + } +#line 2320 "y.tab.c" /* yacc.c:1646 */ + break; + + case 61: +#line 845 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + + CheckIsValueExpression((yyvsp[-2].node)); + CheckIsValueExpression((yyvsp[0].node)); + + if (Types_ExpressionCompatible((yyvsp[-1].integer), Trees_Type((yyvsp[-2].node)), Trees_Type((yyvsp[0].node)))) { + (yyval.node) = TermConstValue((yyvsp[-1].integer), (yyvsp[-2].node), (yyvsp[0].node)); + if ((yyval.node) == NULL) { + (yyval.node) = Trees_NewNode((yyvsp[-1].integer), (yyvsp[-2].node), (yyvsp[0].node)); + if (Types_IsByte(Trees_Type((yyvsp[-2].node))) || Types_IsByte(Trees_Type((yyvsp[0].node)))) { + Trees_SetType(Trees_NewLeaf(TREES_INTEGER_TYPE), (yyval.node)); + } else { + Trees_SetType(Trees_Type((yyvsp[-2].node)), (yyval.node)); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in operation \"%s\": %s, %s\n", + OperatorString((yyvsp[-1].integer)), TypeString(Trees_Type((yyvsp[-2].node))), TypeString(Trees_Type((yyvsp[0].node)))); + YYABORT; + } + + assert((yyval.node) != NULL); + } +#line 2350 "y.tab.c" /* yacc.c:1646 */ + break; + + case 62: +#line 874 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '*'; + } +#line 2358 "y.tab.c" /* yacc.c:1646 */ + break; + + case 63: +#line 878 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '/'; + } +#line 2366 "y.tab.c" /* yacc.c:1646 */ + break; + + case 64: +#line 882 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = DIV; + } +#line 2374 "y.tab.c" /* yacc.c:1646 */ + break; + + case 65: +#line 886 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = MOD; + } +#line 2382 "y.tab.c" /* yacc.c:1646 */ + break; + + case 66: +#line 890 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = '&'; + } +#line 2390 "y.tab.c" /* yacc.c:1646 */ + break; + + case 68: +#line 898 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewString((yyvsp[0].string)); + } +#line 2398 "y.tab.c" /* yacc.c:1646 */ + break; + + case 69: +#line 902 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewLeaf(NIL); + Trees_SetType(Trees_NewLeaf(TREES_NIL_TYPE), (yyval.node)); + } +#line 2407 "y.tab.c" /* yacc.c:1646 */ + break; + + case 70: +#line 907 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewLeaf(TRUE); + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), (yyval.node)); + } +#line 2416 "y.tab.c" /* yacc.c:1646 */ + break; + + case 71: +#line 912 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewLeaf(FALSE); + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), (yyval.node)); + } +#line 2425 "y.tab.c" /* yacc.c:1646 */ + break; + + case 72: +#line 917 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[0].node); + Trees_SetType(Trees_NewLeaf(TREES_SET_TYPE), (yyval.node)); + } +#line 2434 "y.tab.c" /* yacc.c:1646 */ + break; + + case 73: +#line 923 "Oberon.y" /* yacc.c:1646 */ + { + const int isFunctionCall = 1; + Trees_Node designator, actualParameters, ident; + + (yyval.node) = NULL; + if (Trees_Symbol((yyvsp[0].node)) == TREES_PROCEDURE_CALL) { + designator = Trees_Left((yyvsp[0].node)); + actualParameters = Trees_Right((yyvsp[0].node)); + HandleProcedureCall(designator, actualParameters, isFunctionCall, &(yyval.node)); + } else { + ident = Trees_Left((yyvsp[0].node)); + if (Trees_Kind(ident) == TREES_CONSTANT_KIND) { + (yyval.node) = Trees_Value(ident); + } else { + (yyval.node) = (yyvsp[0].node); + } + } + assert((yyval.node) != NULL); + } +#line 2458 "y.tab.c" /* yacc.c:1646 */ + break; + + case 74: +#line 943 "Oberon.y" /* yacc.c:1646 */ + { + CheckIsValueExpression((yyvsp[-1].node)); + (yyval.node) = (yyvsp[-1].node); + } +#line 2467 "y.tab.c" /* yacc.c:1646 */ + break; + + case 75: +#line 948 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + CheckIsValueExpression((yyvsp[0].node)); + if (Types_ExpressionCompatible('~', Trees_Type((yyvsp[0].node)), NULL)) { + switch (Trees_Symbol((yyvsp[0].node))) { + case TRUE: + (yyval.node) = Trees_NewLeaf(FALSE); + break; + case FALSE: + (yyval.node) = Trees_NewLeaf(TRUE); + break; + default: + (yyval.node) = Trees_NewNode('~', (yyvsp[0].node), NULL); + } + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), (yyval.node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible type in operation \"~\": %s\n", TypeString(Trees_Type((yyvsp[0].node)))); + YYABORT; + } + assert((yyval.node) != NULL); + } +#line 2494 "y.tab.c" /* yacc.c:1646 */ + break; + + case 76: +#line 975 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node designator, identType, actualParameters; + int parameterListFound; /*possibly empty*/ + + Trees_ReverseList(&(yyvsp[0].node)); /*correct order*/ + designator = Designator((yyvsp[-1].ident), (yyvsp[0].node)); + + identType = Trees_Type(BaseIdent(designator)); + SetSelectorTypes(identType, designator, ¶meterListFound); + if (parameterListFound) { + RemoveActualParameters(&designator, &actualParameters); + (yyval.node) = Trees_NewNode(TREES_PROCEDURE_CALL, designator, actualParameters); + } else { + (yyval.node) = designator; + } + } +#line 2515 "y.tab.c" /* yacc.c:1646 */ + break; + + case 77: +#line 995 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node curr; + + if ((Trees_Symbol((yyvsp[0].node)) == '[') && (Trees_Right((yyvsp[0].node)) != NULL)) { /*multi-dimensional element selector*/ + /*attatch last element selector node to $1*/ + Trees_ReverseList(&(yyvsp[0].node)); + (yyval.node) = (yyvsp[-1].node); + curr = (yyvsp[0].node); + do { + (yyval.node) = Trees_NewNode('[', Trees_Left(curr), (yyval.node)); + curr = Trees_Right(curr); + } while (curr != NULL); + Trees_ReverseList(&(yyval.node)); + } else { + (yyval.node) = Trees_NewNode(Trees_Symbol((yyvsp[0].node)), Trees_Left((yyvsp[0].node)), (yyvsp[-1].node)); + } + } +#line 2537 "y.tab.c" /* yacc.c:1646 */ + break; + + case 78: +#line 1013 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 2545 "y.tab.c" /* yacc.c:1646 */ + break; + + case 79: +#line 1020 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node field; + + field = Trees_NewIdent((yyvsp[0].ident)); + Trees_SetKind(TREES_FIELD_KIND, field); + (yyval.node) = Trees_NewNode('.', field, NULL); + } +#line 2557 "y.tab.c" /* yacc.c:1646 */ + break; + + case 80: +#line 1028 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node curr, exp; + + /*create one selector node per index*/ + (yyval.node) = NULL; + curr = (yyvsp[-1].node); /*NOTE: ExpList is reversed*/ + do { + exp = Trees_Left(curr); + if (Types_IsInteger(Trees_Type(exp))) { + (yyval.node) = Trees_NewNode('[', Trees_Left(curr), (yyval.node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer array index expected\n"); + YYABORT; + } + curr = Trees_Right(curr); + } while (curr != NULL); + } +#line 2580 "y.tab.c" /* yacc.c:1646 */ + break; + + case 81: +#line 1047 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode('^', NULL, NULL); + } +#line 2588 "y.tab.c" /* yacc.c:1646 */ + break; + + case 82: +#line 1052 "Oberon.y" /* yacc.c:1646 */ + { + Trees_ReverseList(&(yyvsp[-1].node)); /*correct order*/ + (yyval.node) = Trees_NewNode('(', (yyvsp[-1].node), NULL); + } +#line 2597 "y.tab.c" /* yacc.c:1646 */ + break; + + case 83: +#line 1057 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode('(', NULL, NULL); + } +#line 2605 "y.tab.c" /* yacc.c:1646 */ + break; + + case 84: +#line 1064 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewSet(0x0u); + } +#line 2613 "y.tab.c" /* yacc.c:1646 */ + break; + + case 85: +#line 1068 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[-1].node); + } +#line 2621 "y.tab.c" /* yacc.c:1646 */ + break; + + case 87: +#line 1076 "Oberon.y" /* yacc.c:1646 */ + { + if ((Trees_Symbol((yyvsp[-2].node)) == TREES_SET_CONSTANT) + && (Trees_Symbol((yyvsp[0].node)) == TREES_SET_CONSTANT)) { + (yyval.node) = Trees_NewSet(Trees_Set((yyvsp[-2].node)) | Trees_Set((yyvsp[0].node))); + } else { + (yyval.node) = Trees_NewNode('+', (yyvsp[-2].node), (yyvsp[0].node)); + } + } +#line 2634 "y.tab.c" /* yacc.c:1646 */ + break; + + case 88: +#line 1088 "Oberon.y" /* yacc.c:1646 */ + { + int i; + Trees_Node type; + + CheckIsValueExpression((yyvsp[0].node)); + (yyval.node) = NULL; + type = Trees_Type((yyvsp[0].node)); + if (IsInteger((yyvsp[0].node))) { + i = Trees_Integer((yyvsp[0].node)); + Range_CheckSetElement(i); + (yyval.node) = Trees_NewSet(1 << i); + } else if (Types_IsInteger(type)) { + (yyval.node) = Trees_NewNode(TREES_SINGLE_ELEMENT_SET, (yyvsp[0].node), NULL); + Trees_SetType(Trees_NewLeaf(TREES_SET_TYPE), (yyval.node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: element must have integer type\n"); + YYABORT; + } + } +#line 2659 "y.tab.c" /* yacc.c:1646 */ + break; + + case 89: +#line 1109 "Oberon.y" /* yacc.c:1646 */ + { + CheckIsValueExpression((yyvsp[-2].node)); + CheckIsValueExpression((yyvsp[0].node)); + (yyval.node) = NULL; + if (IsInteger((yyvsp[-2].node))) { + Range_CheckSetElement(Trees_Integer((yyvsp[-2].node))); + } + if (IsInteger((yyvsp[0].node))) { + Range_CheckSetElement(Trees_Integer((yyvsp[0].node))); + } + if (IsInteger((yyvsp[-2].node)) && IsInteger((yyvsp[0].node))) { + (yyval.node) = Trees_NewSet(OBNC_RANGE(Trees_Integer((yyvsp[-2].node)), Trees_Integer((yyvsp[0].node)))); + } else if (Types_IsInteger(Trees_Type((yyvsp[-2].node))) && Types_IsInteger(Trees_Type((yyvsp[0].node)))) { + (yyval.node) = Trees_NewNode(TREES_RANGE_SET, (yyvsp[-2].node), (yyvsp[0].node)); + Trees_SetType(Trees_NewLeaf(TREES_SET_TYPE), (yyval.node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: element must have integer type\n"); + YYABORT; + } + } +#line 2685 "y.tab.c" /* yacc.c:1646 */ + break; + + case 90: +#line 1134 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_EXP_LIST, (yyvsp[0].node), NULL); + Trees_SetType(Trees_Type((yyvsp[0].node)), (yyval.node)); + } +#line 2694 "y.tab.c" /* yacc.c:1646 */ + break; + + case 91: +#line 1139 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node reversedList; + + reversedList = Trees_NewNode(TREES_EXP_LIST, (yyvsp[0].node), (yyvsp[-2].node)); + (yyval.node) = reversedList; + Trees_SetType(Trees_Type((yyvsp[0].node)), (yyval.node)); + } +#line 2706 "y.tab.c" /* yacc.c:1646 */ + break; + + case 99: +#line 1160 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 2714 "y.tab.c" /* yacc.c:1646 */ + break; + + case 100: +#line 1167 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node designator, ident, designatorType, exp; + + CheckIsValueExpression((yyvsp[0].node)); + switch (Trees_Symbol((yyvsp[-2].node))) { + case TREES_DESIGNATOR: + designator = (yyvsp[-2].node); + exp = (yyvsp[0].node); + ident = BaseIdent((yyvsp[-2].node)); + designatorType = Trees_Type((yyvsp[-2].node)); + switch (Trees_Kind(ident)) { + case TREES_VARIABLE_KIND: + case TREES_VALUE_PARAM_KIND: + case TREES_VAR_PARAM_KIND: + if (Writable((yyvsp[-2].node))) { + ValidateAssignment(exp, designatorType, ASSIGNMENT_CONTEXT, 0); + if (Types_IsChar(designatorType) && IsString(exp)) { + exp = Trees_NewChar(Trees_String(exp)[0]); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: assignment to read-only variable\n"); + YYABORT; + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: assignment to non-variable\n"); + YYABORT; + } + (yyval.node) = Trees_NewNode(BECOMES, designator, exp); + break; + case TREES_PROCEDURE_CALL: + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected procedure call in assignment target\n"); + YYABORT; + break; + default: + assert(0); + } + } +#line 2760 "y.tab.c" /* yacc.c:1646 */ + break; + + case 101: +#line 1213 "Oberon.y" /* yacc.c:1646 */ + { + const int isFunctionCall = 0; + Trees_Node designator, actualParameters; + + if (Trees_Symbol((yyvsp[0].node)) == TREES_PROCEDURE_CALL) { + designator = Trees_Left((yyvsp[0].node)); + actualParameters = Trees_Right((yyvsp[0].node)); + } else { + designator = (yyvsp[0].node); + actualParameters = NULL; + } + HandleProcedureCall(designator, actualParameters, isFunctionCall, &(yyval.node)); + assert((yyval.node) != NULL); + } +#line 2779 "y.tab.c" /* yacc.c:1646 */ + break; + + case 102: +#line 1231 "Oberon.y" /* yacc.c:1646 */ + { + Trees_ReverseList(&(yyvsp[0].node)); /*correct order*/ + (yyval.node) = (yyvsp[0].node); + } +#line 2788 "y.tab.c" /* yacc.c:1646 */ + break; + + case 103: +#line 1239 "Oberon.y" /* yacc.c:1646 */ + { + if ((yyvsp[0].node) == NULL) { + (yyval.node) = NULL; + } else { + (yyval.node) = Trees_NewNode(TREES_STATEMENT_SEQUENCE, (yyvsp[0].node), NULL); + } + } +#line 2800 "y.tab.c" /* yacc.c:1646 */ + break; + + case 104: +#line 1247 "Oberon.y" /* yacc.c:1646 */ + { + if ((yyvsp[0].node) != NULL) { + (yyval.node) = Trees_NewNode(TREES_STATEMENT_SEQUENCE, (yyvsp[0].node), (yyvsp[-2].node)); + } else { + (yyval.node) = (yyvsp[-2].node); + } + } +#line 2812 "y.tab.c" /* yacc.c:1646 */ + break; + + case 105: +#line 1258 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node currElsif, currExp, currThen, currStmt; + + if ((yyvsp[-2].node) == NULL) { + (yyval.node) = Trees_NewNode(IF, (yyvsp[-5].node), Trees_NewNode(THEN, (yyvsp[-3].node), (yyvsp[-1].node))); + } else { + /*correct order of elsif nodes*/ + (yyval.node) = (yyvsp[-1].node); + currElsif = (yyvsp[-2].node); + do { + currExp = Trees_Left(currElsif); + currThen = Trees_Right(currElsif); + currStmt = Trees_Left(currThen); + (yyval.node) = Trees_NewNode(ELSIF, currExp, Trees_NewNode(THEN, currStmt, (yyval.node))); + currElsif = Trees_Right(currThen); + } while (currElsif != NULL); + (yyval.node) = Trees_NewNode(IF, (yyvsp[-5].node), Trees_NewNode(THEN, (yyvsp[-3].node), (yyval.node))); + } + } +#line 2836 "y.tab.c" /* yacc.c:1646 */ + break; + + case 106: +#line 1281 "Oberon.y" /* yacc.c:1646 */ + { + CheckIsValueExpression((yyvsp[0].node)); + if (Types_IsBoolean(Trees_Type((yyvsp[0].node)))) { + (yyval.node) = (yyvsp[0].node); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: boolean expression expected\n"); + YYABORT; + } + } +#line 2851 "y.tab.c" /* yacc.c:1646 */ + break; + + case 107: +#line 1295 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(ELSIF, (yyvsp[-2].node), Trees_NewNode(THEN, (yyvsp[0].node), (yyvsp[-4].node))); + } +#line 2859 "y.tab.c" /* yacc.c:1646 */ + break; + + case 108: +#line 1299 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 2867 "y.tab.c" /* yacc.c:1646 */ + break; + + case 109: +#line 1306 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(ELSE, (yyvsp[0].node), NULL); + } +#line 2875 "y.tab.c" /* yacc.c:1646 */ + break; + + case 110: +#line 1310 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 2883 "y.tab.c" /* yacc.c:1646 */ + break; + + case 111: +#line 1317 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node expType, caseVariable; + + if ((yyvsp[-1].node) != NULL) { + Trees_ReverseList(&(yyvsp[-1].node)); /*correct order*/ + } + expType = Trees_Type((yyvsp[-3].node)); + if (Types_IsRecord(expType) || Types_IsPointer(expType)) { + /*reset original type*/ + caseVariable = Trees_Left((yyvsp[-3].node)); + Trees_SetType(caseExpressionType, caseVariable); + } + (yyval.node) = Trees_NewNode(CASE, (yyvsp[-3].node), (yyvsp[-1].node)); + } +#line 2902 "y.tab.c" /* yacc.c:1646 */ + break; + + case 112: +#line 1335 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node typeStruct, caseVariable; + + CheckIsValueExpression((yyvsp[0].node)); + typeStruct = Types_Structure(Trees_Type((yyvsp[0].node))); + switch (Trees_Symbol(typeStruct)) { + case RECORD: + /*fall through*/ + case POINTER: + if (IsDesignator((yyvsp[0].node)) && (FirstSelector((yyvsp[0].node)) == NULL)) { + caseVariable = BaseIdent((yyvsp[0].node)); + if (! Types_IsRecord(typeStruct) || (Trees_Kind(caseVariable) == TREES_VAR_PARAM_KIND)) { + (yyval.node) = (yyvsp[0].node); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record CASE expression must be a variable parameter\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: non-integral CASE expression must be a variable\n"); + YYABORT; + } + /*fall through*/ + case TREES_INTEGER_TYPE: + /*fall through*/ + case TREES_CHAR_TYPE: + currentCaseExpression = (yyvsp[0].node); + caseExpressionType = Trees_Type((yyvsp[0].node)); + currentlyDefinedCaseLabels = NULL; + (yyval.node) = (yyvsp[0].node); + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: invalid type of CASE expression\n"); + YYABORT; + } + } +#line 2945 "y.tab.c" /* yacc.c:1646 */ + break; + + case 113: +#line 1377 "Oberon.y" /* yacc.c:1646 */ + { + if ((yyvsp[0].node) != NULL) { + (yyval.node) = Trees_NewNode(TREES_CASE_REP, (yyvsp[0].node), NULL); + } else { + (yyval.node) = NULL; + } + } +#line 2957 "y.tab.c" /* yacc.c:1646 */ + break; + + case 114: +#line 1385 "Oberon.y" /* yacc.c:1646 */ + { + if ((yyvsp[0].node) != NULL) { + if ((yyvsp[-2].node) != NULL) { + (yyval.node) = Trees_NewNode(TREES_CASE_REP, (yyvsp[0].node), (yyvsp[-2].node)); + } else { + (yyval.node) = Trees_NewNode(TREES_CASE_REP, (yyvsp[0].node), NULL); + } + } else { + (yyval.node) = NULL; + } + } +#line 2973 "y.tab.c" /* yacc.c:1646 */ + break; + + case 115: +#line 1400 "Oberon.y" /* yacc.c:1646 */ + { + Trees_ReverseList(&(yyvsp[-2].node)); /*correct order*/ + (yyval.node) = Trees_NewNode(TREES_CASE, (yyvsp[-2].node), (yyvsp[0].node)); + } +#line 2982 "y.tab.c" /* yacc.c:1646 */ + break; + + case 116: +#line 1405 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 2990 "y.tab.c" /* yacc.c:1646 */ + break; + + case 117: +#line 1412 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_CASE_LABEL_LIST, (yyvsp[0].node), NULL); + } +#line 2998 "y.tab.c" /* yacc.c:1646 */ + break; + + case 118: +#line 1416 "Oberon.y" /* yacc.c:1646 */ + { + switch (Trees_Symbol((yyvsp[0].node))) { + case INTEGER: + case TREES_CHAR_CONSTANT: + case DOTDOT: + (yyval.node) = Trees_NewNode(TREES_CASE_LABEL_LIST, (yyvsp[0].node), (yyvsp[-2].node)); + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected list of type name labels\n"); + YYABORT; + } + } +#line 3016 "y.tab.c" /* yacc.c:1646 */ + break; + + case 119: +#line 1433 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[0].node); + CheckCaseLabelUniqueness((yyvsp[0].node)); + currentlyDefinedCaseLabels = Trees_NewNode(TREES_NOSYM, (yyvsp[0].node), currentlyDefinedCaseLabels); + } +#line 3026 "y.tab.c" /* yacc.c:1646 */ + break; + + case 120: +#line 1439 "Oberon.y" /* yacc.c:1646 */ + { + const int rangeLenMax = 255; + int leftSym, rightSym; + int rangeMin, rangeMax; + + leftSym = Trees_Symbol((yyvsp[-2].node)); + rightSym = Trees_Symbol((yyvsp[0].node)); + if (leftSym == rightSym) { + switch (leftSym) { + case INTEGER: + rangeMin = Trees_Integer((yyvsp[-2].node)); + rangeMax = Trees_Integer((yyvsp[0].node)); + if (rangeMin <= rangeMax) { + if (rangeMax - rangeMin > rangeLenMax) { + Oberon_PrintContext(); + fprintf(stderr, "maximum range length of %d exceeded\n", rangeLenMax); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: left integer must be less than right integer in case range\n"); + YYABORT; + } + break; + case TREES_CHAR_CONSTANT: + if (Trees_Char((yyvsp[-2].node)) >= Trees_Char((yyvsp[0].node))) { + Oberon_PrintContext(); + fprintf(stderr, "error: left string must be less than right string in case range\n"); + YYABORT; + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: case label ranges must contain integers or single-character strings\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: case labels in a range must have the same type\n"); + YYABORT; + } + (yyval.node) = Trees_NewNode(DOTDOT, (yyvsp[-2].node), (yyvsp[0].node)); + CheckCaseLabelUniqueness((yyval.node)); + currentlyDefinedCaseLabels = Trees_NewNode(TREES_NOSYM, (yyval.node), currentlyDefinedCaseLabels); + } +#line 3076 "y.tab.c" /* yacc.c:1646 */ + break; + + case 121: +#line 1488 "Oberon.y" /* yacc.c:1646 */ + { + if (Types_IsInteger(Trees_Type(currentCaseExpression))) { + (yyval.node) = Trees_NewInteger((yyvsp[0].integer)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected integer label\n"); + YYABORT; + } + } +#line 3090 "y.tab.c" /* yacc.c:1646 */ + break; + + case 122: +#line 1498 "Oberon.y" /* yacc.c:1646 */ + { + if (Types_IsChar(Trees_Type(currentCaseExpression))) { + if (strlen((yyvsp[0].string)) <= 1) { + (yyval.node) = Trees_NewChar((yyvsp[0].string)[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "single-character string expected: %s\n", (yyvsp[0].string)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected string label: %s\n", (yyvsp[0].string)); + YYABORT; + } + } +#line 3110 "y.tab.c" /* yacc.c:1646 */ + break; + + case 123: +#line 1514 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node caseExpTypeStruct, constValue, caseVariable; + + (yyval.node) = Table_At(Trees_Name((yyvsp[0].node))); + if ((yyval.node) != NULL) { + caseExpTypeStruct = Types_Structure(Trees_Type(currentCaseExpression)); + switch (Trees_Symbol(caseExpTypeStruct)) { + case TREES_INTEGER_TYPE: + constValue = Trees_Value((yyval.node)); + if (Trees_Symbol(constValue) == INTEGER) { + if (Trees_Integer(constValue) >= 0) { + (yyval.node) = constValue; + } else { + Oberon_PrintContext(); + fprintf(stderr, "non-negative case label expected: %" OBNC_INT_MOD "d\n", Trees_Integer(constValue)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer label expected\n"); + YYABORT; + } + break; + case TREES_CHAR_TYPE: + constValue = Trees_Value((yyval.node)); + if (Trees_Symbol(constValue) == STRING) { + if (Types_StringLength(Trees_Type(constValue)) <= 1) { + (yyval.node) = Trees_NewChar(Trees_String(constValue)[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "single-character string expected: %s\n", Trees_String(constValue)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: character label expected\n"); + YYABORT; + } + break; + case RECORD: + if (Types_IsRecord((yyval.node))) { + if (Types_Extends(Trees_Type(currentCaseExpression), (yyval.node))) { + caseVariable = Trees_Left(currentCaseExpression); + Trees_SetType((yyval.node), caseVariable); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: extended type expected in label\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record type label expected\n"); + YYABORT; + } + break; + case POINTER: + if (Types_IsPointer((yyval.node))) { + if (Types_Extends(Trees_Type(currentCaseExpression), (yyval.node))) { + caseVariable = Trees_Left(currentCaseExpression); + Trees_SetType((yyval.node), caseVariable); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: extended type expected in label\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: pointer type label expected\n"); + YYABORT; + } + break; + default: + assert(0); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + } +#line 3195 "y.tab.c" /* yacc.c:1646 */ + break; + + case 124: +#line 1598 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(WHILE, (yyvsp[-4].node), Trees_NewNode(DO, (yyvsp[-2].node), (yyvsp[-1].node))); + } +#line 3203 "y.tab.c" /* yacc.c:1646 */ + break; + + case 125: +#line 1605 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(ELSIF, (yyvsp[-2].node), Trees_NewNode(THEN, (yyvsp[0].node), (yyvsp[-4].node))); + } +#line 3211 "y.tab.c" /* yacc.c:1646 */ + break; + + case 126: +#line 1609 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 3219 "y.tab.c" /* yacc.c:1646 */ + break; + + case 127: +#line 1616 "Oberon.y" /* yacc.c:1646 */ + { + CheckIsValueExpression((yyvsp[0].node)); + (yyval.node) = NULL; + if (Types_IsBoolean(Trees_Type((yyvsp[0].node)))) { + (yyval.node) = Trees_NewNode(REPEAT, (yyvsp[-2].node), (yyvsp[0].node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: boolean expression expected\n"); + YYABORT; + } + } +#line 3235 "y.tab.c" /* yacc.c:1646 */ + break; + + case 128: +#line 1632 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node byExp; + + if ((yyvsp[-3].node) != NULL) { + byExp = (yyvsp[-3].node); + } else { + byExp = Trees_NewInteger(1); + } + (yyval.node) = Trees_NewNode(FOR, + (yyvsp[-6].node), + Trees_NewNode(TO, + (yyvsp[-4].node), + Trees_NewNode(BY, byExp, (yyvsp[-1].node)))); + } +#line 3254 "y.tab.c" /* yacc.c:1646 */ + break; + + case 129: +#line 1650 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node ctrlVar, ctrlVarType; + + CheckIsValueExpression((yyvsp[0].node)); + ctrlVar = Table_At((yyvsp[-2].ident)); + if (ctrlVar != NULL) { + ctrlVarType = Trees_Type(ctrlVar); + if (Types_IsInteger(ctrlVarType)) { + if (Types_IsInteger(Trees_Type((yyvsp[0].node)))) { + (yyval.node) = Trees_NewNode(BECOMES, ctrlVar, (yyvsp[0].node)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer expression expected as initial value\n"); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer control variable expected: %s\n", (yyvsp[-2].ident)); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared control variable: %s\n", (yyvsp[-2].ident)); + YYABORT; + } + } +#line 3285 "y.tab.c" /* yacc.c:1646 */ + break; + + case 130: +#line 1679 "Oberon.y" /* yacc.c:1646 */ + { + CheckIsValueExpression((yyvsp[0].node)); + if (! Types_IsInteger(Trees_Type((yyvsp[0].node)))) { + Oberon_PrintContext(); + fprintf(stderr, "error: integer expression expected as upper limit\n"); + YYABORT; + } + } +#line 3298 "y.tab.c" /* yacc.c:1646 */ + break; + + case 131: +#line 1691 "Oberon.y" /* yacc.c:1646 */ + { + if (IsInteger((yyvsp[0].node))) { + if (Trees_Integer((yyvsp[0].node)) != 0) { + (yyval.node) = (yyvsp[0].node); + } else { + Oberon_PrintContext(); + fprintf(stderr, "warning: steps by zero leads to infinite loop\n"); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: integer increment expected\n"); + YYABORT; + } + } +#line 3317 "y.tab.c" /* yacc.c:1646 */ + break; + + case 132: +#line 1706 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 3325 "y.tab.c" /* yacc.c:1646 */ + break; + + case 133: +#line 1716 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node procIdent, procType, resultType, procStatements, returnExp; + const char *procName; + + procIdent = (yyvsp[-6].node); + procName = Trees_Name(procIdent); + procType = Trees_Type((yyvsp[-6].node)); + resultType = Types_ResultType(procType); + procStatements = (yyvsp[-3].node); + returnExp = (yyvsp[-2].node); + + if (strcmp(procName, (yyvsp[0].ident)) == 0) { + if (resultType == NULL) { + if (returnExp != NULL) { + Oberon_PrintContext(); + fprintf(stderr, "error: unexpected return expression\n"); + YYABORT; + } + } else { + if (returnExp != NULL) { + CheckIsValueExpression(returnExp); + ValidateAssignment(returnExp, resultType, PROC_RESULT_CONTEXT, 0); + if ((Trees_Symbol(returnExp) == STRING) && Types_IsChar(resultType)) { + returnExp = Trees_NewChar(Trees_String(returnExp)[0]); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: return expression expected\n"); + YYABORT; + } + } + if (procStatements != NULL) { + Generate_ProcedureStatements(procStatements); + } + if (returnExp != NULL) { + Generate_ReturnClause(returnExp); + } + if (procedureDeclarationStack != NULL) { + procedureDeclarationStack = Trees_Right(procedureDeclarationStack); + } + Generate_ProcedureEnd(procIdent); + Table_CloseScope(); + } else { + Oberon_PrintContext(); + fprintf(stderr, "expected procedure name: %s\n", procName); + YYABORT; + } + } +#line 3378 "y.tab.c" /* yacc.c:1646 */ + break; + + case 134: +#line 1768 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node paramList, param; + + (yyval.node) = NULL; + Table_CloseScope(); + Trees_SetType((yyvsp[0].node), (yyvsp[-1].node)); + Table_OpenScope(); + + /*reenter parameters in the symbol table*/ + paramList = Types_Parameters((yyvsp[0].node)); + while (paramList != NULL) { + param = Trees_Left(paramList); + Table_Put(param); + paramList = Trees_Right(paramList); + } + + procedureDeclarationStack = Trees_NewNode(TREES_NOSYM, (yyvsp[-1].node), procedureDeclarationStack); + Generate_ProcedureHeading((yyvsp[-1].node)); + (yyval.node) = (yyvsp[-1].node); + } +#line 3403 "y.tab.c" /* yacc.c:1646 */ + break; + + case 135: +#line 1792 "Oberon.y" /* yacc.c:1646 */ + { + if (! (Trees_Exported((yyvsp[0].node)) && Trees_Local((yyvsp[0].node)))) { + Trees_SetKind(TREES_PROCEDURE_KIND, (yyvsp[0].node)); + Table_Put((yyvsp[0].node)); + Table_OpenScope(); + } else { + Oberon_PrintContext(); + fprintf(stderr, "cannot export local procedure: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + (yyval.node) = (yyvsp[0].node); + } +#line 3420 "y.tab.c" /* yacc.c:1646 */ + break; + + case 136: +#line 1808 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[0].node); + } +#line 3428 "y.tab.c" /* yacc.c:1646 */ + break; + + case 137: +#line 1812 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 3436 "y.tab.c" /* yacc.c:1646 */ + break; + + case 138: +#line 1819 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[0].node); + } +#line 3444 "y.tab.c" /* yacc.c:1646 */ + break; + + case 139: +#line 1823 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 3452 "y.tab.c" /* yacc.c:1646 */ + break; + + case 145: +#line 1844 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node unresolvedPointerType, undeclaredBaseType; + + if (unresolvedPointerTypes != NULL) { + unresolvedPointerType = Trees_Left(unresolvedPointerTypes); + undeclaredBaseType = Types_PointerBaseType(unresolvedPointerType); + Oberon_PrintContext(); + fprintf(stderr, "undeclared pointer base type: %s\n", Trees_Name(undeclaredBaseType)); + YYABORT; + } + } +#line 3468 "y.tab.c" /* yacc.c:1646 */ + break; + + case 146: +#line 1856 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 3476 "y.tab.c" /* yacc.c:1646 */ + break; + + case 147: +#line 1863 "Oberon.y" /* yacc.c:1646 */ + { + unresolvedPointerTypes = NULL; + } +#line 3484 "y.tab.c" /* yacc.c:1646 */ + break; + + case 156: +#line 1890 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Types_NewProcedure((yyvsp[-2].node), (yyvsp[0].node)); + } +#line 3492 "y.tab.c" /* yacc.c:1646 */ + break; + + case 157: +#line 1897 "Oberon.y" /* yacc.c:1646 */ + { + Trees_ReverseList(&(yyvsp[0].node)); /*correct order*/ + (yyval.node) = (yyvsp[0].node); + } +#line 3501 "y.tab.c" /* yacc.c:1646 */ + break; + + case 158: +#line 1902 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 3509 "y.tab.c" /* yacc.c:1646 */ + break; + + case 159: +#line 1909 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = (yyvsp[0].node); + Trees_ReverseList(&(yyval.node)); + } +#line 3518 "y.tab.c" /* yacc.c:1646 */ + break; + + case 160: +#line 1914 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node curr; + + /*make one list of the two lists*/ + (yyval.node) = (yyvsp[-2].node); + curr = (yyvsp[0].node); + do { + (yyval.node) = Trees_NewNode(TREES_IDENT_LIST, Trees_Left(curr), (yyval.node)); + curr = Trees_Right(curr); + } while (curr != NULL); + /*$$ in reversed order*/ + } +#line 3535 "y.tab.c" /* yacc.c:1646 */ + break; + + case 161: +#line 1930 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = ResolvedType((yyvsp[0].node), 0); + if ((yyval.node) != NULL) { + if (Trees_Symbol((yyval.node)) == IDENT) { + if (Trees_Kind((yyval.node)) != TREES_TYPE_KIND) { + Oberon_PrintContext(); + fprintf(stderr, "type name expected as result type: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + if (! Types_Scalar((yyval.node))) { + Oberon_PrintContext(); + fprintf(stderr, "scalar result type expected: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name((yyvsp[0].node))); + YYABORT; + } + } +#line 3561 "y.tab.c" /* yacc.c:1646 */ + break; + + case 162: +#line 1952 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = NULL; + } +#line 3569 "y.tab.c" /* yacc.c:1646 */ + break; + + case 163: +#line 1959 "Oberon.y" /* yacc.c:1646 */ + { + Trees_Node curr, ident; + + Trees_ReverseList(&(yyvsp[-2].node)); /*correct order*/ + curr = (yyvsp[-2].node); + do { + ident = Trees_Left(curr); + Trees_SetKind((yyvsp[-3].integer), ident); + Trees_SetType((yyvsp[0].node), ident); + Trees_SetLocal(ident); + if (! Table_LocallyDeclared(Trees_Name(ident))) { + Table_Put(ident); + } else { + Oberon_PrintContext(); + fprintf(stderr, "redeclaration of identifier with the same name: %s\n", Trees_Name(ident)); + YYABORT; + } + curr = Trees_Right(curr); + } while (curr != NULL); + + (yyval.node) = (yyvsp[-2].node); + } +#line 3596 "y.tab.c" /* yacc.c:1646 */ + break; + + case 164: +#line 1985 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = TREES_VAR_PARAM_KIND; + } +#line 3604 "y.tab.c" /* yacc.c:1646 */ + break; + + case 165: +#line 1989 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = TREES_VALUE_PARAM_KIND; + } +#line 3612 "y.tab.c" /* yacc.c:1646 */ + break; + + case 166: +#line 1996 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_IDENT_LIST, Trees_NewIdent((yyvsp[0].ident)), NULL); + } +#line 3620 "y.tab.c" /* yacc.c:1646 */ + break; + + case 167: +#line 2000 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_IDENT_LIST, Trees_NewIdent((yyvsp[0].ident)), (yyvsp[-2].node)); + } +#line 3628 "y.tab.c" /* yacc.c:1646 */ + break; + + case 168: +#line 2007 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = ResolvedType((yyvsp[0].node), 0); + if ((yyval.node) != NULL) { + if ((yyvsp[-1].integer)) { + (yyval.node) = Types_NewArray(NULL, (yyval.node)); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name((yyvsp[0].node))); + exit(EXIT_FAILURE); + } + } +#line 3645 "y.tab.c" /* yacc.c:1646 */ + break; + + case 169: +#line 2023 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = 1; + } +#line 3653 "y.tab.c" /* yacc.c:1646 */ + break; + + case 170: +#line 2027 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.integer) = 0; + } +#line 3661 "y.tab.c" /* yacc.c:1646 */ + break; + + case 171: +#line 2037 "Oberon.y" /* yacc.c:1646 */ + { + static char symfilePath[PATH_MAX + 1]; + + if (strcmp((yyvsp[-1].ident), inputModuleName) == 0) { + Generate_ModuleEnd(); + Generate_Close(); + + sprintf(symfilePath, ".obnc/%s.sym", inputModuleName); + if (parseMode == OBERON_ENTRY_POINT_MODE) { + if (Files_Exists(symfilePath)) { + Files_Remove(symfilePath); + } + } else { + ExportSymbolTable(symfilePath); + } + YYACCEPT; + } else { + Oberon_PrintContext(); + fprintf(stderr, "expected identifier %s\n", inputModuleName); + YYABORT; + } + } +#line 3688 "y.tab.c" /* yacc.c:1646 */ + break; + + case 172: +#line 2064 "Oberon.y" /* yacc.c:1646 */ + { + if (strcmp((yyvsp[0].ident), inputModuleName) == 0) { + if (parseMode != OBERON_IMPORT_LIST_MODE) { + Generate_ModuleHeading(); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "module name does not match filename: %s\n", (yyvsp[0].ident)); + YYABORT; + } + } +#line 3704 "y.tab.c" /* yacc.c:1646 */ + break; + + case 173: +#line 2079 "Oberon.y" /* yacc.c:1646 */ + { + if (parseMode == OBERON_IMPORT_LIST_MODE) { + YYACCEPT; + } + } +#line 3714 "y.tab.c" /* yacc.c:1646 */ + break; + + case 174: +#line 2085 "Oberon.y" /* yacc.c:1646 */ + { + if (parseMode == OBERON_IMPORT_LIST_MODE) { + YYACCEPT; + } + } +#line 3724 "y.tab.c" /* yacc.c:1646 */ + break; + + case 175: +#line 2094 "Oberon.y" /* yacc.c:1646 */ + { + static char impfilePath[PATH_MAX + 1]; + Trees_Node moduleAndDirPath, module, p; + FILE *impFile; + const char *name; + + Trees_ReverseList(&(yyvsp[-1].node)); /*correct order*/ + if (parseMode == OBERON_IMPORT_LIST_MODE) { + while ((yyvsp[-1].node) != NULL) { + module = Trees_Left((yyvsp[-1].node)); + puts(Trees_Name(module)); + (yyvsp[-1].node) = Trees_Right((yyvsp[-1].node)); + } + } else { + sprintf(impfilePath, ".obnc/%s.imp", inputModuleName); + if (parseMode == OBERON_ENTRY_POINT_MODE) { + if (Files_Exists(impfilePath)) { + Files_Remove(impfilePath); + } + } else { + impFile = Files_New(impfilePath); + p = (yyvsp[-1].node); + do { + moduleAndDirPath = Trees_Left(p); + module = Trees_Left(moduleAndDirPath); + name = Trees_UnaliasedName(module); + fprintf(impFile, "%s\n", name); + p = Trees_Right(p); + } while (p != NULL); + Files_Close(impFile); + } + Generate_ImportList((yyvsp[-1].node)); + } + } +#line 3763 "y.tab.c" /* yacc.c:1646 */ + break; + + case 176: +#line 2132 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_NOSYM, (yyvsp[0].node), NULL); + } +#line 3771 "y.tab.c" /* yacc.c:1646 */ + break; + + case 177: +#line 2136 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.node) = Trees_NewNode(TREES_NOSYM, (yyvsp[0].node), (yyvsp[-2].node)); + } +#line 3779 "y.tab.c" /* yacc.c:1646 */ + break; + + case 178: +#line 2143 "Oberon.y" /* yacc.c:1646 */ + { + static Maps_Map importedModules = NULL; + static char symbolFileDir[PATH_MAX + 1]; + static char symbolFileName[PATH_MAX + 1]; + static char moduleDirPath[PATH_MAX + 1]; + const char *module, *qualifier; + Trees_Node qualifierSym, moduleIdent; + + if (importedModules == NULL) { + importedModules = Maps_New(); + } + if ((yyvsp[0].ident) != NULL) { + module = (yyvsp[0].ident); + qualifier = (yyvsp[-1].ident); + } else { + module = (yyvsp[-1].ident); + qualifier = (yyvsp[-1].ident); + } + (yyval.node) = NULL; + if (strcmp(module, inputModuleName) != 0) { + if (! Maps_HasKey(module, importedModules)) { + Maps_Put(module, NULL, &importedModules); + qualifierSym = Table_At(qualifier); + if (qualifierSym == NULL) { + qualifierSym = Trees_NewIdent(qualifier); + if ((yyvsp[0].ident) != NULL) { + Trees_SetUnaliasedName(module, qualifierSym); + } + Trees_SetKind(TREES_QUALIFIER_KIND, qualifierSym); + Table_Put(qualifierSym); + + if (parseMode == OBERON_IMPORT_LIST_MODE) { + (yyval.node) = Trees_NewIdent(module); + } else { + Path_Get(module, moduleDirPath, LEN(moduleDirPath)); + if (moduleDirPath[0] != '\0') { + /*import identifiers into the symbol table*/ + sprintf(symbolFileDir, "%s/.obnc", moduleDirPath); + if (! Files_Exists(symbolFileDir)) { + sprintf(symbolFileDir, "%s", moduleDirPath); + } + sprintf(symbolFileName, "%s/%s.sym", symbolFileDir, module); + if (Files_Exists(symbolFileName)) { + Table_Import(symbolFileName, module, qualifier); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: symbol file not found for module %s: %s\n", module, symbolFileName); + YYABORT; + } + + moduleIdent = Trees_NewIdent(module); + Trees_SetKind(TREES_QUALIFIER_KIND, moduleIdent); + (yyval.node) = Trees_NewNode(TREES_NOSYM, moduleIdent, Trees_NewString(moduleDirPath)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: imported module not found: %s\n", module); + YYABORT; + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: qualifier already used: %s\n", qualifier); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: module already imported: %s\n", module); + YYABORT; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: a module cannot import itself\n"); + YYABORT; + } + } +#line 3859 "y.tab.c" /* yacc.c:1646 */ + break; + + case 179: +#line 2222 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.ident) = (yyvsp[0].ident); + } +#line 3867 "y.tab.c" /* yacc.c:1646 */ + break; + + case 180: +#line 2226 "Oberon.y" /* yacc.c:1646 */ + { + (yyval.ident) = NULL; + } +#line 3875 "y.tab.c" /* yacc.c:1646 */ + break; + + case 181: +#line 2233 "Oberon.y" /* yacc.c:1646 */ + { + Generate_ModuleStatements((yyvsp[0].node)); + } +#line 3883 "y.tab.c" /* yacc.c:1646 */ + break; + + +#line 3887 "y.tab.c" /* yacc.c:1646 */ + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 2238 "Oberon.y" /* yacc.c:1906 */ + + +static char *ModuleName(const char filename[]) +{ + char *result; + int startPos, endPos, resultLen, i; + + endPos = strlen(filename); + do { + endPos--; + } while ((endPos >= 0) && (filename[endPos] != '.')); + assert(endPos > 0); + assert(filename[endPos] == '.'); + + startPos = endPos - 1; + while ((startPos >= 0) && isalnum(filename[startPos])) { + startPos--; + } + if ((startPos < 0) || ! isalnum(filename[startPos])) { + startPos++; + } + + resultLen = endPos - startPos; + NEW_ARRAY(result, resultLen); + for (i = 0; i < resultLen; i++) { + result[i] = filename[startPos + i]; + } + return result; +} + + +void Oberon_Parse(const char inputFile[], int mode) +{ + int error; + + Table_Init(); + inputFilename = inputFile; + parseMode = mode; + inputModuleName = ModuleName(inputFile); + + yyin = fopen(inputFile, "r"); + if (yyin != NULL) { + if (mode != OBERON_IMPORT_LIST_MODE) { + Generate_Open(inputModuleName, mode == OBERON_ENTRY_POINT_MODE); + } + error = yyparse(); + if (error) { + fprintf(stderr, "compilation failed\n"); + exit(1); + } + } else { + fprintf(stderr, "obnc-compile: error: cannot open file: %s: %s\n", inputFile, strerror(errno)); + exit(1); + } +} + + +/*NOTE: prefer Oberon_PrintContext and fprintf over yyerror since a C compiler cannot type-check the format string of yyerror*/ + +void Oberon_PrintContext(void) +{ + fprintf(stderr, "obnc-compile: %s:%d: ", inputFilename, yylineno); +} + + +void yyerror(const char format[], ...) +{ + va_list ap; + + Oberon_PrintContext(); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fputc('\n', stderr); +} + + +/*accessor functions*/ + +static char *QualidentName(const char qualifier[], const char ident[]) +{ + int resultLen; + char *result; + + resultLen = strlen(qualifier) + strlen(".") + strlen(ident) + 1; + NEW_ARRAY(result, resultLen); + sprintf(result, "%s.%s", qualifier, ident); + return result; +} + + +/*constant predicate functions*/ + +static int IsBoolean(Trees_Node node) +{ + return (Trees_Symbol(node) == TRUE) || (Trees_Symbol(node) == FALSE); +} + + +static int IsChar(Trees_Node node) +{ + return Trees_Symbol(node) == TREES_CHAR_CONSTANT; +} + + +static int IsInteger(Trees_Node node) +{ + return Trees_Symbol(node) == INTEGER; +} + + +static int IsReal(Trees_Node node) +{ + return Trees_Symbol(node) == REAL; +} + + +static int IsString(Trees_Node node) +{ + return Trees_Symbol(node) == STRING; +} + + +static int IsSet(Trees_Node node) +{ + return Trees_Symbol(node) == TREES_SET_CONSTANT; +} + + +/*functions for type declaration productions*/ + +static Trees_Node ResolvedType(Trees_Node type, int isTypeDecl) +{ + Trees_Node result, identDef, typeStruct; + const char *name; + + result = NULL; + if (Trees_Symbol(type) == IDENT) { + name = Trees_Name(type); + identDef = Table_At(name); + if (identDef != NULL) { + if (Trees_Kind(identDef) == TREES_TYPE_KIND) { + typeStruct = Types_Structure(identDef); + if (typeStruct != NULL) { + if (Types_Basic(Trees_Type(identDef)) && ! isTypeDecl) { + result = Trees_Type(identDef); + } else { + result = identDef; + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unresolved type: %s\n", name); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "type expected: %s\n", name); + exit(EXIT_FAILURE); + } + } + } else { + result = type; + } + return result; +} + + +static void ResolvePointerTypes(Trees_Node baseType) +{ + const char *baseTypeName; + Trees_Node prev, curr, currPointerType, currBaseType; + + assert(Trees_Symbol(baseType) == IDENT); + baseTypeName = Trees_Name(baseType); + + prev = NULL; + curr = unresolvedPointerTypes; + while (curr != NULL) { + currPointerType = Trees_Left(curr); + currBaseType = Types_PointerBaseType(currPointerType); + if (strcmp(Trees_Name(currBaseType), baseTypeName) == 0) { + if (Types_IsRecord(baseType)) { + /*update pointer base type*/ + Types_SetPointerBaseType(baseType, currPointerType); + /*delete current node*/ + if (curr == unresolvedPointerTypes) { + unresolvedPointerTypes = Trees_Right(curr); + } else { + Trees_SetRight(Trees_Right(curr), prev); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "record type expected in declaration of pointer base type: %s\n", baseTypeName); + exit(EXIT_FAILURE); + } + } + prev = curr; + curr = Trees_Right(curr); + } +} + + +static const char *TypeString(Trees_Node type) +{ + const char *result = ""; + + assert(Types_IsType(type)); + + switch (Trees_Symbol(type)) { + case IDENT: + result = Trees_Name(type); + break; + case TREES_STRING_TYPE: + switch (Types_StringLength(type)) { + case 0: + result = "empty string"; + break; + case 1: + result = "single-char string"; + break; + default: + result = "multi-char string"; + } + break; + case TREES_BOOLEAN_TYPE: + result = "BOOLEAN"; + break; + case TREES_CHAR_TYPE: + result = "CHAR"; + break; + case TREES_INTEGER_TYPE: + result = "INTEGER"; + break; + case TREES_REAL_TYPE: + result = "REAL"; + break; + case TREES_BYTE_TYPE: + result = "BYTE"; + break; + case TREES_SET_TYPE: + result = "SET"; + break; + case ARRAY: + if (Types_IsOpenArray(type)) { + result = "open ARRAY"; + } else { + result = "anon ARRAY"; + } + break; + case RECORD: + result = "anon RECORD"; + break; + case POINTER: + result = "anon POINTER"; + break; + case PROCEDURE: + result = "anon PROCEDURE"; + break; + default: + assert(0); + } + return result; +} + + +/*functions for expression productions*/ + +static int IsDesignator(Trees_Node exp) +{ + return Trees_Symbol(exp) == TREES_DESIGNATOR; +} + + +static void CheckIsValueExpression(Trees_Node exp) +{ + Trees_Node ident; + + if (Trees_Symbol(exp) == TREES_DESIGNATOR) { + ident = Trees_Left(exp); + switch (Trees_Kind(ident)) { + case TREES_CONSTANT_KIND: + case TREES_FIELD_KIND: + case TREES_VARIABLE_KIND: + case TREES_PROCEDURE_KIND: + case TREES_VALUE_PARAM_KIND: + case TREES_VAR_PARAM_KIND: + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "value expected: %s\n", Trees_Name(ident)); + exit(EXIT_FAILURE); + } + } +} + + +static Trees_Node Designator(const char identName[], Trees_Node selectorList) +{ + Trees_Node identSym, qualidentSym, designator, qualidentSelectorList; + const char *qualidentName; + + /*set qualident name, symbol and selector list*/ + qualidentSym = NULL; + qualidentSelectorList = NULL; + if ((procedureDeclarationStack != NULL) + && (strcmp(identName, Trees_Name(Trees_Left(procedureDeclarationStack))) == 0)) { + qualidentSym = Trees_Left(procedureDeclarationStack); + qualidentSelectorList = selectorList; + } else { + identSym = Table_At(identName); + if (identSym != NULL) { + if (Trees_Kind(identSym) == TREES_QUALIFIER_KIND) { + if ((selectorList != NULL) && (Trees_Symbol(selectorList) == '.')) { + qualidentName = QualidentName(identName, Trees_Name(Trees_Left(selectorList))); + qualidentSym = Table_At(qualidentName); + qualidentSelectorList = Trees_Right(selectorList); + if (qualidentSym == NULL) { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", qualidentName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "'.' expected after qualifier: %s\n", identName); + exit(EXIT_FAILURE); + } + } else { + qualidentSym = identSym; + qualidentSelectorList = selectorList; + } + + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", identName); + exit(EXIT_FAILURE); + } + } + assert(qualidentSym != NULL); + + designator = Trees_NewNode(TREES_DESIGNATOR, qualidentSym, qualidentSelectorList); + + return designator; +} + + +static Trees_Node BaseIdent(Trees_Node designator) +{ + assert(Trees_Symbol(designator) == TREES_DESIGNATOR); + + return Trees_Left(designator); +} + + +static Trees_Node FirstSelector(Trees_Node designator) +{ + assert(Trees_Symbol(designator) == TREES_DESIGNATOR); + + return Trees_Right(designator); +} + + +static void SetSelectorTypes(Trees_Node identType, Trees_Node designator, int *parameterListFound) +{ + Trees_Node currType, currTypeStruct, currSelector, prevSelector, indexExp, lengthNode, pointerNode, expList, extendedType, symbol, varField, typeField, fieldBaseType; + int length, index; + const char *fieldName; + + currType = identType; + currSelector = FirstSelector(designator); + prevSelector = designator; + *parameterListFound = 0; + while ((currSelector != NULL) && ! *parameterListFound) { + currTypeStruct = Types_Structure(currType); + switch (Trees_Symbol(currSelector)) { + case '[': + if ((currTypeStruct != NULL) && (Trees_Symbol(currTypeStruct) == ARRAY)) { + indexExp = Trees_Left(currSelector); + lengthNode = Types_ArrayLength(currTypeStruct); + if ((lengthNode != NULL) && (Trees_Symbol(indexExp) == INTEGER)) { + length = Trees_Integer(lengthNode); + index = Trees_Integer(indexExp); + if ((index < 0) || (index >= length)) { + Oberon_PrintContext(); + fprintf(stderr, "invalid array index: %d not between 0 and %d\n", index, length - 1); + exit(EXIT_FAILURE); + } + } + Trees_SetType(currType, currSelector); + currType = Types_ElementType(currTypeStruct); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: array variable expected in element selector\n"); + exit(EXIT_FAILURE); + } + break; + case '.': + if (currType != NULL) { + switch (Trees_Symbol(currTypeStruct)) { + case POINTER: + pointerNode = Trees_NewNode('^', NULL, currSelector); + Trees_SetType(currType, pointerNode); + Trees_SetRight(pointerNode, prevSelector); + currType = Types_PointerBaseType(currTypeStruct); + /*fall through*/ + case RECORD: + Trees_SetType(currType, currSelector); + varField = Trees_Left(currSelector); + fieldName = Trees_Name(varField); + Types_GetFieldIdent(fieldName, currType, Trees_Imported(BaseIdent(designator)), &typeField, &fieldBaseType); + if (typeField != NULL) { + if (Trees_Exported(typeField)) { + Trees_SetExported(varField); + } + currType = Trees_Type(typeField); + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared field: %s\n", fieldName); + exit(EXIT_FAILURE); + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "error: record variable expected in field selector\n"); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: record variable expected in field selector\n"); + exit(EXIT_FAILURE); + } + break; + case '^': + if ((currType != NULL) && (Trees_Symbol(currTypeStruct) == POINTER)) { + Trees_SetType(currType, currSelector); + currType = Types_PointerBaseType(currTypeStruct); + } else { + Oberon_PrintContext(); + fprintf(stderr, "error: pointer variable expected in pointer dereference\n"); + exit(EXIT_FAILURE); + } + break; + case '(': + if (Types_IsProcedure(currTypeStruct)) { + *parameterListFound = 1; + } else if (Types_IsRecord(currTypeStruct) || Types_IsPointer(currTypeStruct)) { + /*type guard*/ + expList = Trees_Left(currSelector); + if (Trees_Right(expList) == NULL) { + if ((Trees_Symbol(Trees_Left(expList)) == TREES_DESIGNATOR) + && (Trees_Right(Trees_Left(expList)) == NULL)) { + extendedType = Trees_Left(Trees_Left(expList)); + symbol = Table_At(Trees_Name(extendedType)); + if (symbol != NULL) { + if (Trees_Kind(symbol) == TREES_TYPE_KIND) { + if ((Types_IsRecord(currType) && Types_IsRecord(Trees_Type(symbol))) + || (Types_IsPointer(currType) && Types_IsPointer(Trees_Type(symbol)))) { + if (Types_Extends(currType, Trees_Type(symbol))) { + Trees_SetLeft(extendedType, currSelector); + Trees_SetType(extendedType, currSelector); + currType = extendedType; + } else { + Oberon_PrintContext(); + fprintf(stderr, "extended type expected: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } else { + if (Types_IsRecord(currType)) { + Oberon_PrintContext(); + fprintf(stderr, "record type expected in type guard: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "pointer type expected in type guard: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "type name expected: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "undeclared identifier: %s\n", Trees_Name(extendedType)); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "identifier expected in type guard\n"); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected comma in type guard\n"); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected parenthesis in designator which is not a record, pointer or procedure\n"); + exit(EXIT_FAILURE); + } + break; + default: + assert(0); + } + prevSelector = currSelector; + currSelector = Trees_Right(currSelector); + } + + if (currSelector == NULL) { + Trees_SetType(currType, designator); + } else { + Oberon_PrintContext(); + fprintf(stderr, "unexpected selector after procedure call\n"); + exit(EXIT_FAILURE); + } +} + + +static void RemoveActualParameters(Trees_Node *designator, Trees_Node *actualParameters) +{ + Trees_Node currSelector; + + currSelector = FirstSelector(*designator); + assert(currSelector != NULL); + if (Trees_Right(currSelector) == NULL) { + *actualParameters = Trees_Left(currSelector); + Trees_SetRight(NULL, *designator); + } else { + while (Trees_Right(Trees_Right(currSelector)) != NULL) { + currSelector = Trees_Right(currSelector); + } + *actualParameters = Trees_Left(Trees_Right(currSelector)); + Trees_SetRight(NULL, currSelector); + } +} + + +static Trees_Node ExpressionConstValue(int relation, Trees_Node expA, Trees_Node expB) +{ + Trees_Node result = NULL; + + switch (relation) { + case '=': + switch (Trees_Symbol(expA)) { + case TRUE: + case FALSE: + if (IsBoolean(expB)) { + result = Trees_NewLeaf((Trees_Symbol(expA) == Trees_Symbol(expB))? TRUE: FALSE); + } + break; + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) == Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) == Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) == Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) == Trees_Real(expB))? TRUE: FALSE); + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewLeaf((Trees_Set(expA) == Trees_Set(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] == Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) == 0)? TRUE: FALSE); + } + break; + } + break; + case '#': + switch (Trees_Symbol(expA)) { + case TRUE: + case FALSE: + if (IsBoolean(expB)) { + result = Trees_NewLeaf((Trees_Symbol(expA) != Trees_Symbol(expB))? TRUE: FALSE); + } + break; + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) != Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) != Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) != Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) != Trees_Real(expB))? TRUE: FALSE); + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewLeaf((Trees_Set(expA) != Trees_Set(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] != Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) != 0)? TRUE: FALSE); + } + break; + } + break; + case '<': + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) < Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) < Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) < Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) < Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] < Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) < 0)? TRUE: FALSE); + } + break; + } + break; + case LE: + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) <= Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) <= Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) <= Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) <= Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] <= Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) <= 0)? TRUE: FALSE); + } + break; + } + break; + case '>': + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) > Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) > Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) > Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) > Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] > Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) > 0)? TRUE: FALSE); + } + break; + } + break; + case GE: + switch (Trees_Symbol(expA)) { + case TREES_CHAR_CONSTANT: + if (IsString(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) >= Trees_String(expB)[0])? TRUE: FALSE); + } else if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_Char(expA) >= Trees_Char(expB))? TRUE: FALSE); + } + break; + case INTEGER: + if (IsInteger(expB)) { + result = Trees_NewLeaf((Trees_Integer(expA) >= Trees_Integer(expB))? TRUE: FALSE); + } + break; + case REAL: + if (IsReal(expB)) { + result = Trees_NewLeaf((Trees_Real(expA) >= Trees_Real(expB))? TRUE: FALSE); + } + break; + case STRING: + if (IsChar(expB)) { + result = Trees_NewLeaf((Trees_String(expA)[0] >= Trees_Char(expB))? TRUE: FALSE); + } else if (IsString(expB)) { + result = Trees_NewLeaf((strcmp(Trees_String(expA), Trees_String(expB)) >= 0)? TRUE: FALSE); + } + break; + } + break; + case IN: + if (IsInteger(expA)) { + Range_CheckSetElement(Trees_Integer(expA)); + if (IsSet(expB)) { + result = Trees_NewLeaf(OBNC_IN(Trees_Integer(expA), Trees_Set(expB))? TRUE: FALSE); + } + } + break; + } + if (result != NULL) { + Trees_SetType(Trees_NewLeaf(TREES_BOOLEAN_TYPE), result); + } + + return result; +} + + +static Trees_Node SimpleExpressionConstValue(int operator, Trees_Node expA, Trees_Node expB) +{ + Trees_Node result = NULL; + + switch (operator) { + case '+': + switch (Trees_Symbol(expA)) { + case INTEGER: + if (expB == NULL) { + result = expA; + } else if (IsInteger(expB)) { + Range_CheckIntSum(Trees_Integer(expA), Trees_Integer(expB)); + result = Trees_NewInteger(Trees_Integer(expA) + Trees_Integer(expB)); + } + break; + case REAL: + if (expB == NULL) { + result = expA; + } else if (IsReal(expB)) { + Range_CheckRealSum(Trees_Real(expA), Trees_Real(expB)); + result = Trees_NewReal(Trees_Real(expA) + Trees_Real(expB)); + } + break; + case TREES_SET_CONSTANT: + if (expB == NULL) { + result = expA; + } else if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) | Trees_Set(expB)); + } + break; + } + break; + case '-': + switch (Trees_Symbol(expA)) { + case INTEGER: + if (expB == NULL) { + Range_CheckIntDiff(0, Trees_Integer(expA)); + result = Trees_NewInteger(-Trees_Integer(expA)); + } else if (IsInteger(expB)) { + Range_CheckIntDiff(Trees_Integer(expA), Trees_Integer(expB)); + result = Trees_NewInteger(Trees_Integer(expA) - Trees_Integer(expB)); + } + break; + case REAL: + if (expB == NULL) { + Range_CheckRealDiff(0.0, Trees_Real(expA)); + result = Trees_NewReal(-Trees_Real(expA)); + } else if (IsReal(expB)) { + Range_CheckRealDiff(Trees_Real(expA), Trees_Real(expB)); + result = Trees_NewReal(Trees_Real(expA) - Trees_Real(expB)); + } + break; + case TREES_SET_CONSTANT: + if (expB == NULL) { + result = Trees_NewSet(~Trees_Set(expA)); + } else if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) & ~Trees_Set(expB)); + } + break; + } + break; + case OR: + if (IsBoolean(expA) && IsBoolean(expB)) { + result = (Trees_Symbol(expA) == TRUE)? expA: expB; + } + break; + } + + return result; +} + + +static Trees_Node TermConstValue(int operator, Trees_Node expA, Trees_Node expB) +{ + Trees_Node result = NULL; + + switch (operator) { + case '*': + switch (Trees_Symbol(expA)) { + case INTEGER: + if (IsInteger(expB)) { + Range_CheckIntProd(Trees_Integer(expA), Trees_Integer(expB)); + result = Trees_NewInteger(Trees_Integer(expA) * Trees_Integer(expB)); + } + break; + case REAL: + if (IsReal(expB)) { + Range_CheckRealProd(Trees_Real(expA), Trees_Real(expB)); + result = Trees_NewReal(Trees_Real(expA) * Trees_Real(expB)); + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) & Trees_Set(expB)); + } + break; + } + break; + case '/': + switch (Trees_Symbol(expA)) { + case REAL: + if (IsReal(expA) && IsReal(expB)) { + if (Trees_Real(expB) != 0) { + result = Trees_NewReal(Trees_Real(expA) / Trees_Real(expB)); + } else { + Oberon_PrintContext(); + fprintf(stderr, "warning: division by zero\n"); + } + } + break; + case TREES_SET_CONSTANT: + if (IsSet(expB)) { + result = Trees_NewSet(Trees_Set(expA) ^ Trees_Set(expB)); + } + break; + } + break; + case DIV: + if (IsInteger(expA) && IsInteger(expB)) { + if (Trees_Integer(expB) > 0) { + result = Trees_NewInteger(OBNC_DIV(Trees_Integer(expA), Trees_Integer(expB))); + } else { + Oberon_PrintContext(); + fprintf(stderr, "positive divisor expected in DIV expression: %" OBNC_INT_MOD "d\n", Trees_Integer(expB)); + exit(EXIT_FAILURE); + } + } + break; + case MOD: + if (IsInteger(expA) && IsInteger(expB)) { + if (Trees_Integer(expB) > 0) { + result = Trees_NewInteger(OBNC_MOD(Trees_Integer(expA), Trees_Integer(expB))); + } else { + Oberon_PrintContext(); + fprintf(stderr, "positive divisor expected in MOD expression: %" OBNC_INT_MOD "d\n", Trees_Integer(expB)); + exit(EXIT_FAILURE); + } + } + break; + case '&': + if (IsBoolean(expA) && IsBoolean(expB)) { + if (Trees_Symbol(expA) == TRUE) { + result = expB; + } else { + result = expA; + } + } + break; + } + + return result; +} + + +static const char *DesignatorString(Trees_Node designator) +{ + const char *baseName; + char *result; + + assert(IsDesignator(designator)); + + baseName = Trees_Name(BaseIdent(designator)); + NEW_ARRAY(result, strlen(baseName) + strlen("...") + 1); + if (FirstSelector(designator) != NULL) { + sprintf(result, "%s...", baseName); + } else { + sprintf(result, "%s", baseName); + } + return result; +} + + +static const char *OperatorString(int operator) +{ + const char *result = ""; + + switch (operator) { + case '+': + result = "+"; + break; + case '-': + result = "-"; + break; + case '*': + result = "*"; + break; + case '/': + result = "/"; + break; + case DIV: + result = "DIV"; + break; + case MOD: + result = "MOD"; + break; + case OR: + result = "OR"; + break; + case '&': + result = "&"; + break; + case '~': + result = "~"; + break; + case '=': + result = "="; + break; + case '#': + result = "#"; + break; + case '<': + result = "<"; + break; + case LE: + result = "<="; + break; + case '>': + result = ">"; + break; + case GE: + result = ">="; + break; + case IN: + result = "IN"; + break; + case IS: + result = "IS"; + break; + default: + assert(0); + } + return result; +} + + +/*functions for statement productions*/ + +static int Writable(Trees_Node designator) +{ + Trees_Node ident, type; + int kind, result; + + assert(IsDesignator(designator)); + + ident = BaseIdent(designator); + kind = Trees_Kind(ident); + type = Trees_Type(ident); + result = ((kind == TREES_VARIABLE_KIND) && ! Trees_Imported(ident)) + || (kind == TREES_VAR_PARAM_KIND) + || ((kind == TREES_VALUE_PARAM_KIND) && ! Types_IsArray(type) && ! Types_IsRecord(type)); + return result; +} + + +static char *AssignmentErrorContext(int context, int paramPos) +{ + char *result; + + NEW_ARRAY(result, 64); + switch (context) { + case ASSIGNMENT_CONTEXT: + strcpy(result, "assignment"); + break; + case PARAM_SUBST_CONTEXT: + assert(paramPos >= 0); + sprintf(result, "substitution of parameter %d", paramPos + 1); + break; + case PROC_RESULT_CONTEXT: + strcpy(result, "return clause"); + break; + default: + assert(0); + } + return result; +} + + +static void ValidateAssignment(Trees_Node expression, Trees_Node targetType, int context, int paramPos) +{ + const char *errorContext; + + assert(expression != NULL); + assert(targetType != NULL); + assert(context >= 0); + assert(paramPos >= 0); + if (Types_AssignmentCompatible(expression, targetType)) { + if (Types_IsByte(targetType) && IsInteger(expression)) { + Range_CheckByte(Trees_Integer(expression)); + } + } else { + errorContext = AssignmentErrorContext(context, paramPos); + if (IsString(expression) && Types_IsCharacterArray(targetType) + && !Types_IsOpenArray(targetType)) { + Oberon_PrintContext(); + fprintf(stderr, "destination array to small in %s\n", errorContext); + exit(EXIT_FAILURE); + } else if (Types_IsPredeclaredProcedure(Trees_Type(expression)) + && Types_IsProcedure(targetType)) { + Oberon_PrintContext(); + fprintf(stderr, "non-predeclared procedure expected in %s\n", errorContext); + exit(EXIT_FAILURE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "incompatible types in %s: %s -> %s\n", + errorContext, TypeString(Trees_Type(expression)), TypeString(targetType)); + exit(EXIT_FAILURE); + } + } +} + + +static void ValidateActualParameter(Trees_Node actualParam, Trees_Node formalParam, int paramPos, Trees_Node procDesignator) +{ + Trees_Node formalType, actualType; + + formalType = Trees_Type(formalParam); + actualType = Trees_Type(actualParam); + + if ((Trees_Kind(formalParam) == TREES_VALUE_PARAM_KIND) + || (IsDesignator(actualParam) && Writable(actualParam))) { + if (Types_IsOpenArray(formalType)) { + if (! Types_ArrayCompatible(actualType, formalType)) { + Oberon_PrintContext(); + fprintf(stderr, "array compatible types expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } else if (Trees_Kind(formalParam) == TREES_VALUE_PARAM_KIND) { + if (! Types_AssignmentCompatible(actualParam, formalType)) { + Oberon_PrintContext(); + fprintf(stderr, "assignment compatible types expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } else if (Trees_Kind(formalParam) == TREES_VAR_PARAM_KIND) { + if (Types_IsRecord(formalType)) { + if (Types_IsRecord(actualType)) { + if (! Types_Extends(formalType, actualType)) { + Oberon_PrintContext(); + fprintf(stderr, "incompatible record types in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "record expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + } + } else { + if (! Types_Same(actualType, formalType)) { + Oberon_PrintContext(); + fprintf(stderr, "same types expected in substitution of parameter %d in %s: %s -> %s\n", paramPos + 1, DesignatorString(procDesignator), TypeString(actualType), TypeString(formalType)); + exit(EXIT_FAILURE); + } + } + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable variable expected in substitution of parameter %d in %s\n", + paramPos + 1, DesignatorString(procDesignator)); + exit(EXIT_FAILURE); + } +} + + +static void ValidateProcedureCall(Trees_Node expList, Trees_Node fpList, Trees_Node procDesignator) +{ + Trees_Node exp, formalParam, fpType; + int pos; + + pos = 0; + while ((expList != NULL) && (fpList != NULL)) { + exp = Trees_Left(expList); + CheckIsValueExpression(exp); + formalParam = Trees_Left(fpList); + fpType = Trees_Type(formalParam); + ValidateActualParameter(exp, formalParam, pos, procDesignator); + + if (Types_IsChar(fpType) && (Trees_Symbol(exp) == STRING)) { + Trees_SetLeft(Trees_NewChar(Trees_String(exp)[0]), expList); + } + expList = Trees_Right(expList); + fpList = Trees_Right(fpList); + pos++; + } + if ((expList == NULL) && (fpList != NULL)) { + Oberon_PrintContext(); + fprintf(stderr, "too few actual parameters in procedure call: %s\n", DesignatorString(procDesignator)); + exit(EXIT_FAILURE); + } else if ((expList != NULL) && (fpList == NULL)) { + Oberon_PrintContext(); + fprintf(stderr, "too many actual parameters in procedure call: %s\n", DesignatorString(procDesignator)); + exit(EXIT_FAILURE); + } +} + + +static Trees_Node PredeclaredProcedureAST(const char procName[], Trees_Node expList, int isFunctionCall) +{ + static const struct { const char *name; int symbol; } symbols[] = { + {"ABS", TREES_ABS_PROC}, + {"ASR", TREES_ASR_PROC}, + {"ASSERT", TREES_ASSERT_PROC}, + {"CHR", TREES_CHR_PROC}, + {"DEC", TREES_DEC_PROC}, + {"EXCL", TREES_EXCL_PROC}, + {"FLOOR", TREES_FLOOR_PROC}, + {"FLT", TREES_FLT_PROC}, + {"INC", TREES_INC_PROC}, + {"INCL", TREES_INCL_PROC}, + {"LEN", TREES_LEN_PROC}, + {"LSL", TREES_LSL_PROC}, + {"NEW", TREES_NEW_PROC}, + {"ODD", TREES_ODD_PROC}, + {"ORD", TREES_ORD_PROC}, + {"PACK", TREES_PACK_PROC}, + {"ROR", TREES_ROR_PROC}, + {"UNPK", TREES_UNPK_PROC}}; + + int paramCount, pos, symbol; + Trees_Node curr, resultType, result; + Trees_Node param[2], paramTypes[2]; + + /*set actual parameters*/ + paramCount = 0; + curr = expList; + while ((paramCount < LEN(param)) && (curr != NULL)) { + param[paramCount] = Trees_Left(curr); + paramTypes[paramCount] = Trees_Type(Trees_Left(curr)); + paramCount++; + curr = Trees_Right(curr); + } + + /*find procedure symbol*/ + pos = 0; + while ((pos < LEN(symbols)) && (strcmp(symbols[pos].name, procName) != 0)) { + pos++; + } + assert(pos < LEN(symbols)); + symbol = symbols[pos].symbol; + + /*validate parameters and build syntax tree*/ + result = NULL; + resultType = NULL; + switch (symbol) { + case TREES_ABS_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + switch (Trees_Symbol(Types_Structure(paramTypes[0]))) { + case TREES_INTEGER_TYPE: + if (IsInteger(param[0])) { + result = Trees_NewInteger(OBNC_ABS_INT(Trees_Integer(param[0]))); + } + break; + case TREES_REAL_TYPE: + if (IsReal(param[0])) { + result = Trees_NewReal(OBNC_ABS_FLT(Trees_Real(param[0]))); + } + break; + case TREES_BYTE_TYPE: + /*do nothing*/ + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "numeric parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + resultType = paramTypes[0]; + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_ODD_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsInteger(paramTypes[0])) { + if (IsInteger(param[0])) { + result = Trees_NewInteger(OBNC_ODD(Trees_Integer(param[0]))); + } + resultType = Trees_NewLeaf(TREES_BOOLEAN_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_LEN_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsArray(paramTypes[0])) { + if (! Types_IsOpenArray(paramTypes[0])) { + result = Types_ArrayLength(paramTypes[0]); + } + resultType = Trees_NewLeaf(TREES_INTEGER_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "array parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_LSL_PROC: /*fall through*/ + case TREES_ASR_PROC: /*fall through*/ + case TREES_ROR_PROC: + if (isFunctionCall) { + if (paramCount == 2) { + if (Types_IsInteger(paramTypes[0])) { + if (Types_IsInteger(paramTypes[1])) { + if (IsInteger(param[1])) { + switch (symbol) { + case TREES_LSL_PROC: + Range_CheckLSL(Trees_Integer(param[1])); + break; + case TREES_ASR_PROC: + Range_CheckASR(Trees_Integer(param[1])); + break; + case TREES_ROR_PROC: + Range_CheckROR(Trees_Integer(param[1])); + break; + default: + assert(0); + } + } + if (IsInteger(param[0]) && IsInteger(param[1])) { + switch (symbol) { + case TREES_LSL_PROC: + result = Trees_NewInteger(OBNC_LSL(Trees_Integer(param[0]), Trees_Integer(param[1]))); + break; + case TREES_ASR_PROC: + result = Trees_NewInteger(OBNC_ASR(Trees_Integer(param[0]), Trees_Integer(param[1]))); + break; + case TREES_ROR_PROC: + result = Trees_NewInteger(OBNC_ROR(Trees_Integer(param[0]), Trees_Integer(param[1]))); + break; + default: + assert(0); + } + } + resultType = paramTypes[0]; + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_FLOOR_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsReal(paramTypes[0])) { + if (IsReal(param[0])) { + OBNC_LONGR double x = Trees_Real(param[0]); + Range_CheckFLOOR(x); + result = Trees_NewInteger(OBNC_FLOOR(x)); + } + resultType = Trees_NewLeaf(TREES_INTEGER_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "real-valued parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_FLT_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsInteger(paramTypes[0])) { + if (IsInteger(param[0])) { + result = Trees_NewReal(OBNC_FLT(Trees_Integer(param[0]))); + } + resultType = Trees_NewLeaf(TREES_REAL_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_ORD_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + switch (Trees_Symbol(Types_Structure(paramTypes[0]))) { + case TREES_CHAR_TYPE: + /*do nothing*/ + break; + case TREES_STRING_TYPE: + if (Types_StringLength(paramTypes[0]) <= 1) { + result = Trees_NewInteger(Trees_String(param[0])[0]); + } else { + Oberon_PrintContext(); + fprintf(stderr, "single-character string parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_BOOLEAN_TYPE: + if (Trees_Symbol(param[0]) == TRUE) { + result = Trees_NewInteger(1); + } else if (Trees_Symbol(param[0]) == FALSE) { + result = Trees_NewInteger(0); + } + break; + case TREES_SET_TYPE: + if (IsSet(param[0])) { + result = Trees_NewInteger(Trees_Set(param[0])); + } + break; + default: + Oberon_PrintContext(); + fprintf(stderr, "character parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + resultType = Trees_NewLeaf(TREES_INTEGER_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_CHR_PROC: + if (isFunctionCall) { + if (paramCount == 1) { + if (Types_IsInteger(paramTypes[0])) { + if (IsInteger(param[0])) { + int i = Trees_Integer(param[0]); + Range_CheckCHR(i); + result = Trees_NewChar(OBNC_CHR(i)); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + resultType = Trees_NewLeaf(TREES_CHAR_TYPE); + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_INC_PROC: /*fall through*/ + case TREES_DEC_PROC: + if (! isFunctionCall) { + if ((paramCount == 1) || (paramCount == 2)) { + if (IsDesignator(param[0])) { + if (Types_IsInteger(paramTypes[0])) { + if (Writable(param[0])) { + if ((paramCount == 2) && ! Types_IsInteger(paramTypes[1])) { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one or two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_INCL_PROC: /*fall through*/ + case TREES_EXCL_PROC: + if (! isFunctionCall) { + if (paramCount == 2) { + if (IsDesignator(param[0])) { + if (Types_IsSet(paramTypes[0])) { + if (Writable(param[0])) { + if (IsInteger(param[1])) { + Range_CheckSetElement(Trees_Integer(param[1])); + } else if (! Types_IsInteger(paramTypes[1])) { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "set expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_ASSERT_PROC: + if (! isFunctionCall) { + if (paramCount == 1) { + if (Types_IsBoolean(paramTypes[0])) { + result = param[0]; + if (Trees_Symbol(param[0]) == TRUE) { + result = Trees_NewLeaf(TRUE); + } else if (Trees_Symbol(param[0]) == FALSE) { + result = Trees_NewLeaf(FALSE); + } + result = Trees_NewNode( + TREES_ASSERT_PROC, + result, + Trees_NewNode(TREES_FILE_POSITION, + Trees_NewString(inputFilename), + Trees_NewInteger(yylineno))); + } else { + Oberon_PrintContext(); + fprintf(stderr, "boolean parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_NEW_PROC: + if (! isFunctionCall) { + if (paramCount == 1) { + if (IsDesignator(param[0])) { + if (Trees_Symbol(Types_Structure(paramTypes[0])) == POINTER) { + if (! Writable(param[0])) { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "pointer parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "one parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_PACK_PROC: + if (! isFunctionCall) { + if (paramCount == 2) { + if (IsDesignator(param[0])) { + if (Types_IsReal(paramTypes[0])) { + if (Writable(param[0])) { + if (! Types_IsInteger(paramTypes[1])) { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "writable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "real-valued expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "variable parameter expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + case TREES_UNPK_PROC: + if (! isFunctionCall) { + if (paramCount == 2) { + if (IsDesignator(param[0]) && IsDesignator(param[1])) { + if (Types_IsReal(paramTypes[0])) { + if (Writable(param[0])) { + if (Types_IsInteger(paramTypes[1])) { + if (! Writable(param[1])) { + Oberon_PrintContext(); + fprintf(stderr, "second parameter is read-only: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "integer expression expected as second parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "first parameter is read-only: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "real-valued expression expected as first parameter: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two variable parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "two parameters expected: %s\n", procName); + exit(EXIT_FAILURE); + } + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", procName); + exit(EXIT_FAILURE); + } + break; + default: + assert(0); + } + + if (result == NULL) { + if (paramCount == 1) { + result = Trees_NewNode(symbol, param[0], NULL); + } else { + result = Trees_NewNode(symbol, param[0], param[1]); + } + } + Trees_SetType(resultType, result); + + return result; +} + + +static void HandleProcedureCall(Trees_Node designator, Trees_Node expList, int isFunctionCall, Trees_Node *ast) +{ + Trees_Node ident, designatorTypeStruct, fpList, resultType; + + ident = BaseIdent(designator); + if (Types_IsPredeclaredProcedure(Trees_Type(ident))) { + *ast = PredeclaredProcedureAST(Trees_Name(ident), expList, isFunctionCall); + if (*ast == NULL) { + Oberon_PrintContext(); + fprintf(stderr, "error: procedure expected\n"); + exit(EXIT_FAILURE); + } + } else { + /*handle non-predeclared procedure*/ + designatorTypeStruct = Types_Structure(Trees_Type(designator)); + if (Types_IsProcedure(designatorTypeStruct)) { + fpList =Types_Parameters(designatorTypeStruct); + resultType = Types_ResultType(designatorTypeStruct); + ValidateProcedureCall(expList, fpList, designator); + *ast = Trees_NewNode(TREES_PROCEDURE_CALL, designator, expList); + if (isFunctionCall) { + if (resultType != NULL) { + Trees_SetType(resultType, *ast); + } else { + Oberon_PrintContext(); + fprintf(stderr, "function procedure expected: %s\n", Trees_Name(ident)); + exit(EXIT_FAILURE); + } + } else if (resultType != NULL) { + Oberon_PrintContext(); + fprintf(stderr, "proper procedure expected: %s\n", Trees_Name(ident)); + exit(EXIT_FAILURE); + } + } + } + assert(*ast != NULL); +} + + +static void CheckIntegerLabelDisjointness(Trees_Node rangeA, Trees_Node rangeB) +{ + int aMin, aMax, bMin, bMax; + + if (Trees_Symbol(rangeA) == DOTDOT) { + aMin = Trees_Integer(Trees_Left(rangeA)); + aMax = Trees_Integer(Trees_Right(rangeA)); + } else { + aMin = Trees_Integer(rangeA); + aMax = Trees_Integer(rangeA); + } + if (Trees_Symbol(rangeB) == DOTDOT) { + bMin = Trees_Integer(Trees_Left(rangeB)); + bMax = Trees_Integer(Trees_Right(rangeB)); + } else { + bMin = Trees_Integer(rangeB); + bMax = Trees_Integer(rangeB); + } + + if ((aMin >= bMin) && (aMin <= bMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %d\n", aMin); + exit(EXIT_FAILURE); + } else if ((bMin >= aMin) && (bMin <= aMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %d\n", bMin); + exit(EXIT_FAILURE); + } +} + + +static void CheckCharLabelDisjointness(Trees_Node rangeA, Trees_Node rangeB) +{ + char aMin, aMax, bMin, bMax; + + if (Trees_Symbol(rangeA) == DOTDOT) { + aMin = Trees_Char(Trees_Left(rangeA)); + aMax = Trees_Char(Trees_Right(rangeA)); + } else { + aMin = Trees_Char(rangeA); + aMax = Trees_Char(rangeA); + } + if (Trees_Symbol(rangeB) == DOTDOT) { + bMin = Trees_Char(Trees_Left(rangeB)); + bMax = Trees_Char(Trees_Right(rangeB)); + } else { + bMin = Trees_Char(rangeB); + bMax = Trees_Char(rangeB); + } + + if ((aMin >= bMin) && (aMin <= bMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %c\n", aMin); + exit(EXIT_FAILURE); + } else if ((bMin >= aMin) && (bMin <= aMax)) { + Oberon_PrintContext(); + fprintf(stderr, "case label defined twice: %c\n", bMin); + exit(EXIT_FAILURE); + } +} + + +static void CheckCaseLabelUniqueness(Trees_Node newLabelRange) +{ + int labelSymbol; + Trees_Node labelList, definedLabelRange; + + if (Trees_Symbol(newLabelRange) == DOTDOT) { + labelSymbol = Trees_Symbol(Trees_Left(newLabelRange)); + } else { + labelSymbol = Trees_Symbol(newLabelRange); + } + + labelList = currentlyDefinedCaseLabels; + while (labelList != NULL) { + definedLabelRange = Trees_Left(labelList); + switch (labelSymbol) { + case INTEGER: + CheckIntegerLabelDisjointness(definedLabelRange, newLabelRange); + break; + case TREES_CHAR_CONSTANT: + CheckCharLabelDisjointness(definedLabelRange, newLabelRange); + break; + case IDENT: + if (Types_Same(definedLabelRange, newLabelRange)) { + Oberon_PrintContext(); + fprintf(stderr, "type label defined twice: %s\n", Trees_Name(newLabelRange)); + exit(EXIT_FAILURE); + } + break; + default: + assert(0); + } + labelList = Trees_Right(labelList); + } +} + + +/*functions for module productions*/ + +static void ExportSymbolTable(const char symfilePath[]) +{ + static char tempSymfilePath[PATH_MAX + 1]; + + if (! Files_Exists(".obnc")) { + Files_CreateDir(".obnc"); + } + sprintf(tempSymfilePath, ".obnc/%s.sym.%d", inputModuleName, getpid()); + Table_Export(tempSymfilePath); + Files_Move(tempSymfilePath, symfilePath); +} diff --git a/src/y.tab.h b/src/y.tab.h new file mode 100644 index 0000000..8b9670c --- /dev/null +++ b/src/y.tab.h @@ -0,0 +1,164 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + TOKEN_START = 258, + ARRAY = 259, + BEGIN_ = 260, + BY = 261, + CASE = 262, + CONST = 263, + DIV = 264, + DO = 265, + ELSE = 266, + ELSIF = 267, + END = 268, + FALSE = 269, + FOR = 270, + IF = 271, + IMPORT = 272, + IN = 273, + IS = 274, + MOD = 275, + MODULE = 276, + NIL = 277, + OF = 278, + OR = 279, + POINTER = 280, + PROCEDURE = 281, + RECORD = 282, + REPEAT = 283, + RETURN = 284, + THEN = 285, + TO = 286, + TRUE = 287, + TYPE = 288, + UNTIL = 289, + VAR = 290, + WHILE = 291, + BECOMES = 292, + DOTDOT = 293, + GE = 294, + LE = 295, + IDENT = 296, + INTEGER = 297, + REAL = 298, + STRING = 299, + TOKEN_END = 300 + }; +#endif +/* Tokens. */ +#define TOKEN_START 258 +#define ARRAY 259 +#define BEGIN_ 260 +#define BY 261 +#define CASE 262 +#define CONST 263 +#define DIV 264 +#define DO 265 +#define ELSE 266 +#define ELSIF 267 +#define END 268 +#define FALSE 269 +#define FOR 270 +#define IF 271 +#define IMPORT 272 +#define IN 273 +#define IS 274 +#define MOD 275 +#define MODULE 276 +#define NIL 277 +#define OF 278 +#define OR 279 +#define POINTER 280 +#define PROCEDURE 281 +#define RECORD 282 +#define REPEAT 283 +#define RETURN 284 +#define THEN 285 +#define TO 286 +#define TRUE 287 +#define TYPE 288 +#define UNTIL 289 +#define VAR 290 +#define WHILE 291 +#define BECOMES 292 +#define DOTDOT 293 +#define GE 294 +#define LE 295 +#define IDENT 296 +#define INTEGER 297 +#define REAL 298 +#define STRING 299 +#define TOKEN_END 300 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED + +union YYSTYPE +{ +#line 103 "Oberon.y" /* yacc.c:1909 */ + + const char *ident; + OBNC_LONGI int integer; + OBNC_LONGR double real; + const char *string; + Trees_Node node; + +#line 152 "y.tab.h" /* yacc.c:1909 */ +}; + +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + +int yyparse (void); + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ diff --git a/test b/test new file mode 100755 index 0000000..6f67230 --- /dev/null +++ b/test @@ -0,0 +1,88 @@ +#!/bin/sh + +# Copyright (C) 2017 Karl Landstrom +# +# This file is part of OBNC. +# +# OBNC is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OBNC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OBNC. If not, see . + +set -o errexit -o nounset + +readonly selfDirPath="$(cd "$(dirname "$0")"; pwd -P)" + +export OBNC_PREFIX="$selfDirPath" +export OBNC_LIBDIR="lib" +export CFLAGS="-I$selfDirPath/lib" + +EchoAndRun() +{ + echo "$@" + eval "$@" +} + + +Test() +{ + local test= + + #test compiler modules + EchoAndRun cd "$selfDirPath/src" + for test in ?*Test.c; do + "$selfDirPath/bin/micb" "$test" >/dev/null + EchoAndRun "./${test%.c}" + done + + #test core library module + EchoAndRun cd "$selfDirPath/lib/obnc" + "$selfDirPath/bin/micb" OBNCTest.c >/dev/null + EchoAndRun ./OBNCTest + + #test executables + EchoAndRun cd "$selfDirPath/bin" + for test in ?*-test; do + echo "./$test" + "./$test" >/dev/null + done + + #test basic library + EchoAndRun cd "$selfDirPath/lib/obnc" + rm -fr ".obnc" + local failure=false + for test in ?*Test.obn; do + #if-command prevents script from halting upon a missing non-required C library, like SDL + if "$selfDirPath/bin/obnc" "$test" >/dev/null; then + if [ -e "${test%.obn}.sh" ]; then + EchoAndRun "./${test%.obn}.sh" + else + EchoAndRun "./${test%.obn}" + fi + else + failure=true + fi + done + + if ! "$failure"; then + printf "\nAll tests passed!\n\n" + else + printf "\nSome test(s) failed!\n\n" + exit 1 + fi +} + +if [ "$#" -eq 0 ]; then + Test +else + echo "usage: test" >&2 + exit 1 +fi diff --git a/tests/obnc/failing-at-compile-time/A.obn b/tests/obnc/failing-at-compile-time/A.obn new file mode 100644 index 0000000..8ea5827 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/A.obn @@ -0,0 +1,28 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE A; + + TYPE + P* = POINTER TO RECORD END; + + VAR + n*: INTEGER; + r*: RECORD f: INTEGER END; + p*: P; + +END A. diff --git a/tests/obnc/failing-at-compile-time/B.obn b/tests/obnc/failing-at-compile-time/B.obn new file mode 100644 index 0000000..83e445e --- /dev/null +++ b/tests/obnc/failing-at-compile-time/B.obn @@ -0,0 +1,19 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE B; +END B. diff --git a/tests/obnc/failing-at-compile-time/T2LocalRecursiveType.obn b/tests/obnc/failing-at-compile-time/T2LocalRecursiveType.obn new file mode 100644 index 0000000..81d6000 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T2LocalRecursiveType.obn @@ -0,0 +1,28 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2LocalRecursiveType; + + PROCEDURE P; + TYPE + List = POINTER TO RECORD + item: INTEGER; + next: List (*inaccessible identifier List declared in intermediate scope*) + END; + END P; + +END T2LocalRecursiveType. diff --git a/tests/obnc/failing-at-compile-time/T2PointerToNonRecord.obn b/tests/obnc/failing-at-compile-time/T2PointerToNonRecord.obn new file mode 100644 index 0000000..f2f2941 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T2PointerToNonRecord.obn @@ -0,0 +1,23 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2PointerToNonRecord; + + TYPE + P = POINTER TO ARRAY 10 OF INTEGER; + +END T2PointerToNonRecord. diff --git a/tests/obnc/failing-at-compile-time/T2RecursiveRecord.obn b/tests/obnc/failing-at-compile-time/T2RecursiveRecord.obn new file mode 100644 index 0000000..7b93cc6 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T2RecursiveRecord.obn @@ -0,0 +1,25 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2RecursiveRecord; + + TYPE + T = RECORD + f: T + END; + +END T2RecursiveRecord. diff --git a/tests/obnc/failing-at-compile-time/T2SelfDeclaration.obn b/tests/obnc/failing-at-compile-time/T2SelfDeclaration.obn new file mode 100644 index 0000000..1ed6407 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T2SelfDeclaration.obn @@ -0,0 +1,22 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2SelfDeclaration; + + TYPE T = T; + +END T2SelfDeclaration. diff --git a/tests/obnc/failing-at-compile-time/T2UnresolvedAnonType.obn b/tests/obnc/failing-at-compile-time/T2UnresolvedAnonType.obn new file mode 100644 index 0000000..83eb275 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T2UnresolvedAnonType.obn @@ -0,0 +1,23 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2UnresolvedAnonType; + + VAR + x: POINTER TO T; + +END T2UnresolvedAnonType. diff --git a/tests/obnc/failing-at-compile-time/T2UnresolvedType.obn b/tests/obnc/failing-at-compile-time/T2UnresolvedType.obn new file mode 100644 index 0000000..0d8d0e8 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T2UnresolvedType.obn @@ -0,0 +1,23 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2UnresolvedType; + + TYPE + P = POINTER TO T; + +END T2UnresolvedType. diff --git a/tests/obnc/failing-at-compile-time/T2WrongResolvedType.obn b/tests/obnc/failing-at-compile-time/T2WrongResolvedType.obn new file mode 100644 index 0000000..3f93bb5 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T2WrongResolvedType.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2WrongResolvedType; + + TYPE + P = POINTER TO T; + T = INTEGER; + +END T2WrongResolvedType. diff --git a/tests/obnc/failing-at-compile-time/T3RepeatedField.obn b/tests/obnc/failing-at-compile-time/T3RepeatedField.obn new file mode 100644 index 0000000..c9f5da2 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T3RepeatedField.obn @@ -0,0 +1,25 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T3RepeatedField; + + VAR + x: RECORD + f, f: INTEGER + END; + +END T3RepeatedField. diff --git a/tests/obnc/failing-at-compile-time/T3RepeatedVar.obn b/tests/obnc/failing-at-compile-time/T3RepeatedVar.obn new file mode 100644 index 0000000..8b55354 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T3RepeatedVar.obn @@ -0,0 +1,23 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T3RepeatedVar; + + VAR + x, x: INTEGER; + +END T3RepeatedVar. diff --git a/tests/obnc/failing-at-compile-time/T4InvalidPointerComparison.obn b/tests/obnc/failing-at-compile-time/T4InvalidPointerComparison.obn new file mode 100644 index 0000000..e53daf9 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T4InvalidPointerComparison.obn @@ -0,0 +1,27 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T4InvalidPointerComparison; + + VAR + p: POINTER TO RECORD END; + q: POINTER TO RECORD END; + +BEGIN + IF p = q THEN END +END T4InvalidPointerComparison. + diff --git a/tests/obnc/failing-at-compile-time/T4InvalidProcedureComparison.obn b/tests/obnc/failing-at-compile-time/T4InvalidProcedureComparison.obn new file mode 100644 index 0000000..a5922f6 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T4InvalidProcedureComparison.obn @@ -0,0 +1,29 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T4InvalidProcedureComparison; + + PROCEDURE P; + END P; + + PROCEDURE Q(x: INTEGER); + END Q; + +BEGIN + IF P = Q THEN END (*P and Q should have equal types*) +END T4InvalidProcedureComparison. + diff --git a/tests/obnc/failing-at-compile-time/T4SelectorOnConstant.obn b/tests/obnc/failing-at-compile-time/T4SelectorOnConstant.obn new file mode 100644 index 0000000..feb55f6 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T4SelectorOnConstant.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T4SelectorOnConstant; + + CONST str = "abc"; + +BEGIN + str[0] := 0X +END T4SelectorOnConstant. diff --git a/tests/obnc/failing-at-compile-time/T5AssignPredefinedProcedure.obn b/tests/obnc/failing-at-compile-time/T5AssignPredefinedProcedure.obn new file mode 100644 index 0000000..dcb7dd3 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5AssignPredefinedProcedure.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5AssignPredefinedProcedure; + + VAR p: PROCEDURE (x: INTEGER): BOOLEAN; + +BEGIN + p := ODD +END T5AssignPredefinedProcedure. diff --git a/tests/obnc/failing-at-compile-time/T5AssignToImportedVariable.obn b/tests/obnc/failing-at-compile-time/T5AssignToImportedVariable.obn new file mode 100644 index 0000000..1825561 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5AssignToImportedVariable.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5AssignToImportedVariable; + + IMPORT A; + +BEGIN + A.n := 0 +END T5AssignToImportedVariable. diff --git a/tests/obnc/failing-at-compile-time/T5FunctionProcedureStatement.obn b/tests/obnc/failing-at-compile-time/T5FunctionProcedureStatement.obn new file mode 100644 index 0000000..4a0c875 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5FunctionProcedureStatement.obn @@ -0,0 +1,26 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5FunctionProcedureStatement; + + PROCEDURE F(): INTEGER; + RETURN 0 + END F; + +BEGIN + F +END T5FunctionProcedureStatement. diff --git a/tests/obnc/failing-at-compile-time/T5InvalidArrayAssignment.obn b/tests/obnc/failing-at-compile-time/T5InvalidArrayAssignment.obn new file mode 100644 index 0000000..ee9249e --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5InvalidArrayAssignment.obn @@ -0,0 +1,28 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5InvalidArrayAssignment; + + VAR + s: ARRAY 32 OF CHAR; + t: ARRAY 32 OF CHAR; + +BEGIN + s := "test"; + t := s +END T5InvalidArrayAssignment. + diff --git a/tests/obnc/failing-at-compile-time/T5NonConstForLoopInc.obn b/tests/obnc/failing-at-compile-time/T5NonConstForLoopInc.obn new file mode 100644 index 0000000..3bb6a44 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5NonConstForLoopInc.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5NonConstForLoopInc; + + VAR i: INTEGER; + +BEGIN + FOR i := 1 TO 10 BY i DO END +END T5NonConstForLoopInc. diff --git a/tests/obnc/failing-at-compile-time/T5PointerVarParamExt.obn b/tests/obnc/failing-at-compile-time/T5PointerVarParamExt.obn new file mode 100644 index 0000000..fe80f92 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5PointerVarParamExt.obn @@ -0,0 +1,32 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5PointerVarParamExt; + + TYPE + P0 = POINTER TO RECORD END; + P1 = POINTER TO RECORD (P0) END; + + VAR + x: P1; + + PROCEDURE P(VAR x: P0); + END P; + +BEGIN + P(x) (*variable pointer parameter cannot be an extended type*) +END T5PointerVarParamExt. diff --git a/tests/obnc/failing-at-compile-time/T5StringAssignment.obn b/tests/obnc/failing-at-compile-time/T5StringAssignment.obn new file mode 100644 index 0000000..5bd050d --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5StringAssignment.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5StringAssignment; + + VAR s: ARRAY 4 OF CHAR; + +BEGIN + s := "help" (*null character won't fit*) +END T5StringAssignment. diff --git a/tests/obnc/failing-at-compile-time/T5StructValueParamAssignment.obn b/tests/obnc/failing-at-compile-time/T5StructValueParamAssignment.obn new file mode 100644 index 0000000..a3d88cc --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T5StructValueParamAssignment.obn @@ -0,0 +1,28 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5StructValueParamAssignment; + + TYPE + String = ARRAY 32 OF CHAR; + + PROCEDURE P(s: String); + BEGIN + s := s + END P; + +END T5StructValueParamAssignment. diff --git a/tests/obnc/failing-at-compile-time/T6ExtendedPointerVarParam.obn b/tests/obnc/failing-at-compile-time/T6ExtendedPointerVarParam.obn new file mode 100644 index 0000000..f2cb32f --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T6ExtendedPointerVarParam.obn @@ -0,0 +1,35 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T6ExtendedPointerVarParam; + + TYPE + Ta = RECORD a : INTEGER END; + Tb = RECORD (Ta) b : INTEGER END; + + Pa = POINTER TO Ta; + Pb = POINTER TO Tb; + + VAR + pb : Pb; + + PROCEDURE F(VAR pa : Pa); + END F; + +BEGIN + F(pb) +END T6ExtendedPointerVarParam. diff --git a/tests/obnc/failing-at-compile-time/T6ForgottenReturnType.obn b/tests/obnc/failing-at-compile-time/T6ForgottenReturnType.obn new file mode 100644 index 0000000..d97fcc3 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T6ForgottenReturnType.obn @@ -0,0 +1,27 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T6ForgottenReturnType; + + PROCEDURE F(m: INTEGER); + VAR n: INTEGER; + BEGIN + n := F(0) + RETURN n + END F; + +END T6ForgottenReturnType. diff --git a/tests/obnc/failing-at-compile-time/T6InaccessibleProcedure.obn b/tests/obnc/failing-at-compile-time/T6InaccessibleProcedure.obn new file mode 100644 index 0000000..3a831d9 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T6InaccessibleProcedure.obn @@ -0,0 +1,28 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T6InaccessibleProcedure; + + PROCEDURE P; + PROCEDURE P(n: INTEGER); (*inaccessable procedure*) + END P; + BEGIN + P(0) + (*invalid procedure call because "The use of the procedure identifier in a call within its declaration implies recursive activation of the procedure."*) + END P; + +END T6InaccessibleProcedure. diff --git a/tests/obnc/failing-at-compile-time/T6NonScalarResultType.obn b/tests/obnc/failing-at-compile-time/T6NonScalarResultType.obn new file mode 100644 index 0000000..f61bbb5 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T6NonScalarResultType.obn @@ -0,0 +1,28 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T6NonScalarResultType; + + TYPE + String = ARRAY 60 OF CHAR; + + PROCEDURE P(): String; + VAR s: String; + RETURN s + END P; + +END T6NonScalarResultType. diff --git a/tests/obnc/failing-at-compile-time/T6ReadOnlyParam.obn b/tests/obnc/failing-at-compile-time/T6ReadOnlyParam.obn new file mode 100644 index 0000000..fd5a7e4 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T6ReadOnlyParam.obn @@ -0,0 +1,29 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T6ReadOnlyParam; + + PROCEDURE P(VAR a: ARRAY OF INTEGER); + END P; + + + PROCEDURE Q(a: ARRAY OF INTEGER); + BEGIN + P(a) + END Q; + +END T6ReadOnlyParam. diff --git a/tests/obnc/failing-at-compile-time/T7AccessNonExportedField.obn b/tests/obnc/failing-at-compile-time/T7AccessNonExportedField.obn new file mode 100644 index 0000000..baa7445 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7AccessNonExportedField.obn @@ -0,0 +1,27 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7AccessNonExportedField; + + IMPORT A; + + VAR + n: INTEGER; + +BEGIN + n := A.r.f +END T7AccessNonExportedField. diff --git a/tests/obnc/failing-at-compile-time/T7ActualVarParamImported.obn b/tests/obnc/failing-at-compile-time/T7ActualVarParamImported.obn new file mode 100644 index 0000000..ba821ea --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ActualVarParamImported.obn @@ -0,0 +1,27 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ActualVarParamImported; + + IMPORT A; + + PROCEDURE P(VAR x: A.P); + END P; + +BEGIN + P(A.p) +END T7ActualVarParamImported. diff --git a/tests/obnc/failing-at-compile-time/T7ActualVarParamImported1.obn b/tests/obnc/failing-at-compile-time/T7ActualVarParamImported1.obn new file mode 100644 index 0000000..bbd9386 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ActualVarParamImported1.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ActualVarParamImported1; + + IMPORT A; + +BEGIN + NEW(A.p) +END T7ActualVarParamImported1. diff --git a/tests/obnc/failing-at-compile-time/T7ExportLocalIdent.obn b/tests/obnc/failing-at-compile-time/T7ExportLocalIdent.obn new file mode 100644 index 0000000..f6d53f1 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ExportLocalIdent.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ExportLocalIdent; + + PROCEDURE P; + VAR x*: INTEGER; + END P; + +END T7ExportLocalIdent. diff --git a/tests/obnc/failing-at-compile-time/T7ImportDuplicate.obn b/tests/obnc/failing-at-compile-time/T7ImportDuplicate.obn new file mode 100644 index 0000000..6514cc2 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportDuplicate.obn @@ -0,0 +1,20 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportDuplicate; + IMPORT A, A; +END T7ImportDuplicate. diff --git a/tests/obnc/failing-at-compile-time/T7ImportDuplicateWithAlias.obn b/tests/obnc/failing-at-compile-time/T7ImportDuplicateWithAlias.obn new file mode 100644 index 0000000..48f59aa --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportDuplicateWithAlias.obn @@ -0,0 +1,22 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportDuplicateWithAlias; + + IMPORT A := B, B; + +END T7ImportDuplicateWithAlias. diff --git a/tests/obnc/failing-at-compile-time/T7ImportLibraryLocal.obn b/tests/obnc/failing-at-compile-time/T7ImportLibraryLocal.obn new file mode 100644 index 0000000..3f735f9 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportLibraryLocal.obn @@ -0,0 +1,22 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportLibraryLocal; + + IMPORT Local; + +END T7ImportLibraryLocal. diff --git a/tests/obnc/failing-at-compile-time/T7ImportNonExisting.obn b/tests/obnc/failing-at-compile-time/T7ImportNonExisting.obn new file mode 100644 index 0000000..bdc1288 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportNonExisting.obn @@ -0,0 +1,22 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportNonExisting; + + IMPORT NonExistingModule; + +END T7ImportNonExisting. diff --git a/tests/obnc/failing-at-compile-time/T7ImportRedeclaration.obn b/tests/obnc/failing-at-compile-time/T7ImportRedeclaration.obn new file mode 100644 index 0000000..3bddbe4 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportRedeclaration.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportRedeclaration; + + IMPORT A; + + CONST A = 0; + +END T7ImportRedeclaration. diff --git a/tests/obnc/failing-at-compile-time/T7ImportRedeclarationAlias.obn b/tests/obnc/failing-at-compile-time/T7ImportRedeclarationAlias.obn new file mode 100644 index 0000000..be0d051 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportRedeclarationAlias.obn @@ -0,0 +1,24 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportRedeclarationAlias; + + IMPORT B := A; + + CONST B = 0; + +END T7ImportRedeclarationAlias. diff --git a/tests/obnc/failing-at-compile-time/T7ImportSelf.obn b/tests/obnc/failing-at-compile-time/T7ImportSelf.obn new file mode 100644 index 0000000..fd77d55 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportSelf.obn @@ -0,0 +1,20 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportSelf; + IMPORT T7ImportSelf; +END T7ImportSelf. diff --git a/tests/obnc/failing-at-compile-time/T7ImportSelfWithAlias.obn b/tests/obnc/failing-at-compile-time/T7ImportSelfWithAlias.obn new file mode 100644 index 0000000..469a954 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportSelfWithAlias.obn @@ -0,0 +1,20 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportSelfWithAlias; + IMPORT M := T7ImportSelfWithAlias; +END T7ImportSelfWithAlias. diff --git a/tests/obnc/failing-at-compile-time/T7ImportWithDuplicateAlias.obn b/tests/obnc/failing-at-compile-time/T7ImportWithDuplicateAlias.obn new file mode 100644 index 0000000..97d970e --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ImportWithDuplicateAlias.obn @@ -0,0 +1,20 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ImportWithDuplicateAlias; + IMPORT B := A, B; +END T7ImportWithDuplicateAlias. diff --git a/tests/obnc/failing-at-compile-time/T7ModuleIdentifierNonMatch.obn b/tests/obnc/failing-at-compile-time/T7ModuleIdentifierNonMatch.obn new file mode 100644 index 0000000..601a1dd --- /dev/null +++ b/tests/obnc/failing-at-compile-time/T7ModuleIdentifierNonMatch.obn @@ -0,0 +1,19 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7ModuleIdentifierNonMatch; +END T7ModuleIdentifierNonMatchFoo. diff --git a/tests/obnc/failing-at-compile-time/lib/Local.obn b/tests/obnc/failing-at-compile-time/lib/Local.obn new file mode 100644 index 0000000..d2773a8 --- /dev/null +++ b/tests/obnc/failing-at-compile-time/lib/Local.obn @@ -0,0 +1,19 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Local; +END Local. diff --git a/tests/obnc/failing-at-runtime/T5AssignStringToOpenArray.obn b/tests/obnc/failing-at-runtime/T5AssignStringToOpenArray.obn new file mode 100644 index 0000000..dd74b78 --- /dev/null +++ b/tests/obnc/failing-at-runtime/T5AssignStringToOpenArray.obn @@ -0,0 +1,30 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5AssignStringToOpenArray; + + VAR + str: ARRAY 4 OF CHAR; + + PROCEDURE P(VAR s: ARRAY OF CHAR); + BEGIN + s := "test" + END P; + +BEGIN + P(str) +END T5AssignStringToOpenArray. diff --git a/tests/obnc/failing-at-runtime/T5CallNilProcedure.obn b/tests/obnc/failing-at-runtime/T5CallNilProcedure.obn new file mode 100644 index 0000000..9b60dbf --- /dev/null +++ b/tests/obnc/failing-at-runtime/T5CallNilProcedure.obn @@ -0,0 +1,26 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5CallNilProcedure; + + VAR + p: PROCEDURE; + +BEGIN + p := NIL; + p +END T5CallNilProcedure. diff --git a/tests/obnc/failing-at-runtime/T5OpenArrayAssignment.obn b/tests/obnc/failing-at-runtime/T5OpenArrayAssignment.obn new file mode 100644 index 0000000..0a2cf4e --- /dev/null +++ b/tests/obnc/failing-at-runtime/T5OpenArrayAssignment.obn @@ -0,0 +1,30 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5OpenArrayAssignment; + + VAR + s1: ARRAY 8 OF CHAR; + + PROCEDURE P(s: ARRAY OF CHAR); + BEGIN + s1 := s + END P; + +BEGIN + P("testing, testing...") +END T5OpenArrayAssignment. diff --git a/tests/obnc/failing-at-runtime/T5RecordVarParamAssignment.obn b/tests/obnc/failing-at-runtime/T5RecordVarParamAssignment.obn new file mode 100644 index 0000000..a71d358 --- /dev/null +++ b/tests/obnc/failing-at-runtime/T5RecordVarParamAssignment.obn @@ -0,0 +1,35 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5RecordVarParamAssignment; + + TYPE + T = RECORD END; + T1 = RECORD (T) END; + + VAR + x: T; + y: T1; + + PROCEDURE P(VAR x, y: T); + BEGIN + y := x + END P; + +BEGIN + P(x, y) +END T5RecordVarParamAssignment. diff --git a/tests/obnc/passing/A.obn b/tests/obnc/passing/A.obn new file mode 100644 index 0000000..4c90765 --- /dev/null +++ b/tests/obnc/passing/A.obn @@ -0,0 +1,86 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE A; + + IMPORT B; + + CONST + boolConst* = TRUE; + charConst* = CHR(22H); + intConst* = 1; + realConst* = 2.3; + strConst* = "hello there"; + setConst* = {0, 2, 3, 5}; + + TYPE + Integer = INTEGER; + String = ARRAY 256 OF CHAR; + EmptyRecord* = RECORD END; + EmptyExtendedRecord* = RECORD (EmptyRecord) END; + List* = POINTER TO Node; + Node = RECORD + key: String; + next: List + END; + Nested* = RECORD + f*: B.U + END; + Proc* = PROCEDURE; + Proc1* = PROCEDURE (n: Node); + Proc2* = PROCEDURE (): List; + Proc3* = PROCEDURE (n: Node): List; + Proc4* = PROCEDURE (n, n1: Node): List; + Proc5* = PROCEDURE (n: Node; i: INTEGER); + Proc6* = PROCEDURE (n, n1: Node; i: INTEGER); + + VAR + boolVar*: BOOLEAN; + charVar*: CHAR; + intVar*: Integer; + realVar* : REAL; + byteVar*: BYTE; + setVar*: SET; + strVar*: String; + recVar*, recVar1: RECORD + f*: INTEGER + END; + ptrVar*: POINTER TO Node; + procVar*: PROCEDURE (s: String); + alias: B.CTAlias; + + PROCEDURE P*(s: String); + END P; + + + PROCEDURE Q*(x: B.T); + END Q; + +BEGIN + boolVar := boolConst; + charVar := charConst; + intVar := intConst; + realVar := realConst; + byteVar := intConst; + setVar := setConst; + strVar := strConst; + recVar.f := 1; + recVar1.f := 0; + ptrVar := NIL; + procVar := P; + B.P(alias) +END A. diff --git a/tests/obnc/passing/B.obn b/tests/obnc/passing/B.obn new file mode 100644 index 0000000..d1864e8 --- /dev/null +++ b/tests/obnc/passing/B.obn @@ -0,0 +1,31 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE B; + + IMPORT C1 := C; + + TYPE + T* = RECORD (C1.T) END; + U* = POINTER TO UDesc; + UDesc* = RECORD f*: INTEGER END; + CTAlias* = C1.T; + + PROCEDURE P*(VAR x: CTAlias); + END P; + +END B. diff --git a/tests/obnc/passing/C.obn b/tests/obnc/passing/C.obn new file mode 100644 index 0000000..44eac69 --- /dev/null +++ b/tests/obnc/passing/C.obn @@ -0,0 +1,23 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE C; + + TYPE + T* = RECORD END; + +END C. diff --git a/tests/obnc/passing/D.obn b/tests/obnc/passing/D.obn new file mode 100644 index 0000000..2ed9b9c --- /dev/null +++ b/tests/obnc/passing/D.obn @@ -0,0 +1,19 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE D; +END D. diff --git a/tests/obnc/passing/OBNC.obn b/tests/obnc/passing/OBNC.obn new file mode 100644 index 0000000..3d345bc --- /dev/null +++ b/tests/obnc/passing/OBNC.obn @@ -0,0 +1,33 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE OBNC; (*should not cause a conflict with system C module OBNC_*) + + (*generated identifiers with suffixes should not conflict with identifiers declared in OBNC_*) + + TYPE + OBNC = RECORD END; + + VAR + a: ARRAY 1 OF INTEGER; + + PROCEDURE Q(OBNC: ARRAY OF INTEGER); + END Q; + +BEGIN + Q(a) +END OBNC. diff --git a/tests/obnc/passing/T1ConstantDeclarations.obn b/tests/obnc/passing/T1ConstantDeclarations.obn new file mode 100644 index 0000000..be96950 --- /dev/null +++ b/tests/obnc/passing/T1ConstantDeclarations.obn @@ -0,0 +1,66 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T1ConstantDeclarations; + + CONST + null = NIL; + valid = FALSE; + singleCharStr = "x"; + lineFeed = 0AX; + quotes = 22X; + backslash = "\"; + nonAscii = 80X; + letterA = CHR(ORD("A")); + sevenDigits = "1234567"; + count = 37; + pi = 3.14; + (*inf = 1.0E+1000;*) + (*nan = 0.0 / 0.0;*) + lastDigits = {0, 2 .. 3, 5}; + + VAR + p: PROCEDURE; + b: BOOLEAN; + ch: CHAR; + s: ARRAY 8 OF CHAR; + i: INTEGER; + x: REAL; + j: BYTE; + A: SET; + +BEGIN + p := null; + b := valid; + ch := singleCharStr; + ch := lineFeed; + ch := quotes; + ch := backslash; + ch := nonAscii; + ch := letterA; + s := singleCharStr; + s := lineFeed; + s := quotes; + s := backslash; + s := sevenDigits; + i := count; + j := count; + x := pi; + (*x := inf;*) + (*x := nan;*) + A := lastDigits +END T1ConstantDeclarations. diff --git a/tests/obnc/passing/T2TypeDeclarations.obn b/tests/obnc/passing/T2TypeDeclarations.obn new file mode 100644 index 0000000..40e93e3 --- /dev/null +++ b/tests/obnc/passing/T2TypeDeclarations.obn @@ -0,0 +1,121 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T2TypeDeclarations; + + TYPE + String = ARRAY 32 OF CHAR; + StringAlias = String; + + ProcTable = ARRAY 1 OF PROCEDURE; + + Element = POINTER TO RECORD END; + + Tree = POINTER TO RECORD + content: Element; + proc: PROCEDURE (t: Tree; VAR t1: Tree): Tree; + left, right: Tree + END; + + IntegerNode = POINTER TO RECORD (Element) + value: INTEGER + END; + + List = POINTER TO Node; + List1 = POINTER TO Node; + Node = RECORD + elem: INTEGER; + next: List; + next1: POINTER TO Node; + p: PROCEDURE (n: Node; VAR n1: Node) + END; + + ArrayRecPtr = POINTER TO RECORD + f: ARRAY 10 OF ArrayRecPtr; + g: RECORD + f: ArrayRecPtr + END + END; + + ProcRecArray = ARRAY 10 OF RECORD + f: PROCEDURE (x: ArrayRecPtr): INTEGER + END; + + VAR + s: String; + s1: StringAlias; + table: ProcTable; + t: Tree; + e: Element; + i: IntegerNode; + n: Node; + p: ArrayRecPtr; + a: ProcRecArray; + + PROCEDURE TestMemoryAllocation; + TYPE + Ta0 = RECORD + ptr: POINTER TO RECORD END + END; + Tb0 = RECORD + proc: PROCEDURE + END; + Ta1 = RECORD (Ta0) END; + Tb1 = RECORD (Tb0) END; + + VAR + x: POINTER TO Ta1; + y: POINTER TO Tb1; + z: POINTER TO RECORD + ptr: POINTER TO RECORD END; + proc: PROCEDURE + END; + BEGIN + NEW(x); + ASSERT(x.ptr = NIL); + NEW(y); + ASSERT(y.proc = NIL); + NEW(z); + ASSERT(z.ptr = NIL); + ASSERT(z.proc = NIL) + END TestMemoryAllocation; + + + PROCEDURE TreeProc(t: Tree; VAR t1: Tree): Tree; + RETURN NIL + END TreeProc; + + + PROCEDURE NodeProc(n: Node; VAR n1: Node); + END NodeProc; + +BEGIN + TestMemoryAllocation; + s1 := s; + table[0] := NIL; + NEW(t); + NEW(i); + t.content := i; + t.content(IntegerNode).value := 1; + t.proc := TreeProc; + NEW(e); + n.elem := 0; + n.next := NIL; + n.p := NodeProc; + NEW(p); + a[0].f := NIL +END T2TypeDeclarations. diff --git a/tests/obnc/passing/T3VariableDeclarations.obn b/tests/obnc/passing/T3VariableDeclarations.obn new file mode 100644 index 0000000..0ea3a4c --- /dev/null +++ b/tests/obnc/passing/T3VariableDeclarations.obn @@ -0,0 +1,61 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T3VariableDeclarations; + + TYPE + Vector = RECORD + x, y: REAL + END; + + Shape = POINTER TO ShapeDesc; + ShapeDesc = RECORD + pos: Vector + END; + + Rectangle = POINTER TO RectangleDesc; + RectangleDesc = RECORD (ShapeDesc) + size: Vector + END; + + PROCEDURE TestInitialization; + VAR s: ShapeDesc; + r: RectangleDesc; + rs: ARRAY 10 OF RectangleDesc; + rp: Rectangle; + + PROCEDURE AssertVector(VAR v: Vector); + BEGIN + ASSERT(v IS Vector) + END AssertVector; + + BEGIN + AssertVector(s.pos); + AssertVector(r.pos); + AssertVector(r.size); + AssertVector(rs[0].pos); + AssertVector(rs[0].size); + + NEW(rp); + ASSERT(rp IS Rectangle); + AssertVector(rp.pos); + AssertVector(rp.size); + END TestInitialization; + +BEGIN + TestInitialization +END T3VariableDeclarations. diff --git a/tests/obnc/passing/T4Expressions.obn b/tests/obnc/passing/T4Expressions.obn new file mode 100644 index 0000000..7b56598 --- /dev/null +++ b/tests/obnc/passing/T4Expressions.obn @@ -0,0 +1,387 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T4Expressions; + + VAR + n: INTEGER; + + PROCEDURE TestDesignators; + VAR A: ARRAY 3, 4 OF INTEGER; + + PROCEDURE IncNReturnZero(): INTEGER; + BEGIN + INC(n) + RETURN 0 + END IncNReturnZero; + + BEGIN + A[1, 2] := 1; + ASSERT(A[1, 2] = A[1][2]); + + n := 0; + A[0, 0] := ABS(A[IncNReturnZero(), IncNReturnZero()]); + ASSERT(n = 2) + END TestDesignators; + + + PROCEDURE TestRelationalOperations; + TYPE + T = POINTER TO RECORD END; + T1 = POINTER TO RECORD (T) END; + + VAR b, b1: BOOLEAN; + ch, ch1: CHAR; + n: INTEGER; + x: REAL; + y: BYTE; + A, B: SET; + str: ARRAY 24 OF CHAR; + str1: ARRAY 32 OF CHAR; + t: T; + t1: T1; + BEGIN + (*booleans*) + ASSERT(TRUE = TRUE); + ASSERT(TRUE # FALSE); + b := TRUE; + b1 := FALSE; + ASSERT(b = TRUE); + ASSERT(b1 # TRUE); + + (*characters / single-character strings*) + ch := 0X; + ch1 := "a"; + ASSERT(ch = 0X); + ASSERT(ch # "a"); + ASSERT(ch < ch1); + ASSERT(ch <= 0X); + ASSERT(ch <= "a"); + ASSERT(ch1 > ch); + (*ASSERT(ch >= 0X);*) + ASSERT("a" >= ch); + ch := 7FX; + ch1 := 80X; + ASSERT(ch < ch1); + + (*integers*) + ASSERT(0 = 0); + ASSERT(0 # 1); + ASSERT(0 < 1); + ASSERT(0 <= 0); + ASSERT(0 <= 1); + ASSERT(1 > 0); + ASSERT(0 >= 0); + ASSERT(1 >= 0); + ASSERT(0 IN {0}); + ASSERT(~(1 IN {0})); + n := 0; + ASSERT(n = 0); + ASSERT(n # 1); + ASSERT(n < 1); + ASSERT(n <= 0); + ASSERT(n <= 1); + ASSERT(n < 1); + ASSERT(n >= 0); + ASSERT(n >= -1); + ASSERT(n IN {0}); + ASSERT(~(1 IN {n})); + + (*real numbers*) + ASSERT(0.0 = 0.0); + ASSERT(0.0 # 1.0); + ASSERT(0.0 < 1.0); + ASSERT(0.0 <= 0.0); + ASSERT(0.0 <= 1.0); + ASSERT(1.0 > 0.0); + ASSERT(0.0 >= 0.0); + ASSERT(1.0 >= 0.0); + x := 0.0; + ASSERT(x = 0.0); + ASSERT(x # 1.0); + ASSERT(x < 1.0); + ASSERT(x <= 0.0); + ASSERT(x <= 1.0); + ASSERT(1.0 > x); + ASSERT(x >= 0.0); + ASSERT(x >= -1.0); + + (*bytes*) + y := 0; + ASSERT(y = 0); + ASSERT(y < 1); + ASSERT(y <= 0); + ASSERT(y <= 1); + ASSERT(1 > y); + (*ASSERT(y >= 0);*) + ASSERT(y IN {0}); + ASSERT(~(1 IN {y})); + + (*sets*) + ASSERT({0, 1} = {1, 0}); + ASSERT({0} # {0, 1}); + ASSERT({1 .. 0} = {}); + n := 1; + ASSERT({n .. 0} = {}); + A := {0}; + B := {0, 1}; + ASSERT(A = {0}); + ASSERT(B # {0}); + ASSERT(A # B); + + (*strings / characters / character arrays*) + ASSERT("foo" = "foo"); + ASSERT("foo" # "bar"); + ASSERT("bar" < "foo"); + ASSERT("foo" <= "foo"); + ASSERT("bar" <= "foo"); + ASSERT("foo" > "bar"); + ASSERT("foo" >= "foo"); + ASSERT("foo" >= "bar"); + ch := "b"; + ASSERT("b" = ch); + ASSERT("f" # ch); + ASSERT(ch < "c"); + ASSERT("b" <= ch); + ASSERT("a" <= ch); + ASSERT("c" > ch); + ASSERT("b" >= ch); + ASSERT("c" >= ch); + str := "foo"; + ASSERT("foo" = str); + ASSERT("fool" # str); + ASSERT("fo" # str); + ASSERT("bar" # str); + ASSERT("bar" < str); + ASSERT("fo" < str); + ASSERT("foo" <= str); + ASSERT("bar" <= str); + ASSERT("qux" > str); + ASSERT("foo" >= str); + ASSERT("qux" >= str); + str1 := "bar"; + ASSERT(~(str = str1)); + ASSERT(str # str1); + ASSERT(~(str < str1)); + ASSERT(~(str <= str1)); + ASSERT(str > str1); + ASSERT(str >= str1); + str[0] := 7FX; str[1] := 0X; + str1[0] := 80X; str1[1] := 0X; + ASSERT(str < str1); + + (*pointers*) + NEW(t1); + t := t1; + ASSERT(t = t1); + ASSERT(t1 = t) + END TestRelationalOperations; + + + PROCEDURE TestAdditiveOperations; + CONST eps = 0.01; + VAR b: BOOLEAN; + n: INTEGER; + x: REAL; + y: BYTE; + A: SET; + BEGIN + (*booleans*) + ASSERT(TRUE OR TRUE); + ASSERT(TRUE OR FALSE); + ASSERT(FALSE OR TRUE); + b := TRUE; + ASSERT(b OR TRUE); + ASSERT(b OR FALSE); + ASSERT(FALSE OR b); + + (*integers*) + ASSERT(1 + 1 = 2); + ASSERT(1 - 1 = 0); + n := 1; + ASSERT(+n = +1); + ASSERT(-n = -1); + ASSERT(n + 1 = 2); + ASSERT(n - 1 = 0); + ASSERT(-n + 1 = 0); + ASSERT(-n - 1 = -2); + + (*reals*) + ASSERT(1.0 + 1.0 >= 2.0 - eps); + ASSERT(1.0 + 1.0 <= 2.0 + eps); + ASSERT(1.0 - 1.0 >= -eps); + ASSERT(1.0 - 1.0 <= eps); + x := 1.0; + ASSERT(+x = +1.0); + ASSERT(-x = -1.0); + ASSERT(x + 1.0 >= 2.0 - eps); + ASSERT(x + 1.0 <= 2.0 + eps); + ASSERT(x - 1.0 >= -eps); + ASSERT(x - 1.0 <= eps); + ASSERT(-x + 1.0 >= - eps); + ASSERT(-x + 1.0 <= eps); + ASSERT(-x - 1.0 >= -2.0 - eps); + ASSERT(-x - 1.0 <= -2.0 + eps); + + (*bytes*) + y := 1; + ASSERT(+y = +1); + ASSERT(-y = -1); + ASSERT(y + 1= 2); + ASSERT(1 - y = 0); + + (*sets*) + ASSERT({0, 1} + (-{0, 1}) = -{}); + ASSERT({0, 1} + {0, 2} = {0 .. 2}); + ASSERT({0, 1} - {0, 2} = {1}); + A := {0, 1}; + ASSERT(A + (-{0, 1}) = -{}); + ASSERT(A + {0, 2} = {0 .. 2}); + ASSERT(A - {0, 2} = {1}) + END TestAdditiveOperations; + + + PROCEDURE TestMultiplicativeOperations; + CONST eps = 0.01; + VAR b: BOOLEAN; + n: INTEGER; + x: REAL; + y: BYTE; + A: SET; + BEGIN + (*booleans*) + ASSERT(TRUE & TRUE); + b := TRUE; + ASSERT(b & TRUE); + + (*integers*) + ASSERT(9 * 2 = 18); + ASSERT(9 DIV 4 = 2); + ASSERT((-9) DIV 4 = -3); + ASSERT(9 MOD 4 = 1); + ASSERT((-9) MOD 4 = 3); + n := -9; + y := 4; + ASSERT(n * y = -36); + ASSERT(n DIV y = -3); + ASSERT(n MOD y = 3); + + (*reals*) + ASSERT(9.0 * 2.0 >= 18.0 - eps); + ASSERT(9.0 * 2.0 <= 18.0 + eps); + ASSERT(9.0 / 2.0 >= 4.5 - eps); + ASSERT(9.0 / 2.0 <= 4.5 + eps); + x := 9.0; + ASSERT(x * 2.0 >= 18.0 - eps); + ASSERT(x * 2.0 <= 18.0 + eps); + ASSERT(x / 2.0 >= 4.5 - eps); + ASSERT(x / 2.0 <= 4.5 + eps); + + (*bytes*) + y := 9; + ASSERT(y * 2 = 18); + (*ASSERT(y DIV 4 = 2); + ASSERT(y MOD 4 = 1);*) + + (*sets*) + ASSERT({0, 1} * {1, 2} = {1}); + ASSERT({0, 1} / {1, 2} = {0, 2}); + A := {0, 1}; + ASSERT(A * {1, 2} = {1}); + ASSERT(A / {1, 2} = {0, 2}) + END TestMultiplicativeOperations; + + + PROCEDURE TestPredeclaredFunctionProcedures; + CONST eps = 0.01; + (*make sure function procedures with constant parameters are constant expressions*) + absConst = ABS(0); + oddConst = ODD(0); + lslConst = LSL(0, 0); + asrConst = ASR(0, 0); + rorConst = ROR(0, 1); + floorConst = FLOOR(eps); + fltConst = FLT(0); + ordConst = ORD(TRUE); + chrConst = CHR(0); + + VAR a: ARRAY 10 OF CHAR; + ch: CHAR; + b: BYTE; + + BEGIN + ASSERT(ABS(-1) = 1); + ASSERT(ABS(0) = 0); + ASSERT(ABS(1) = 1); + ASSERT(ABS(-1.0) = 1.0); + ASSERT(ABS(0.0) = 0.0); + ASSERT(ABS(1.0) = 1.0); + + ASSERT(~ODD(-2)); + ASSERT(ODD(-1)); + ASSERT(~ODD(0)); + ASSERT(ODD(1)); + ASSERT(~ODD(2)); + + a := ""; + ASSERT(LEN(a) = 10); + + ASSERT(LSL(0, 0) = 0); + ASSERT(LSL(0, 1) = 0); + ASSERT(LSL(1, 0) = 1); + ASSERT(LSL(1, 1) = 2); + + ASSERT(ASR(0, 0) = 0); + ASSERT(ASR(0, 1) = 0); + ASSERT(ASR(1, 0) = 1); + ASSERT(ASR(1, 1) = 0); + + ASSERT(ROR(0, 1) = 0); + ASSERT(ROR(2, 1) = 1); + + ASSERT(FLOOR(-1.5) = -2); + ASSERT(FLOOR(0.0) = 0); + ASSERT(FLOOR(1.5) = 1); + + ASSERT(FLT(-1) = -1.0); + ASSERT(FLT(0) = 0.0); + ASSERT(FLT(1) = 1.0); + + ch := 0X; + ASSERT(ORD(ch) = 0); + ASSERT(ORD("A") = 41H); + ASSERT(ORD(FALSE) = 0); + ASSERT(ORD({}) = 0); + ch := 80X; + ASSERT(ORD(ch) = 80H); + + ASSERT(CHR(0) = 0X); + ASSERT(CHR(1) = 1X); + ASSERT(CHR(7FH) = 7FX); + ch := 7FX; + ASSERT(CHR(7FH) = ch); + b := 1; + ASSERT(CHR(b) = 1X) + END TestPredeclaredFunctionProcedures; + +BEGIN + TestDesignators; + TestRelationalOperations; + TestAdditiveOperations; + TestMultiplicativeOperations; + TestPredeclaredFunctionProcedures; +END T4Expressions. diff --git a/tests/obnc/passing/T5Statements.obn b/tests/obnc/passing/T5Statements.obn new file mode 100644 index 0000000..0513296 --- /dev/null +++ b/tests/obnc/passing/T5Statements.obn @@ -0,0 +1,479 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T5Statements; + + TYPE + Shape = POINTER TO ShapeDesc; + ShapeDesc = RECORD + x, y: REAL + END; + + Rectangle = POINTER TO RectangleDesc; + RectangleDesc = RECORD (ShapeDesc) + w, h: REAL + END; + + Circle = POINTER TO CircleDesc; + CircleDesc = RECORD (ShapeDesc) + r: REAL + END; + + VAR + globalInteger: INTEGER; + + PROCEDURE TestBasicAssignments; + VAR b, b1: BOOLEAN; + ch, ch1: CHAR; + n, n1: INTEGER; + x, x1: REAL; + y, y1: BYTE; + A, A1: SET; + BEGIN + b := TRUE; + b1 := FALSE; + b := b1; + ASSERT(b = b1); + ch := "a"; + ch1 := 22X; + ch := ch1; + ASSERT(ch = ch1); + n := 0; + n1 := -1; + n := n1; + ASSERT(n = n1); + x := 0.0; + x1 := -1.0; + x := x1; + ASSERT(x = x1); + y := 0; + y1 := 255; + y := y1; + ASSERT(y = y1); + n := 0; + y := n; + ASSERT(y = n); + A := {}; + A1 := {0, 1}; + A := A1; + ASSERT(A = A1); + END TestBasicAssignments; + + + PROCEDURE TestArrayAssignments; + VAR str, str1: ARRAY 60 OF CHAR; + + PROCEDURE AssignString(VAR s: ARRAY OF CHAR); + BEGIN + s := "hello" + END AssignString; + + PROCEDURE AssignOpenArray(s: ARRAY OF CHAR); + VAR t: ARRAY 128 OF CHAR; + BEGIN + t := s + END AssignOpenArray; + + BEGIN + str := "testing, testing..."; + str1 := "more testing..."; + str := str1; + ASSERT(str = str1); + AssignString(str); + ASSERT(str = "hello"); + AssignOpenArray("hello") + END TestArrayAssignments; + + + PROCEDURE TestRecordAssignments; + CONST eps = 0.01; + VAR foo, bar: RECORD ch: CHAR; i: INTEGER END; + s: ShapeDesc; + r: RectangleDesc; + c: CircleDesc; + a: ARRAY 10 OF CircleDesc; + + PROCEDURE P(VAR s: ShapeDesc); + BEGIN + ASSERT(s IS CircleDesc); + s(CircleDesc) := s(CircleDesc); + s(CircleDesc).r := 1.0 + END P; + + PROCEDURE Copy(VAR source, target: ShapeDesc); + BEGIN + target := source + END Copy; + + BEGIN + foo.i := 37; + bar := foo; + ASSERT(bar.i = 37); + + s.x := 0.0; + s.y := 0.0; + r.x := 0.0; + P(c); + ASSERT(ABS(c.r - 1.0) < eps); + + r.x := 1.0; + s := r; + ASSERT(s.x = r.x); + + P(a[9]); + + Copy(r, r) + END TestRecordAssignments; + + + PROCEDURE TestPointerAssignments; + VAR x: Rectangle; + y: Shape; + s: POINTER TO ShapeDesc; + r: POINTER TO RectangleDesc; + r1: POINTER TO RectangleDesc; + BEGIN + NEW(x); + y := x; + ASSERT(y IS Rectangle); + NEW(r); + s := r; + ASSERT(s IS RectangleDesc); + r1 := r; + ASSERT(r1 IS RectangleDesc) + END TestPointerAssignments; + + + PROCEDURE P; + END P; + + PROCEDURE P1(n: INTEGER); + END P1; + + PROCEDURE P2(n: INTEGER; x: REAL); + END P2; + + PROCEDURE F(): INTEGER; + RETURN 0 + END F; + + PROCEDURE F1(n: INTEGER): INTEGER; + RETURN 0 + END F1; + + PROCEDURE F2(VAR n: INTEGER; x: REAL; s: ARRAY OF CHAR): INTEGER; + RETURN 0 + END F2; + + PROCEDURE TestProcedureAssignments; + TYPE + PT = PROCEDURE; + PT1 = PROCEDURE (n: INTEGER); + PT2 = PROCEDURE (n: INTEGER; x: REAL); + FT = PROCEDURE (): INTEGER; + FT1 = PROCEDURE (n: INTEGER): INTEGER; + FT2 = PROCEDURE (VAR n: INTEGER; x: REAL; s: ARRAY OF CHAR): INTEGER; + VAR p: PT; p1: PT1; p2: PT2; + f: FT; f1: FT1; f2, g2: FT2; + n: INTEGER; + + PROCEDURE Local; + END Local; + + BEGIN + p := NIL; + p := P; + p := Local; + p1 := P1; + p2 := P2; + f := F; + n := f(); + f1 := F1; + f2 := F2; + g2 := f2 + END TestProcedureAssignments; + + + PROCEDURE TestAssignments; + BEGIN + TestBasicAssignments; + TestArrayAssignments; + TestRecordAssignments; + TestPointerAssignments; + TestProcedureAssignments + END TestAssignments; + + + PROCEDURE TestProcedureCalls; + VAR s: ARRAY 16 OF CHAR; + + PROCEDURE P1; + END P1; + + PROCEDURE P2(n: INTEGER); + END P2; + + PROCEDURE P3(a, b: INTEGER); + END P3; + + PROCEDURE P4(a: INTEGER; b: INTEGER); + END P4; + + PROCEDURE P5(ch: CHAR); + END P5; + + PROCEDURE P6(s: ARRAY OF CHAR); + END P6; + + BEGIN + P1; + P2(0); + P3(0, 0); + P4(0, 0); + P5("x"); + P5(0X); + P6("test"); + s := "test"; + P6(s) + END TestProcedureCalls; + + + PROCEDURE TestPredeclaredProperProcedures; + CONST eps = 0.01; + VAR n: INTEGER; + A: SET; + x: REAL; + v: POINTER TO RECORD f: INTEGER END; + BEGIN + n := 0; + INC(n); + ASSERT(n = 1); + + n := 0; + INC(n, 10); + ASSERT(n = 10); + + n := 0; + DEC(n); + ASSERT(n = -1); + + n := 0; + DEC(n, 10); + ASSERT(n = -10); + + A := {}; + INCL(A, 0); + ASSERT(A = {0}); + + A := {0}; + EXCL(A, 0); + ASSERT(A = {}); + + NEW(v); + ASSERT(v # NIL); + v.f := 1; + ASSERT(v.f = 1); + + x := 1.0; + PACK(x, 2); + ASSERT(x >= 4.0 - eps); + ASSERT(x <= 4.0 + eps); + + x := 4.0; + UNPK(x, n); + ASSERT(x >= 1.0); + ASSERT(x < 2.0); + ASSERT(n = 2) + END TestPredeclaredProperProcedures; + + + PROCEDURE TestIfStatements; + VAR n: INTEGER; + BEGIN + n := 0; + IF n = 0 THEN + n := 1 + END; + ASSERT(n = 1); + n := 1; + IF n = 0 THEN + n := 1 + ELSE + n := 2 + END; + ASSERT(n = 2); + n := 2; + IF n = 0 THEN + n := 1 + ELSIF n = 1 THEN + n := 2 + ELSE + n := 3 + END; + ASSERT(n = 3) + END TestIfStatements; + + + PROCEDURE TestCaseStatements; + CONST + C = 0; + + VAR + n: INTEGER; + ch: CHAR; + sp: Shape; + rp: Rectangle; + c: CircleDesc; + + PROCEDURE P(VAR s: ShapeDesc); + BEGIN + CASE s OF + (*ShapeDesc: + s.x := 0.0; s.y := 0.0 + | *)RectangleDesc: + s.w := 1.0; s.h := 0.0 + | CircleDesc: + s.r := 1.0 + END; + END P; + + BEGIN + n := 15; + CASE n OF + C: + | 1, 2: + | 4, 5, 7: + | 8 .. 9: + | 10, 12 .. 20: + n := 0 + END; + ASSERT(n = 0); + ch := "u"; + CASE ch OF + | 0X: + | "a", "b": + | "d", "e", "f": + | "h" .. "k": + | "l", "m" .. "z": + ch := 0X + END; + ASSERT(ch = 0X); + NEW(rp); + sp := rp; + CASE sp OF + (*Shape: + sp.x := 0.0; sp.y := 0.0 + | *)Rectangle: + sp.w := 1.0; sp.h := 2.0; + sp := sp + | Circle: + sp.r := 1.0 + END; + ASSERT(sp(Rectangle).w = 1.0); + ASSERT(sp(Rectangle).h = 2.0); + P(c); + ASSERT(c.r = 1.0); + END TestCaseStatements; + + + PROCEDURE TestWhileStatements; + VAR n, n1, i: INTEGER; + BEGIN + n := 0; + i := 1; + WHILE i <= 10 DO + n := n + 1; + i := i + 1 + END; + ASSERT(n = 10); + n := 4; + n1 := 6; + WHILE n > n1 DO + n := n - n1 + ELSIF n1 > n DO + n1 := n1 - n + END; + ASSERT(n = 2); + ASSERT(n1 = 2); + n := 5; + n1 := 6; + WHILE n > n1 DO + n := n - n1 + ELSIF n1 > n DO + n1 := n1 - n + END; + ASSERT(n = 1); + ASSERT(n1 = 1); + END TestWhileStatements; + + + PROCEDURE TestRepeatStatements; + VAR n, i: INTEGER; + BEGIN + n:= 0; + i := 1; + REPEAT + INC(n); + INC(i) + UNTIL i = 11; + ASSERT(n = 10); + END TestRepeatStatements; + + + PROCEDURE IncGlobalIntegerReturnOne(): INTEGER; + BEGIN + INC(globalInteger) + RETURN 1 + END IncGlobalIntegerReturnOne; + + + PROCEDURE TestForStatements; + VAR n, i: INTEGER; + x: REAL; + BEGIN + n := 0; + FOR i := 1 TO 10 DO + n := n + 1 + END; + ASSERT(n = 10); + n := 0; + FOR i := 1 TO 20 BY 2 DO + n := n + 1 + END; + ASSERT(n = 10); + n := 0; + FOR i := 20 TO 1 BY -2 DO + n := n + 1 + END; + ASSERT(n = 10); + globalInteger := 0; + FOR i := 0 TO IncGlobalIntegerReturnOne() DO (*make sure the limit function is called three times*) + x := x + 1.0 + END; + ASSERT(globalInteger = 3) + END TestForStatements; + +BEGIN + TestAssignments; + TestProcedureCalls; + TestPredeclaredProperProcedures; + TestIfStatements; + TestCaseStatements; + TestWhileStatements; + TestRepeatStatements; + TestForStatements +END T5Statements. diff --git a/tests/obnc/passing/T6ProcedureDeclarations.obn b/tests/obnc/passing/T6ProcedureDeclarations.obn new file mode 100644 index 0000000..3a11218 --- /dev/null +++ b/tests/obnc/passing/T6ProcedureDeclarations.obn @@ -0,0 +1,110 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T6ProcedureDeclarations; + + TYPE + Ptr = POINTER TO RECORD f: INTEGER END; + Proc = PROCEDURE; + + PROCEDURE TestValueParameters; + VAR ptr: Ptr; proc: Proc; + + PROCEDURE P(x: INTEGER); + BEGIN + x := 0 + END P; + + PROCEDURE P1(x: Ptr); + BEGIN + x := NIL + END P1; + + PROCEDURE P2(x: Proc); + BEGIN + x := NIL + END P2; + + BEGIN + P(0); + NEW(ptr); + P1(ptr); + P2(proc) + END TestValueParameters; + + + PROCEDURE TestVarParameters; + VAR x: Ptr; + + PROCEDURE Alloc(VAR p: Ptr); + BEGIN + NEW(p); + p.f := 1 + END Alloc; + + BEGIN + Alloc(x); + ASSERT(x.f = 1) + END TestVarParameters; + + + PROCEDURE TestResultExpressions; + VAR x: Ptr; + + PROCEDURE P(): Ptr; + TYPE PtrExt = POINTER TO RECORD (Ptr) END; + VAR y: PtrExt; + BEGIN + NEW(y) + RETURN y + END P; + + BEGIN + x := P() + END TestResultExpressions; + + + PROCEDURE TestLocalProcedures; + VAR s: INTEGER; + + PROCEDURE Sum(n: INTEGER): INTEGER; + + PROCEDURE Inner(i, acc: INTEGER): INTEGER; + VAR result: INTEGER; + BEGIN + IF i >= 1 THEN + result := Inner(i - 1, acc + i) + ELSE + result := acc + END + RETURN result + END Inner; + + RETURN Inner(n, 0) + END Sum; + + BEGIN + s := Sum(10); + ASSERT(s = 55) + END TestLocalProcedures; + +BEGIN + TestValueParameters; + TestVarParameters; + TestResultExpressions; + TestLocalProcedures +END T6ProcedureDeclarations. diff --git a/tests/obnc/passing/T7Modules.obn b/tests/obnc/passing/T7Modules.obn new file mode 100644 index 0000000..ddaa695 --- /dev/null +++ b/tests/obnc/passing/T7Modules.obn @@ -0,0 +1,64 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE T7Modules; + + IMPORT + A, + B1 := B, + B := C, + D := D, + T7Modules := libE, + lib1M, + OBNC; + + TYPE + ListExt = POINTER TO RECORD (A.List) END; + + VAR + intVar: INTEGER; + w: B.T; + x: B1.T; + y: A.Nested; + list: A.List; + +BEGIN + ASSERT(A.boolConst); + ASSERT(A.charConst = CHR(22H)); + ASSERT(A.intConst = 1); + ASSERT(A.realConst = 2.3); + ASSERT(A.strConst = "hello there"); + ASSERT(A.setConst = {0, 2, 3, 5}); + + ASSERT(A.boolVar = A.boolConst); + ASSERT(A.charVar = A.charConst); + ASSERT(A.intVar = A.intConst); + ASSERT(A.realVar = A.realConst); + ASSERT(A.strVar = A.strConst); + ASSERT(A.setVar = A.setConst); + ASSERT(A.recVar.f = 1); + + intVar := A.intVar; + A.procVar(A.strVar); + NEW(y.f); + y.f.f := 1; + w := x; + A.Q(x); + NEW(list); +END T7Modules. + +All text after a module should be ignored diff --git a/tests/obnc/passing/lib/Local.obn b/tests/obnc/passing/lib/Local.obn new file mode 100644 index 0000000..18f12ac --- /dev/null +++ b/tests/obnc/passing/lib/Local.obn @@ -0,0 +1,29 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Local; + + TYPE + T* = RECORD END; + + VAR + x*: INTEGER; + + PROCEDURE P*; + END P; + +END Local. diff --git a/tests/obnc/passing/lib/libE.obn b/tests/obnc/passing/lib/libE.obn new file mode 100644 index 0000000..cbb759f --- /dev/null +++ b/tests/obnc/passing/lib/libE.obn @@ -0,0 +1,25 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE libE; + + IMPORT Local; + + VAR + x: Local.T; + +END libE. diff --git a/tests/obnc/passing/lib1/Local.obn b/tests/obnc/passing/lib1/Local.obn new file mode 100644 index 0000000..18f12ac --- /dev/null +++ b/tests/obnc/passing/lib1/Local.obn @@ -0,0 +1,29 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE Local; + + TYPE + T* = RECORD END; + + VAR + x*: INTEGER; + + PROCEDURE P*; + END P; + +END Local. diff --git a/tests/obnc/passing/lib1/lib1M.obn b/tests/obnc/passing/lib1/lib1M.obn new file mode 100644 index 0000000..8420048 --- /dev/null +++ b/tests/obnc/passing/lib1/lib1M.obn @@ -0,0 +1,25 @@ +(*Copyright (C) 2017 Karl Landstrom + +This file is part of OBNC. + +OBNC is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OBNC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OBNC. If not, see .*) + +MODULE lib1M; + + IMPORT Local; + + VAR + x: Local.T; + +END lib1M. diff --git a/tests/obncdoc/Test.def b/tests/obncdoc/Test.def new file mode 100644 index 0000000..8f31666 --- /dev/null +++ b/tests/obncdoc/Test.def @@ -0,0 +1,12 @@ +DEFINITION Test; + + CONST + s = "(*(a < b) & (b > c)*)"; (*(*"foo"*)*) + + PROCEDURE P; +(* + a multi-line comment +*) + END P; + +END Test. diff --git a/tests/obncdoc/Test.def.html b/tests/obncdoc/Test.def.html new file mode 100644 index 0000000..b2b63d4 --- /dev/null +++ b/tests/obncdoc/Test.def.html @@ -0,0 +1,12 @@ +DEFINITION Test; + + CONST + s = "(*(a < b) & (b > c)*)"; (*(*"foo"*)*) + + PROCEDURE P; +(* + a multi-line comment +*) + END P; + +END Test. diff --git a/tests/scanner/tokens.txt b/tests/scanner/tokens.txt new file mode 100644 index 0000000..1f79f7c --- /dev/null +++ b/tests/scanner/tokens.txt @@ -0,0 +1,78 @@ +x +scan +Oberon +GetSymbol +firstLetter +2147483647 +100H +340282346638528859811704183484516925440.0 +4.567E+6 +4.567E-6 +179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0 +1..10 +"" +0X +"*" +2AX +"Don't worry!" ++ +- +* +/ +~ +& +. +, +; +| +( +[ +{ +:= +^ += +# +< +> +<= +>= +.. +: +) +] +} +ARRAY +BEGIN +BY +CASE +CONST +DIV +DO +ELSE +ELSIF +END +FALSE +FOR +IF +IMPORT +IN +IS +MOD +MODULE +NIL +OF +OR +POINTER +PROCEDURE +RECORD +REPEAT +RETURN +THEN +TO +TRUE +TYPE +UNTIL +VAR +WHILE +(*a comment*) +(*a (*nested*) comment*)