Add compiler and vm-translator
This commit is contained in:
commit
553c87029f
|
@ -0,0 +1,3 @@
|
|||
*.h linguist-language=C
|
||||
*.c linguist-language=C
|
||||
Makefile -linguist-detectable
|
|
@ -0,0 +1,2 @@
|
|||
jack-compiler
|
||||
tags
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
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
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
@ -0,0 +1,8 @@
|
|||
FILES = *.c */*.c
|
||||
LIBRARIES = -lpthread
|
||||
INCLUDES = -I. -I./parser/ -I./compiler/ -I./vm/ -I./tokenizer/ -I./misc/
|
||||
CFLAGS = -std=c99 -Wall
|
||||
OUTFILE = jackc
|
||||
|
||||
main: ${FILES}
|
||||
${CC} ${CFLAGS} ${LIBRARIES} ${INCLUDES} -o ${OUTFILE} ${FILES}
|
|
@ -0,0 +1,2 @@
|
|||
# jackc
|
||||
Jack language compiler, but instead of producing ".vm" files, it produces binary ".hack" files straight away<br>
|
|
@ -0,0 +1,226 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "compiler-expressions.h"
|
||||
#include "compiler-util.h"
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
LINE* pushconstant(int n);
|
||||
LINE* mathopln(char op);
|
||||
LINE* pushthat();
|
||||
int countexpressions(EXPRESSIONLIST* explist);
|
||||
char* toascii(char c);
|
||||
|
||||
// Dealing with singular terms
|
||||
LINEBLOCK* compilestrconst(char* str);
|
||||
LINEBLOCK* compilekeywordconst(SCOPE* s, TERM* t);
|
||||
LINEBLOCK* compilearrayitem(SCOPE* s, TERM* t);
|
||||
LINEBLOCK* compilecallln(SCOPE* s, SUBROUTDEC* d, SUBROUTCALL* call);
|
||||
LINEBLOCK* pushunaryopterm(SCOPE* s, TERM* t);
|
||||
LINEBLOCK* compileterm(SCOPE* s, TERM* t);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
|
||||
|
||||
// Miscelaneous
|
||||
LINE* mathopln(char op) {
|
||||
if(op == '+')
|
||||
return onetoken("add");
|
||||
if(op == '-')
|
||||
return onetoken("sub");
|
||||
if(op == '=')
|
||||
return onetoken("eq");
|
||||
if(op == '>')
|
||||
return onetoken("gt");
|
||||
if(op == '<')
|
||||
return onetoken("lt");
|
||||
if(op == '|')
|
||||
return onetoken("or");
|
||||
if(op == '&')
|
||||
return onetoken("and");
|
||||
if(op == '/') {
|
||||
char* tokens[] = { "call", "Math.divide", "2" };
|
||||
return mkln(tokens);
|
||||
}
|
||||
char* tokens[] = { "call", "Math.multiply", "2" };
|
||||
return mkln(tokens);
|
||||
}
|
||||
|
||||
LINE* pushconstant(int n) {
|
||||
char* tokens[] = { "push", "constant", itoa(n) };
|
||||
LINE* ln = mkln(tokens);
|
||||
free(tokens[2]);
|
||||
return ln;
|
||||
}
|
||||
|
||||
LINE* pushthat() {
|
||||
char* pushthat[] = { "push", "that", "0" };
|
||||
return mkln(pushthat);
|
||||
}
|
||||
|
||||
int countexpressions(EXPRESSIONLIST* explist) {
|
||||
int i = 0;
|
||||
while(explist != NULL) {
|
||||
i++;
|
||||
explist = explist->next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
char* toascii(char c) {
|
||||
char* result = (char*)malloc(sizeof(char) * (countplaces(c) + 1));
|
||||
sprintf(result, "%i", c);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Dealing with singular terms
|
||||
LINEBLOCK* compilestrconst(char* str) {
|
||||
if(str[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
char* pushchar[] = { "push", "constant", toascii(str[0]) };
|
||||
LINEBLOCK* blk = mklnblk(mksimpleln(pushchar, strcount(pushchar)));
|
||||
free(pushchar[2]);
|
||||
|
||||
char* appendchar[] = { "call", "String.appendChar", "2" };
|
||||
appendln(blk, mkln(appendchar));
|
||||
|
||||
int i = 1;
|
||||
char c;
|
||||
while(c = str[i], c != '\0') {
|
||||
pushchar[2] = toascii(c);
|
||||
appendln(blk, mksimpleln(pushchar, strcount(pushchar)));
|
||||
free(pushchar[2]);
|
||||
appendln(blk, mksimpleln(appendchar, strcount(appendchar)));
|
||||
i++;
|
||||
}
|
||||
|
||||
char* strsize[] = { "push", "constant", itoa(i) };
|
||||
char* mknew[] = { "call", "String.new", "1" };
|
||||
appendlnbefore(blk, mkln(mknew));
|
||||
appendlnbefore(blk, mkln(strsize));
|
||||
free(strsize[2]);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINE* pushthisadd() {
|
||||
char* pushthisadd[] = { "push", "pointer", "0" };
|
||||
return mkln(pushthisadd);
|
||||
}
|
||||
|
||||
LINE* pushfalse() {
|
||||
return pushconstant(0);
|
||||
}
|
||||
|
||||
LINEBLOCK* pushtrue() {
|
||||
LINEBLOCK* blk = mklnblk(pushfalse());
|
||||
appendln(blk, onetoken("not"));
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilekeywordconst(SCOPE* s, TERM* t) {
|
||||
if(!strcmp(t->string, "this")) return mklnblk(pushthisadd());
|
||||
if(!strcmp(t->string, "false")) return mklnblk(pushfalse());
|
||||
if(!strcmp(t->string, "true")) return pushtrue();
|
||||
return mklnblk(pushconstant(0));
|
||||
}
|
||||
|
||||
LINEBLOCK* compilearrayitem(SCOPE* s, TERM* t) {
|
||||
LINEBLOCK* blk = compileexpression(s, t->array->exp);
|
||||
appendln(blk, pushvar(s, t->array->name));
|
||||
|
||||
appendln(blk, onetoken("add"));
|
||||
|
||||
appendln(blk, popthatadd());
|
||||
appendln(blk, pushthat());
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilecallln(SCOPE* s, SUBROUTDEC* d, SUBROUTCALL* call) {
|
||||
LINE* ln = mkline(3);
|
||||
|
||||
addtoken(ln, ezheapstr("call"));
|
||||
|
||||
addtoken(ln, dotlabel(d->class->name, call->name));
|
||||
|
||||
int count = countexpressions(call->parameters);
|
||||
if(d->subroutclass == method)
|
||||
count++;
|
||||
addtoken(ln, itoa(count));
|
||||
|
||||
return mklnblk(ln);
|
||||
}
|
||||
|
||||
LINEBLOCK* compilesubroutcall(SCOPE* s, SUBROUTCALL* call) {
|
||||
VAR* v;
|
||||
SUBROUTDEC* d = getsubroutdecfromcall(s, call, &v);
|
||||
LINEBLOCK* blk = compilecallln(s, d, call);
|
||||
|
||||
if(call->parameters != NULL)
|
||||
blk = mergelnblks(compileexplist(s, call->parameters), blk);
|
||||
|
||||
if(d->subroutclass == method) {
|
||||
if(call->parentname == NULL)
|
||||
appendlnbefore(blk, pushthisadd());
|
||||
else
|
||||
appendlnbefore(blk, pushvarraw(s, v));
|
||||
}
|
||||
|
||||
// void functions always return 0
|
||||
// therefore must be thrown away
|
||||
if(!strcmp(d->type, "void")) {
|
||||
appendln(blk, poptemp());
|
||||
}
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* pushunaryopterm(SCOPE* s, TERM* t) {
|
||||
LINEBLOCK* blk = compileexpression(s, t->expression);
|
||||
LINE* neg;
|
||||
if(t->unaryop == '-')
|
||||
neg = onetoken("neg");
|
||||
else
|
||||
neg = onetoken("not");
|
||||
appendln(blk, neg);
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compileterm(SCOPE* s, TERM* t) {
|
||||
if(t->type == varname) return mklnblk(pushvar(s, t->string));
|
||||
if(t->type == intconstant) return mklnblk(pushconstant(t->integer));
|
||||
if(t->type == stringconstant) return compilestrconst(t->string);
|
||||
if(t->type == keywordconstant) return compilekeywordconst(s, t);
|
||||
if(t->type == arrayitem) return compilearrayitem(s, t);
|
||||
if(t->type == subroutcall) return compilesubroutcall(s, t->call);
|
||||
if(t->type == innerexpression) return compileexpression(s, t->expression);
|
||||
return pushunaryopterm(s, t);
|
||||
}
|
||||
|
||||
// Dealing with whole expressions
|
||||
LINEBLOCK* compileexpression(SCOPE* s, TERM* e) {
|
||||
LINEBLOCK* blk = NULL;
|
||||
if(e != NULL) {
|
||||
while(true) {
|
||||
blk = mergelnblks(blk, compileterm(s, e));
|
||||
if(e->next == NULL)
|
||||
break;
|
||||
appendln(blk, mathopln(e->op));
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compileexplist(SCOPE* s, EXPRESSIONLIST* explist) {
|
||||
LINEBLOCK* head = NULL;
|
||||
while(explist != NULL) {
|
||||
head = mergelnblks(head, compileexpression(s, explist->expression));
|
||||
explist = explist->next;
|
||||
}
|
||||
return head;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef COMPILER_EXPRESSIONS_H
|
||||
#define COMPILER_EXPRESSIONS_H
|
||||
#include "vm-lines.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-expressions
|
||||
* Functions for dealing and compiling expressions and singular terms. */
|
||||
|
||||
// Dealing with singular terms
|
||||
LINEBLOCK* compilesubroutcall(SCOPE* s, SUBROUTCALL* call);
|
||||
|
||||
// Dealing with whole expressions
|
||||
LINEBLOCK* compileexpression(SCOPE* s, TERM* e);
|
||||
LINEBLOCK* compileexplist(SCOPE* s, EXPRESSIONLIST* explist);
|
||||
#endif
|
|
@ -0,0 +1,310 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "compiler.h"
|
||||
#include "compiler-scopes.h"
|
||||
#include "os.h"
|
||||
|
||||
typedef enum { local, staticseg, arg, fieldseg } MEMSEGMENT;
|
||||
char* memsegnames[] = { "local", "static", "argument", "this" };
|
||||
|
||||
// Error messages
|
||||
void doubledeclaration(const char* name, DEBUGINFO* d1, DEBUGINFO* d2);
|
||||
void ensurenoduplicate(SCOPE* s, char* name);
|
||||
|
||||
// Getters
|
||||
VAR* getvarinvars(VAR* vars, const char* name);
|
||||
CLASS* getclass(SCOPE* s, const char* name);
|
||||
SUBROUTDEC* getsubroutdecfromlist(SUBROUTDEC* start, char* name);
|
||||
SUBROUTDEC* getmethod(SCOPE* s, VAR* parent, SUBROUTCALL* call);
|
||||
SUBROUTDEC* getfunction(SCOPE* s, SUBROUTCALL* call);
|
||||
SUBROUTDEC* getsubroutdecwithparent(SCOPE* s, SUBROUTCALL* call, VAR** varret);
|
||||
SUBROUTDEC* getsubroutdecwithoutparent(SCOPE* s, SUBROUTCALL* call);
|
||||
SUBROUTDEC* getsubroutdec(SCOPE* s, const char* name);
|
||||
|
||||
// Scope adding
|
||||
VAR* mkvar(char* type, char* name, bool primitive, DEBUGINFO* debug, MEMSEGMENT seg, int i);
|
||||
void addvar(SCOPE* s, VAR** dest, VAR* v);
|
||||
void addlocalvar(SCOPE* s, VARDEC* v, int* i);
|
||||
void addstaticvar(SCOPE* s, CLASSVARDEC* v);
|
||||
void addfield(SCOPE* s, CLASSVARDEC* v, int* i);
|
||||
void addclassvardec(SCOPE* s, CLASSVARDEC* v, int* i);
|
||||
void addparameter(SCOPE* s, PARAMETER* p, int* i);
|
||||
|
||||
// Error messages
|
||||
void doubledeclaration(const char* name, DEBUGINFO* d1, DEBUGINFO* d2) {
|
||||
eprintf("Double declaration of '%s' at '%s', line %i; previously defined at '%s', line %i\n",
|
||||
name, d1->file, d1->definedat, d2->file, d2->definedat);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void notdeclared(const char* name, DEBUGINFO* debug) {
|
||||
eprintf("'%s' not declared; file '%s', line %i\n", name, debug->file, debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void invalidparent(SUBROUTCALL* call) {
|
||||
eprintf("Invalid subroutine parent '%s'; file '%s', line %i\n", call->parentname, call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void ensurenoduplicate(SCOPE* s, char* name) {
|
||||
VAR* v = getvar(s, name);
|
||||
if(v != NULL)
|
||||
doubledeclaration(name, s->currdebug, v->debug);
|
||||
|
||||
CLASS* c = getclass(s, name);
|
||||
if(c != NULL)
|
||||
doubledeclaration(name, s->currdebug, c->debug);
|
||||
|
||||
SUBROUTDEC* sr = getsubroutdec(s, name);
|
||||
if(sr != NULL)
|
||||
doubledeclaration(name, s->currdebug, sr->debug);
|
||||
}
|
||||
|
||||
// Scope handling
|
||||
SCOPE* mkscope(SCOPE* prev) {
|
||||
SCOPE* s = (SCOPE*)malloc(sizeof(SCOPE));
|
||||
s->previous = prev;
|
||||
if(prev != NULL)
|
||||
s->compiler = prev->compiler;
|
||||
s->localvars = NULL;
|
||||
s->fields = NULL;
|
||||
s->staticvars = NULL;
|
||||
s->parameters = NULL;
|
||||
s->classes = NULL;
|
||||
s->subroutines = NULL;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Getters
|
||||
VAR* getvarinvars(VAR* vars, const char* name) {
|
||||
while(vars != NULL) {
|
||||
if(!strcmp(vars->name, name))
|
||||
return vars;
|
||||
vars = vars->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VAR* getvar(SCOPE* s, const char* name) {
|
||||
VAR* var = getvarinvars(s->localvars, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
var = getvarinvars(s->parameters, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
var = getvarinvars(s->fields, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
var = getvarinvars(s->staticvars, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
if(s->previous != NULL)
|
||||
return getvar(s->previous, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CLASS* getclass(SCOPE* s, const char* name) {
|
||||
CLASS* curr = s->classes;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
if(s->previous != NULL)
|
||||
return getclass(s->previous, name);
|
||||
return getosclass(s->compiler->os, name);
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecfromlist(SUBROUTDEC* start, char* name) {
|
||||
while(start != NULL) {
|
||||
if(!strcmp(start->name, name))
|
||||
return start;
|
||||
start = start->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getmethod(SCOPE* s, VAR* parent, SUBROUTCALL* call) {
|
||||
CLASS* c = getclass(s, parent->type);
|
||||
SUBROUTDEC* d = getsubroutdecfromlist(c->subroutdecs, call->name);
|
||||
if(d == NULL)
|
||||
return NULL;
|
||||
if(d->subroutclass != method) {
|
||||
eprintf("Calling a function/constructor as if it were a method; file '%s', line %i\n", call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getfunction(SCOPE* s, SUBROUTCALL* call) {
|
||||
CLASS* c = getclass(s, call->parentname);
|
||||
if(c == NULL)
|
||||
notdeclared(call->parentname, call->debug);
|
||||
SUBROUTDEC* d = getsubroutdecfromlist(c->subroutdecs, call->name);
|
||||
if(d == NULL)
|
||||
return NULL;
|
||||
if(d->subroutclass == method) {
|
||||
eprintf("Calling a method as if it were a function; file '%s', line %i\n", call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecwithparent(SCOPE* s, SUBROUTCALL* call, VAR** varret) {
|
||||
VAR* parent = getvar(s, call->parentname);
|
||||
if(parent != NULL) {
|
||||
if(parent->primitive) {
|
||||
eprintf("Primitive type does not have subroutines; file '%s', line %i\n", call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
*varret = parent;
|
||||
return getmethod(s, parent, call);
|
||||
}
|
||||
else
|
||||
return getfunction(s, call);
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecwithoutparent(SCOPE* s, SUBROUTCALL* call) {
|
||||
SUBROUTDEC* d = getsubroutdecfromlist(s->currclass->subroutdecs, call->name);
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecfromcall(SCOPE* s, SUBROUTCALL* call, VAR** varret) {
|
||||
SUBROUTDEC* d;
|
||||
*varret = NULL;
|
||||
if(call->parentname != NULL) {
|
||||
d = getossubroutdec(s->compiler->os, call);
|
||||
if(d == NULL)
|
||||
d = getsubroutdecwithparent(s, call, varret);
|
||||
}
|
||||
else {
|
||||
d = getsubroutdecwithoutparent(s, call);
|
||||
}
|
||||
if(d == NULL)
|
||||
notdeclared(call->name, call->debug);
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdec(SCOPE* s, const char* name) {
|
||||
SUBROUTDEC* curr = s->subroutines;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
if(s->previous != NULL)
|
||||
return getsubroutdec(s->previous, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Scope adding
|
||||
VAR* mkvar(char* type, char* name, bool primitive, DEBUGINFO* debug, MEMSEGMENT seg, int i) {
|
||||
VAR* v = (VAR*)malloc(sizeof(VAR));
|
||||
v->name = name;
|
||||
v->type = type;
|
||||
v->debug = debug;
|
||||
v->memsegment = memsegnames[seg];
|
||||
v->primitive = primitive;
|
||||
v->index = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
void addvar(SCOPE* s, VAR** dest, VAR* v) {
|
||||
ensurenoduplicate(s, v->name);
|
||||
|
||||
if(!v->primitive) {
|
||||
CLASS* type = getclass(s, v->type);
|
||||
if(type == NULL)
|
||||
notdeclared(v->type, v->debug);
|
||||
}
|
||||
|
||||
v->next = *dest;
|
||||
*dest = v;
|
||||
}
|
||||
|
||||
void addlocalvar(SCOPE* s, VARDEC* v, int* i) {
|
||||
STRINGLIST* currname = v->names;
|
||||
while(currname != NULL) {
|
||||
addvar(s, &(s->localvars), mkvar(v->type, currname->content, v->primitive, v->debug, local, *i));
|
||||
currname = currname->next;
|
||||
(*i)++;
|
||||
}
|
||||
}
|
||||
|
||||
void addstaticvar(SCOPE* s, CLASSVARDEC* v) {
|
||||
STRINGLIST* currname = v->base->names;
|
||||
pthread_mutex_lock(&(s->compiler->staticmutex));
|
||||
static int i = 0;
|
||||
while(currname != NULL) {
|
||||
addvar(s, &(s->staticvars), mkvar(v->base->type, currname->content, v->base->primitive, v->base->debug, staticseg, i));
|
||||
currname = currname->next;
|
||||
i++;
|
||||
}
|
||||
pthread_mutex_unlock(&(s->compiler->staticmutex));
|
||||
}
|
||||
|
||||
void addfield(SCOPE* s, CLASSVARDEC* v, int* i) {
|
||||
STRINGLIST* currname = v->base->names;
|
||||
while(currname != NULL) {
|
||||
addvar(s, &(s->fields), mkvar(v->base->type, currname->content, v->base->primitive, v->base->debug, fieldseg, *i));
|
||||
currname = currname->next;
|
||||
(*i)++;
|
||||
}
|
||||
}
|
||||
|
||||
void addclassvardec(SCOPE* s, CLASSVARDEC* v, int* i) {
|
||||
if(v->type == stat)
|
||||
addstaticvar(s, v);
|
||||
else {
|
||||
addfield(s, v, i);
|
||||
}
|
||||
}
|
||||
|
||||
void addparameter(SCOPE* s, PARAMETER* p, int* i) {
|
||||
addvar(s, &(s->parameters), mkvar(p->type, p->name, p->primitive, p->debug, arg, *i));
|
||||
(*i)++;
|
||||
}
|
||||
|
||||
// Group adding
|
||||
void addclassvardecs(SCOPE* s, CLASSVARDEC* classvardecs) {
|
||||
int i = 0;
|
||||
while(classvardecs != NULL) {
|
||||
addclassvardec(s, classvardecs, &i);
|
||||
classvardecs = classvardecs->next;
|
||||
}
|
||||
}
|
||||
|
||||
void addlocalvars(SCOPE* s, VARDEC* localvars) {
|
||||
int i = 0;
|
||||
while(localvars != NULL) {
|
||||
addlocalvar(s, localvars, &i);
|
||||
localvars = localvars->next;
|
||||
}
|
||||
}
|
||||
|
||||
void addparameters(SCOPE* s, bool isformethod, PARAMETER* params) {
|
||||
int i = isformethod ? 1 : 0;
|
||||
while(params != NULL) {
|
||||
addparameter(s, params, &i);
|
||||
params = params->next;
|
||||
}
|
||||
}
|
||||
|
||||
void freevars(VAR* v) {
|
||||
if(v != NULL) {
|
||||
VAR* next = v->next;
|
||||
free(v);
|
||||
freevars(next);
|
||||
}
|
||||
}
|
||||
|
||||
void freescope(SCOPE* s) {
|
||||
freevars(s->fields);
|
||||
freevars(s->staticvars);
|
||||
freevars(s->localvars);
|
||||
freevars(s->parameters);
|
||||
free(s);
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef COMPILER_SCOPES_H
|
||||
#define COMPILER_SCOPES_H
|
||||
#include "parser-tree.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-scopes
|
||||
* Tools for dealing with scopes.
|
||||
*
|
||||
* They can be used to create, expand and stack scopes, as well as to enforce
|
||||
* certain semantic rules. */
|
||||
|
||||
// Data types
|
||||
typedef struct var {
|
||||
DEBUGINFO* debug;
|
||||
char* memsegment;
|
||||
char* type;
|
||||
char* name;
|
||||
int index;
|
||||
bool primitive;
|
||||
struct var* next;
|
||||
} VAR;
|
||||
|
||||
typedef struct scope {
|
||||
struct compiler* compiler;
|
||||
DEBUGINFO* currdebug;
|
||||
CLASS* currclass;
|
||||
|
||||
CLASS* classes;
|
||||
SUBROUTDEC* subroutines;
|
||||
|
||||
VAR* fields;
|
||||
VAR* staticvars;
|
||||
VAR* localvars;
|
||||
VAR* parameters;
|
||||
|
||||
struct scope* previous;
|
||||
} SCOPE;
|
||||
|
||||
struct compiler;
|
||||
|
||||
// Group adding
|
||||
void addclassvardecs(SCOPE* s, CLASSVARDEC* classvardecs);
|
||||
void addlocalvars(SCOPE* s, VARDEC* localvars);
|
||||
void addparameters(SCOPE* s, bool isformethod, PARAMETER* params);
|
||||
|
||||
// Scope handling
|
||||
SCOPE* mkscope(SCOPE* prev);
|
||||
|
||||
// Single type getters
|
||||
SUBROUTDEC* getsubroutdecfromcall(SCOPE* s, SUBROUTCALL* call, VAR** varret);
|
||||
CLASS* getclass(SCOPE* s, const char* name);
|
||||
|
||||
// Generic getters
|
||||
VAR* getvar(SCOPE* s, const char* name);
|
||||
|
||||
// Freeing
|
||||
void freescope(SCOPE* s);
|
||||
#endif
|
|
@ -0,0 +1,171 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "compiler-expressions.h"
|
||||
#include "compiler-statements.h"
|
||||
#include "compiler-util.h"
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
LINE* popthat();
|
||||
LINE* pushtemp();
|
||||
char* mkcondlabel(char* name, int count);
|
||||
|
||||
// Handling individual statements
|
||||
LINEBLOCK* compileret(SCOPE* s, TERM* e);
|
||||
LINEBLOCK* compileif(SCOPE* s, IFSTATEMENT* st);
|
||||
LINEBLOCK* compilewhile(SCOPE* s, CONDSTATEMENT* w);
|
||||
LINEBLOCK* compilelet(SCOPE* s, LETSTATEMENT* l);
|
||||
LINEBLOCK* compilestatement(SCOPE* s, STATEMENT* st);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
|
||||
|
||||
// Miscelaneous
|
||||
LINE* popthat() {
|
||||
char* popthat[] = { "pop", "that", "0" };
|
||||
return mkln(popthat);
|
||||
}
|
||||
|
||||
LINE* pushtemp() {
|
||||
char* pushtemp[] = { "push", "temp", "0" };
|
||||
return mkln(pushtemp);
|
||||
}
|
||||
|
||||
char* mkcondlabel(char* name, int count) {
|
||||
int sz = (strlen(name) + countplaces(count) + 1) * sizeof(char);
|
||||
char* result = (char*)malloc(sz);
|
||||
sprintf(result, "%s%i", name, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Handling individual statements
|
||||
LINEBLOCK* compileret(SCOPE* s, TERM* e) {
|
||||
LINE* ret = onetoken("return");
|
||||
LINEBLOCK* blk = mklnblk(ret);
|
||||
|
||||
// void subroutdecs return 0
|
||||
if(e == NULL) {
|
||||
char* tokens[] = { "push", "constant", "0" };
|
||||
appendlnbefore(blk, mkln(tokens));
|
||||
} else
|
||||
blk = mergelnblks(compileexpression(s, e), blk);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compileif(SCOPE* s, IFSTATEMENT* st) {
|
||||
LINEBLOCK* blk = compileexpression(s, st->base->expression);
|
||||
|
||||
pthread_mutex_lock(&(s->compiler->ifmutex));
|
||||
static int ifcount = 0;
|
||||
int mycount = ifcount;
|
||||
ifcount++;
|
||||
pthread_mutex_unlock(&(s->compiler->ifmutex));
|
||||
|
||||
char* truelabel = mkcondlabel("IF_TRUE", mycount);
|
||||
char* ifgoto[] = { "if-goto", truelabel };
|
||||
appendln(blk, mkln(ifgoto));
|
||||
|
||||
char* falselabel = mkcondlabel("IF_FALSE", mycount);
|
||||
char* gotofalse[] = { "goto", falselabel };
|
||||
appendln(blk, mkln(gotofalse));
|
||||
|
||||
char* truelabelln[] = { "label", truelabel };
|
||||
appendln(blk, mkln(truelabelln));
|
||||
|
||||
blk = mergelnblks(blk, compilestatements(s, st->base->statements));
|
||||
|
||||
char* endlabel;
|
||||
bool haselse = st->elsestatements != NULL;
|
||||
if(haselse) {
|
||||
endlabel = mkcondlabel("IF_END", mycount);
|
||||
char* endgoto[] = { "goto", endlabel };
|
||||
appendln(blk, mkln(endgoto));
|
||||
}
|
||||
|
||||
char* falselabelln[] = { "label", falselabel};
|
||||
appendln(blk, mkln(falselabelln));
|
||||
|
||||
if(haselse) {
|
||||
blk = mergelnblks(blk, compilestatements(s, st->elsestatements));
|
||||
char* endlabelln[] = { "label", endlabel };
|
||||
appendln(blk, mkln(endlabelln));
|
||||
free(endlabel);
|
||||
}
|
||||
|
||||
free(falselabel);
|
||||
free(truelabel);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilewhile(SCOPE* s, CONDSTATEMENT* w) {
|
||||
LINEBLOCK* blk = compileexpression(s, w->expression);
|
||||
|
||||
pthread_mutex_lock(&(s->compiler->whilemutex));
|
||||
static int whilecount = 0;
|
||||
int mycount = whilecount;
|
||||
whilecount++;
|
||||
pthread_mutex_unlock(&(s->compiler->whilemutex));
|
||||
|
||||
char* explabel = mkcondlabel("WHILE_EXP", mycount);
|
||||
char* explabelln[] = { "label", explabel };
|
||||
appendlnbefore(blk, mkln(explabelln));
|
||||
|
||||
appendln(blk, onetoken("not"));
|
||||
|
||||
char* endlabel = mkcondlabel("WHILE_END", mycount);
|
||||
char* ifgoto[] = { "if-goto", endlabel };
|
||||
appendln(blk, mkln(ifgoto));
|
||||
|
||||
blk = mergelnblks(blk, compilestatements(s, w->statements));
|
||||
|
||||
char* gotoln[] = { "goto", explabel };
|
||||
appendln(blk, mkln(gotoln));
|
||||
|
||||
char* endlabelln[] = { "label", endlabel };
|
||||
appendln(blk, mkln(endlabelln));
|
||||
|
||||
free(explabel);
|
||||
free(endlabel);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilelet(SCOPE* s, LETSTATEMENT* l) {
|
||||
LINEBLOCK* blk = compileexpression(s, l->expression);
|
||||
|
||||
if(l->arrayind != NULL) {
|
||||
appendlnbefore(blk, onetoken("add"));
|
||||
appendlnbefore(blk, pushvar(s, l->varname));
|
||||
blk = mergelnblks(compileexpression(s, l->arrayind), blk);
|
||||
|
||||
appendln(blk, poptemp());
|
||||
appendln(blk, popthatadd());
|
||||
appendln(blk, pushtemp());
|
||||
appendln(blk, popthat());
|
||||
}
|
||||
else
|
||||
appendln(blk, popvar(s, l->varname));
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilestatement(SCOPE* s, STATEMENT* st) {
|
||||
s->currdebug = st->debug;
|
||||
if(st->type == dostatement) return compilesubroutcall(s, st->dostatement);
|
||||
if(st->type == returnstatement) return compileret(s, st->retstatement);
|
||||
if(st->type == ifstatement) return compileif(s, st->ifstatement);
|
||||
if(st->type == whilestatement) return compilewhile(s, st->whilestatement);
|
||||
return compilelet(s, st->letstatement);
|
||||
}
|
||||
|
||||
LINEBLOCK* compilestatements(SCOPE* s, STATEMENT* sts) {
|
||||
LINEBLOCK* head = NULL;
|
||||
while(sts != NULL) {
|
||||
head = mergelnblks(head, compilestatement(s, sts));
|
||||
sts = sts->next;
|
||||
}
|
||||
return head;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef COMPILER_STATEMENTS_H
|
||||
#define COMPILER_STATEMENTS_H
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-statements
|
||||
* Single function for compiling statements */
|
||||
|
||||
LINEBLOCK* compilestatements(SCOPE* s, STATEMENT* sts);
|
||||
#endif
|
|
@ -0,0 +1,135 @@
|
|||
#include <stdlib.h>
|
||||
#include "compiler-statements.h"
|
||||
#include "compiler-structure.h"
|
||||
#include "compiler-util.h"
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
int countlocalvars(VARDEC* decs);
|
||||
int countstrs(STRINGLIST* ls);
|
||||
int getobjsize(CLASS* c);
|
||||
LINE* mksubdeclabel(CLASS* c, SUBROUTDEC* sd);
|
||||
|
||||
// Compiling methods
|
||||
LINEBLOCK* compilefunbody(SCOPE* s, CLASS* cl, SUBROUTBODY* b);
|
||||
LINEBLOCK* compilefundec(SCOPE* s, CLASS* cl, SUBROUTDEC* f);
|
||||
LINEBLOCK* compileconstructor(SCOPE* s, CLASS* cl, SUBROUTDEC* con);
|
||||
LINEBLOCK* compilemethod(SCOPE* s, CLASS* cl, SUBROUTDEC* m);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
|
||||
// Miscelaneous
|
||||
int countlocalvars(VARDEC* decs) {
|
||||
int i = 0;
|
||||
while(decs != NULL) {
|
||||
STRINGLIST* curr = decs->names;
|
||||
while(curr != NULL) {
|
||||
i++;
|
||||
curr = curr->next;
|
||||
}
|
||||
decs = decs->next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int countstrs(STRINGLIST* ls) {
|
||||
int count = 0;
|
||||
while(ls != NULL) {
|
||||
count++;
|
||||
ls = ls->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int getobjsize(CLASS* c) {
|
||||
CLASSVARDEC* curr = c->vardecs;
|
||||
int count = 0;
|
||||
while(curr != NULL) {
|
||||
if(curr->type == field)
|
||||
count += countstrs(curr->base->names);
|
||||
curr = curr->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
LINE* mksubdeclabel(CLASS* c, SUBROUTDEC* sd) {
|
||||
char* labelstrs[] = { "function", dotlabel(c->name, sd->name), itoa(countlocalvars(sd->body->vardecs)) };
|
||||
LINE* label = mkln(labelstrs);
|
||||
free(labelstrs[1]);
|
||||
free(labelstrs[2]);
|
||||
label->next = NULL;
|
||||
return label;
|
||||
}
|
||||
|
||||
// Compiling methods
|
||||
LINEBLOCK* compilefunbody(SCOPE* s, CLASS* cl, SUBROUTBODY* b) {
|
||||
SCOPE* myscope = mkscope(s);
|
||||
myscope->currclass = cl;
|
||||
if(b->vardecs != NULL)
|
||||
addlocalvars(myscope, b->vardecs);
|
||||
LINEBLOCK* head = compilestatements(myscope, b->statements);
|
||||
freescope(myscope);
|
||||
return head;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilefundec(SCOPE* s, CLASS* cl, SUBROUTDEC* f) {
|
||||
LINE* label = mksubdeclabel(cl, f);
|
||||
|
||||
if(f->body->statements != NULL) {
|
||||
LINEBLOCK* body = compilefunbody(s, cl, f->body);
|
||||
appendlnbefore(body, label);
|
||||
return body;
|
||||
}
|
||||
else
|
||||
return mklnblk(label);
|
||||
}
|
||||
|
||||
LINEBLOCK* compileconstructor(SCOPE* s, CLASS* cl, SUBROUTDEC* con) {
|
||||
LINE* label = mksubdeclabel(cl, con);
|
||||
LINEBLOCK* blk = mklnblk(label);
|
||||
|
||||
char* size[] = { "push", "constant", itoa(getobjsize(cl)) };
|
||||
char* memalloc[] = { "call", "Memory.alloc", "1" };
|
||||
char* poppointer[] = { "pop", "pointer", "0" };
|
||||
appendln(blk, mkln(size));
|
||||
appendln(blk, mkln(memalloc));
|
||||
appendln(blk, mkln(poppointer));
|
||||
free(size[2]);
|
||||
|
||||
if(con->body != NULL)
|
||||
return mergelnblks(blk, compilefunbody(s, cl, con->body));
|
||||
else
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilemethod(SCOPE* s, CLASS* cl, SUBROUTDEC* m) {
|
||||
LINE* label = mksubdeclabel(cl, m);
|
||||
LINEBLOCK* blk = mklnblk(label);
|
||||
|
||||
char* pusharg0[] = { "push", "argument", "0" };
|
||||
char* poppointer[] = { "pop", "pointer", "0" };
|
||||
appendln(blk, mkln(pusharg0));
|
||||
appendln(blk, mkln(poppointer));
|
||||
|
||||
if(m->body != NULL)
|
||||
return mergelnblks(blk, compilefunbody(s, cl, m->body));
|
||||
else
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilesubroutdec(SCOPE* s, CLASS* cl, SUBROUTDEC* sd) {
|
||||
SCOPE* myscope = mkscope(s);
|
||||
LINEBLOCK* blk;
|
||||
if(sd->parameters != NULL)
|
||||
addparameters(myscope, sd->subroutclass == method, sd->parameters);
|
||||
if(sd->subroutclass == function)
|
||||
blk = compilefundec(myscope, cl, sd);
|
||||
else if(sd->subroutclass == constructor)
|
||||
blk = compileconstructor(myscope, cl, sd);
|
||||
else
|
||||
blk = compilemethod(myscope, cl, sd);
|
||||
freescope(myscope);
|
||||
return blk;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef COMPILER_STRUCTURE_H
|
||||
#define COMPILER_STRUCTURE_H
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-structure
|
||||
* Module for dealing with and compiling general program structure. */
|
||||
|
||||
LINEBLOCK* compilesubroutdec(SCOPE* s, CLASS* cl, SUBROUTDEC* sd);
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
#include <stdlib.h>
|
||||
#include "compiler-util.h"
|
||||
|
||||
LINE* opvarraw(SCOPE* s, char* op, VAR* v) {
|
||||
char* tokens[] = { op, v->memsegment, itoa(v->index) };
|
||||
LINE* ln = mksimpleln(tokens, strcount(tokens));
|
||||
free(tokens[2]);
|
||||
return ln;
|
||||
}
|
||||
|
||||
LINE* opvar(SCOPE* s, char* op, const char* name) {
|
||||
VAR* v = getvar(s, name);
|
||||
return opvarraw(s, op, v);
|
||||
}
|
||||
|
||||
LINE* pushvarraw(SCOPE*s, VAR* v) {
|
||||
return opvarraw(s, "push", v);
|
||||
}
|
||||
|
||||
LINE* pushvar(SCOPE* s, const char* name) {
|
||||
return opvar(s, "push", name);
|
||||
}
|
||||
|
||||
LINE* popvar(SCOPE* s, const char* name) {
|
||||
return opvar(s, "pop", name);
|
||||
}
|
||||
|
||||
LINE* poptemp() {
|
||||
char* poptemp[] = { "pop", "temp", "0" };
|
||||
return mksimpleln(poptemp, strcount(poptemp));
|
||||
}
|
||||
|
||||
LINE* popthatadd() {
|
||||
char* popthatadd[] = { "pop", "pointer", "1" };
|
||||
return mksimpleln(popthatadd, strcount(popthatadd));
|
||||
}
|
||||
|
||||
LINE* onetoken(char* str) {
|
||||
LINE* ln = mkline(1);
|
||||
addtoken(ln, ezheapstr(str));
|
||||
ln->next = NULL;
|
||||
return ln;
|
||||
}
|
||||
|
||||
LINE* mksimpleln(char** tokens, int count) {
|
||||
LINE* ln = mkline(count);
|
||||
for(int i = 0; i < count; i++)
|
||||
addtoken(ln, ezheapstr(tokens[i]));
|
||||
ln->next = NULL;
|
||||
return ln;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef COMPILER_UTIL_H
|
||||
#define COMPILER_UTIL_H
|
||||
#include "vm-lines.h"
|
||||
#include "compiler-scopes.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-util
|
||||
* Random utilities used in the compiler module. */
|
||||
|
||||
#define mkln(id) mksimpleln(id, strcount(id))
|
||||
|
||||
LINE* onetoken(char* str);
|
||||
LINE* mksimpleln(char** tokens, int count);
|
||||
|
||||
LINE* pushvarraw(SCOPE*s, VAR* v);
|
||||
LINE* pushvar(SCOPE* s, const char* name);
|
||||
LINE* popvar(SCOPE* s, const char* name);
|
||||
LINE* poptemp();
|
||||
LINE* popthatadd();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,46 @@
|
|||
#include <stdlib.h>
|
||||
#include "os.h"
|
||||
#include "compiler-structure.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* This should be part of compiler-structure, but since it is used by other modules,
|
||||
* it will stay here for convenience */
|
||||
LINEBLOCK* compileclass(COMPILER* c, CLASS* class) {
|
||||
SCOPE* topscope = mkscope(c->globalscope);
|
||||
if(class->vardecs != NULL)
|
||||
addclassvardecs(topscope, class->vardecs);
|
||||
if(class->subroutdecs != NULL)
|
||||
topscope->subroutines = class->subroutdecs;
|
||||
|
||||
LINEBLOCK* output = NULL;
|
||||
SUBROUTDEC* curr = class->subroutdecs;
|
||||
while(curr != NULL) {
|
||||
output = mergelnblks(output, compilesubroutdec(topscope, class, curr));
|
||||
curr = curr->next;
|
||||
}
|
||||
freescope(topscope);
|
||||
return output;
|
||||
}
|
||||
|
||||
COMPILER* mkcompiler(CLASS* classes) {
|
||||
COMPILER* c = (COMPILER*)malloc(sizeof(COMPILER));
|
||||
c->globalscope = mkscope(NULL);
|
||||
c->globalscope->compiler = c;
|
||||
c->globalscope->classes = classes;
|
||||
c->classes = classes;
|
||||
c->os = mkos();
|
||||
pthread_mutex_init(&(c->ifmutex), NULL);
|
||||
pthread_mutex_init(&(c->whilemutex), NULL);
|
||||
pthread_mutex_init(&(c->staticmutex), NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
void freecompiler(COMPILER* c) {
|
||||
pthread_mutex_destroy(&(c->ifmutex));
|
||||
pthread_mutex_destroy(&(c->whilemutex));
|
||||
pthread_mutex_destroy(&(c->staticmutex));
|
||||
// to be continued
|
||||
freeos(c->os);
|
||||
freescope(c->globalscope);
|
||||
free(c);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
#include <pthread.h>
|
||||
#include "parser-tree.h"
|
||||
#include "vm-lines.h"
|
||||
#include "compiler-scopes.h"
|
||||
|
||||
/* compiler
|
||||
* This is the file that should be included in other modules
|
||||
* that want to compile a class/program. */
|
||||
|
||||
struct scope;
|
||||
typedef struct compiler {
|
||||
pthread_mutex_t ifmutex;
|
||||
pthread_mutex_t whilemutex;
|
||||
pthread_mutex_t staticmutex;
|
||||
CLASS* classes;
|
||||
CLASS* os;
|
||||
struct scope* globalscope;
|
||||
} COMPILER;
|
||||
|
||||
COMPILER* mkcompiler(CLASS* classes);
|
||||
LINEBLOCK* compileclass(COMPILER* c, CLASS* class);
|
||||
void freecompiler(COMPILER* c);
|
||||
#endif
|
|
@ -0,0 +1,78 @@
|
|||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "threads.h"
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
#include "io.h"
|
||||
#include "os.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if(argc < 2) {
|
||||
eprintf("Usage: %s {input file(s)}\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILELIST* files = getfiles(argv[1]);
|
||||
FILELIST* curr = files->next;
|
||||
|
||||
COMPILEUNIT* head = (COMPILEUNIT*)malloc(sizeof(COMPILEUNIT));
|
||||
|
||||
head->file = files;
|
||||
head->parser = mkparser(tokenize(files->fullname), files->name);
|
||||
|
||||
COMPILEUNIT* currunit = head;
|
||||
while(curr != NULL) {
|
||||
COMPILEUNIT* newunit = (COMPILEUNIT*)malloc(sizeof(COMPILEUNIT));
|
||||
newunit->file = curr;
|
||||
newunit->parser = mkparser(tokenize(curr->fullname), curr->name);
|
||||
currunit->next = newunit;
|
||||
currunit = newunit;
|
||||
curr = curr->next;
|
||||
}
|
||||
currunit->next = NULL;
|
||||
|
||||
actonunits(head, parseunit);
|
||||
|
||||
CLASS* headclass = head->parsed;
|
||||
CLASS* currclass = headclass;
|
||||
currunit = head->next;
|
||||
while(currunit != NULL) {
|
||||
currclass->next = currunit->parsed;
|
||||
currclass = currunit->parsed;
|
||||
currunit = currunit->next;
|
||||
}
|
||||
currclass->next = NULL;
|
||||
COMPILER* compiler = mkcompiler(headclass);
|
||||
|
||||
currunit = head;
|
||||
while(currunit != NULL) {
|
||||
currunit->compiler = compiler;
|
||||
currunit = currunit->next;
|
||||
}
|
||||
|
||||
actonunits(head, compileunit);
|
||||
|
||||
actonunits(head, vmtranslateunit);
|
||||
|
||||
currunit = head;
|
||||
while(currunit != NULL) {
|
||||
FILE* output = fopen(currunit->file->outname, "w");
|
||||
if(output == NULL) {
|
||||
eprintf("%s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printstrlist(currunit->asmlns, output);
|
||||
fclose(output);
|
||||
COMPILEUNIT* next = currunit->next;
|
||||
freeunit(currunit);
|
||||
currunit = next;
|
||||
}
|
||||
|
||||
freecompiler(compiler);
|
||||
freetree(headclass);
|
||||
freefilelist(files);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include "util.h"
|
||||
#include "io.h"
|
||||
|
||||
#include <limits.h>
|
||||
#ifndef PATH_MAX
|
||||
#ifdef __linux__
|
||||
#include <linux/limits.h>
|
||||
#else
|
||||
#define PATH_MAX 512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
char* strtail(char* str, int len, int count) {
|
||||
int index = len - count;
|
||||
if (index <= 0) return str;
|
||||
return str + (sizeof(char) * (index));
|
||||
}
|
||||
|
||||
char* strhead(char* str, int count) {
|
||||
return str + (sizeof(char) * count);
|
||||
}
|
||||
|
||||
char* trimstr(char* str, int len, int end) {
|
||||
int count = len - end;
|
||||
char oldchar = str[count];
|
||||
str[count] = '\0';
|
||||
char* newstr = (char*)malloc(sizeof(char) * (1 + count));
|
||||
strcpy(newstr, str);
|
||||
str[count] = oldchar;
|
||||
return newstr;
|
||||
}
|
||||
|
||||
char* getname(char* f, int len) {
|
||||
int startind = 0;
|
||||
int endind = len - 1;
|
||||
bool readsmt = false;
|
||||
|
||||
for(int i = endind; i >= 0; i--) {
|
||||
if(f[i] == '/') {
|
||||
if(!readsmt) {
|
||||
endind = i-1;
|
||||
f[i] = '\0';
|
||||
continue;
|
||||
}
|
||||
startind = i+1;
|
||||
break;
|
||||
}
|
||||
readsmt = true;
|
||||
}
|
||||
|
||||
int sz = sizeof(char)*(endind - startind + 2);
|
||||
char* startstr = strhead(f, startind);
|
||||
char* retstr = (char*)malloc(sz);
|
||||
snprintf(retstr, sz, "%s", startstr);
|
||||
return retstr;
|
||||
}
|
||||
|
||||
char* getfullname(char* fname, int fnamelen, char* dirname, int dirlen) {
|
||||
int sz = sizeof(char)*(fnamelen+dirlen+2);
|
||||
char* fullname = (char*)malloc(sz);
|
||||
sprintf(fullname, "%s/%s", dirname, fname);
|
||||
return fullname;
|
||||
}
|
||||
|
||||
bool isdotjack(char* f, int len) {
|
||||
const char* ext = ".jack";
|
||||
return strcmp(strtail(f, len, strlen(ext)), ext) == 0;
|
||||
}
|
||||
|
||||
bool isdir(char* f, int len) {
|
||||
bool readsmt = false;
|
||||
for(int i = len-1; i >= 0; i--) {
|
||||
if(f[i] == '.') {
|
||||
if(readsmt)
|
||||
return false;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if(f[i] == '/')
|
||||
return 1;
|
||||
readsmt = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char* getoutname(char* fullname, int len) {
|
||||
char* trimmed = trimstr(fullname, len, 4);
|
||||
int sz = sizeof(char) * (len);
|
||||
char* outname = (char*)malloc(sz);
|
||||
snprintf(outname, sz, "%sasm", trimmed);
|
||||
free(trimmed);
|
||||
return outname;
|
||||
}
|
||||
|
||||
FILELIST* addfile(FILELIST* l, char* fullname, char* name) {
|
||||
FILELIST* new = (FILELIST*)malloc(sizeof(FILELIST));
|
||||
new->name = name;
|
||||
new->fullname = fullname;
|
||||
new->next = l;
|
||||
new->outname = getoutname(fullname, strlen(fullname));
|
||||
return new;
|
||||
}
|
||||
|
||||
FILELIST* getfilesfromdir(char* dir) {
|
||||
FILELIST* filelist = NULL;
|
||||
DIR* d = opendir(dir);
|
||||
|
||||
if(d == NULL) {
|
||||
eprintf("Error while opening directory '%s': %s\n", dir, strerror(errno));
|
||||
exit(errno);
|
||||
}
|
||||
|
||||
int len = strlen(dir);
|
||||
struct dirent* thisfile;
|
||||
while(thisfile = readdir(d), thisfile != NULL) {
|
||||
int thislen = strlen(thisfile->d_name);
|
||||
if(isdotjack(thisfile->d_name, thislen)) {
|
||||
char* fullname = getfullname(thisfile->d_name, thislen, dir, len);
|
||||
char* name = ezheapstr(thisfile->d_name);
|
||||
filelist = addfile(filelist, fullname, name);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
if(filelist == NULL) {
|
||||
eprintf("Directory '%s' doesn't have any .jack file\n", dir);
|
||||
exit(1);
|
||||
}
|
||||
return filelist;
|
||||
}
|
||||
|
||||
FILELIST* getsinglefile(char* file) {
|
||||
int len = strlen(file);
|
||||
if(isdotjack(file, len)){
|
||||
char* name = getname(file, len);
|
||||
char* fullname = heapstr(file, len);
|
||||
|
||||
FILE* input = fopen(fullname, "r");
|
||||
if(input == NULL) {
|
||||
eprintf("Error while reading file '%s': %s\n", file, strerror(errno));
|
||||
exit(errno);
|
||||
}
|
||||
fclose(input);
|
||||
|
||||
return addfile(NULL, fullname, name);
|
||||
}
|
||||
else {
|
||||
eprintf("Input file must be named like 'Xxx.vm'\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
FILELIST* getfiles(char* input) {
|
||||
int inplen = strlen(input);
|
||||
bool isitdir = isdir(input, inplen);
|
||||
|
||||
if(isitdir)
|
||||
return getfilesfromdir(input);
|
||||
else
|
||||
return getsinglefile(input);
|
||||
}
|
||||
|
||||
void freefilelist(FILELIST* fs) {
|
||||
free(fs->name);
|
||||
free(fs->fullname);
|
||||
free(fs->outname);
|
||||
FILELIST* next = fs->next;
|
||||
free(fs);
|
||||
if(next != NULL)
|
||||
freefilelist(next);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
typedef struct flist {
|
||||
char* name;
|
||||
char* fullname;
|
||||
char* outname;
|
||||
struct flist* next;
|
||||
} FILELIST;
|
||||
|
||||
|
||||
FILELIST* getfiles(char* input);
|
||||
void freefilelist(FILELIST* fs);
|
||||
#endif
|
|
@ -0,0 +1,166 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "os.h"
|
||||
#include "util.h"
|
||||
|
||||
CLASS* mkosclass(CLASS* os, const char* name) {
|
||||
CLASS* c = (CLASS*)malloc(sizeof(CLASS));
|
||||
c->name = ezheapstr(name);
|
||||
c->subroutdecs = NULL;
|
||||
c->next = os;
|
||||
return c;
|
||||
}
|
||||
|
||||
void adddec(CLASS* c, SUBROUTCLASS subroutclass, char* type, const char* name) {
|
||||
SUBROUTDEC* dec = (SUBROUTDEC*)malloc(sizeof(SUBROUTDEC));
|
||||
dec->class = c;
|
||||
dec->subroutclass = subroutclass;
|
||||
dec->name = ezheapstr(name);
|
||||
dec->type = ezheapstr(type);
|
||||
dec->next = c->subroutdecs;
|
||||
c->subroutdecs = dec;
|
||||
}
|
||||
|
||||
CLASS* mkmath(CLASS* os) {
|
||||
CLASS* mathclass = mkosclass(os, "Math");
|
||||
adddec(mathclass, function, "int", "multiply");
|
||||
adddec(mathclass, function, "int", "divide");
|
||||
adddec(mathclass, function, "int", "abs");
|
||||
adddec(mathclass, function, "int", "min");
|
||||
adddec(mathclass, function, "int", "max");
|
||||
adddec(mathclass, function, "int", "sqrt");
|
||||
return mathclass;
|
||||
}
|
||||
|
||||
CLASS* mkstringclass(CLASS* os) {
|
||||
CLASS* strclass = mkosclass(os, "String");
|
||||
adddec(strclass, constructor, "String", "new");
|
||||
adddec(strclass, method, "int", "dispose");
|
||||
adddec(strclass, method, "int", "length");
|
||||
adddec(strclass, method, "char", "charAt");
|
||||
adddec(strclass, method, "void", "setCharAt");
|
||||
adddec(strclass, method, "String", "appendChar");
|
||||
adddec(strclass, method, "void", "eraseLastChar");
|
||||
adddec(strclass, method, "int", "intValue");
|
||||
adddec(strclass, method, "void", "setInt");
|
||||
adddec(strclass, function, "char", "backSpace");
|
||||
adddec(strclass, function, "char", "doubleQuote");
|
||||
adddec(strclass, function, "char", "newLine");
|
||||
return strclass;
|
||||
}
|
||||
|
||||
CLASS* mkarray(CLASS* os) {
|
||||
CLASS* arrclass = mkosclass(os, "Array");
|
||||
adddec(arrclass, function, "Array", "new");
|
||||
adddec(arrclass, method, "void", "dispose");
|
||||
return arrclass;
|
||||
}
|
||||
|
||||
CLASS* mkoutput(CLASS* os) {
|
||||
CLASS* outclass = mkosclass(os, "Output");
|
||||
adddec(outclass, function, "void", "moveCursor");
|
||||
adddec(outclass, function, "void", "printChar");
|
||||
adddec(outclass, function, "void", "printString");
|
||||
adddec(outclass, function, "void", "printInt");
|
||||
adddec(outclass, function, "void", "println");
|
||||
adddec(outclass, function, "void", "backSpace");
|
||||
return outclass;
|
||||
}
|
||||
|
||||
CLASS* mkscreen(CLASS* os) {
|
||||
CLASS* scrclass = mkosclass(os, "Screen");
|
||||
adddec(scrclass, function, "void", "clearScreen");
|
||||
adddec(scrclass, function, "void", "setColor");
|
||||
adddec(scrclass, function, "void", "drawPixel");
|
||||
adddec(scrclass, function, "void", "drawLine");
|
||||
adddec(scrclass, function, "void", "drawRectangle");
|
||||
adddec(scrclass, function, "void", "drawCircle");
|
||||
return scrclass;
|
||||
}
|
||||
|
||||
CLASS* mkkeyboard(CLASS* os) {
|
||||
CLASS* kbdclass = mkosclass(os, "Keyboard");
|
||||
adddec(kbdclass, function, "char", "keyPressed");
|
||||
adddec(kbdclass, function, "char", "readChar");
|
||||
adddec(kbdclass, function, "String", "readLine");
|
||||
adddec(kbdclass, function, "int", "readInt");
|
||||
return kbdclass;
|
||||
}
|
||||
|
||||
CLASS* mkmemory(CLASS* os) {
|
||||
CLASS* memclass = mkosclass(os, "Memory");
|
||||
adddec(memclass, function, "int", "peek");
|
||||
adddec(memclass, function, "void", "poke");
|
||||
adddec(memclass, function, "Array", "alloc");
|
||||
adddec(memclass, function, "void", "deAlloc");
|
||||
return memclass;
|
||||
}
|
||||
|
||||
CLASS* mksys(CLASS* os) {
|
||||
CLASS* sysclass = mkosclass(os, "Sys");
|
||||
adddec(sysclass, function, "void", "halt");
|
||||
adddec(sysclass, function, "void", "error");
|
||||
adddec(sysclass, function, "void", "wait");
|
||||
return sysclass;
|
||||
}
|
||||
|
||||
CLASS* mkos() {
|
||||
CLASS* os = mkmath(NULL);
|
||||
os = mkstringclass(os);
|
||||
os = mkarray(os);
|
||||
os = mkoutput(os);
|
||||
os = mkscreen(os);
|
||||
os = mkkeyboard(os);
|
||||
os = mkmemory(os);
|
||||
os = mksys(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
void freeossubroutdecs(SUBROUTDEC* d) {
|
||||
free(d->name);
|
||||
free(d->type);
|
||||
SUBROUTDEC* next = d->next;
|
||||
free(d);
|
||||
if(next != NULL)
|
||||
freeossubroutdecs(next);
|
||||
}
|
||||
|
||||
void freeosclasses(CLASS* c) {
|
||||
freeossubroutdecs(c->subroutdecs);
|
||||
free(c->name);
|
||||
CLASS* next = c->next;
|
||||
free(c);
|
||||
if(next != NULL)
|
||||
freeosclasses(next);
|
||||
}
|
||||
|
||||
void freeos(CLASS* os) {
|
||||
freeosclasses(os);
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecinclass(CLASS* c, const char* name) {
|
||||
SUBROUTDEC* curr = c->subroutdecs;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CLASS* getosclass(CLASS* os, const char* name) {
|
||||
CLASS* curr = os;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getossubroutdec(CLASS* os, SUBROUTCALL* call) {
|
||||
CLASS* c = getosclass(os, call->parentname);
|
||||
if(c == NULL)
|
||||
return NULL;
|
||||
return getsubroutdecinclass(c, call->name);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef OS_H
|
||||
#define OS_H
|
||||
#include "parser-tree.h"
|
||||
|
||||
SUBROUTDEC* getossubroutdec(CLASS* os, SUBROUTCALL* call);
|
||||
CLASS* getosclass(CLASS* os, const char* name);
|
||||
CLASS* mkos();
|
||||
void freeos(CLASS* os);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,102 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "threads.h"
|
||||
|
||||
void* parseunit(void* input) {
|
||||
COMPILEUNIT* unit = (COMPILEUNIT*)input;
|
||||
|
||||
unit->parsed = parse(unit->parser);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void* compileunit(void* input) {
|
||||
COMPILEUNIT* unit = (COMPILEUNIT*)input;
|
||||
|
||||
unit->compiled = compileclass(unit->compiler, unit->parsed);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
char* getclassname(char* filename) {
|
||||
int count = 0;
|
||||
int len = strlen(filename);
|
||||
|
||||
for(int i = len-1; i >= 0; i--)
|
||||
if(filename[i] == '.') {
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
|
||||
int sz = sizeof(char) * (len - count);
|
||||
char* classname = (char*)malloc(sz);
|
||||
snprintf(classname, sz, "%s", filename); // legitimately needs to be snprintf
|
||||
return classname;
|
||||
}
|
||||
|
||||
void* vmtranslateunit(void* input) {
|
||||
COMPILEUNIT* unit = (COMPILEUNIT*)input;
|
||||
|
||||
if(unit->compiled == NULL) {
|
||||
eprintf("Class '%s' is empty; file '%s'\n", unit->parsed->name, unit->file->name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char* classname = getclassname(unit->file->name);
|
||||
unit->vmtranslator = mkvmtranslator(classname, unit->compiled);
|
||||
unit->asmlns = translatevm(unit->vmtranslator);
|
||||
free(classname);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void waitthreads(pthread_t* threads, int amount) {
|
||||
void* status;
|
||||
int code;
|
||||
for(int i = 0; i < amount; i++) {
|
||||
code = pthread_join(threads[i], &status);
|
||||
if(code) {
|
||||
eprintf("Error while joining thread %i: %s\n", i, strerror(code));
|
||||
exit(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void actonunits(COMPILEUNIT* units, void*(*fun)(void*)) {
|
||||
pthread_t mythreads[_SC_THREAD_THREADS_MAX];
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
|
||||
COMPILEUNIT* curr = units;
|
||||
|
||||
int i;
|
||||
int code;
|
||||
do {
|
||||
i = 0;
|
||||
while(curr != NULL && i < _SC_THREAD_THREADS_MAX) {
|
||||
code = pthread_create(&mythreads[i], &attr, fun, curr);
|
||||
|
||||
if(code) {
|
||||
eprintf("Error while creating thread %i: %s\n", i, strerror(code));
|
||||
exit(code);
|
||||
}
|
||||
|
||||
curr = curr->next;
|
||||
i++;
|
||||
}
|
||||
waitthreads(mythreads, i);
|
||||
} while(i == _SC_THREAD_THREADS_MAX);
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
void freeunit(COMPILEUNIT* u) {
|
||||
freeparser(u->parser);
|
||||
freelnblk(u->compiled);
|
||||
freestrlist(u->asmlns);
|
||||
freevmtranslator(u->vmtranslator);
|
||||
free(u);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef THREADS_H
|
||||
#define THREADS_H
|
||||
#include <pthread.h>
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
#include "io.h"
|
||||
#include "vm-translator.h"
|
||||
|
||||
/* threads
|
||||
* Tools for dealing with the compiling pipeline in a parallel way */
|
||||
|
||||
typedef struct unit {
|
||||
FILELIST* file;
|
||||
PARSER* parser;
|
||||
CLASS* parsed;
|
||||
COMPILER* compiler;
|
||||
STRINGLIST* asmlns;
|
||||
LINEBLOCK* compiled;
|
||||
VMTRANSLATOR* vmtranslator;
|
||||
struct unit* next;
|
||||
} COMPILEUNIT;
|
||||
|
||||
void* parseunit(void* input);
|
||||
void* compileunit(void* input);
|
||||
void* vmtranslateunit(void* input);
|
||||
void waitthreads(pthread_t* threads, int amount);
|
||||
void actonunits(COMPILEUNIT* units, void*(*fun)(void*));
|
||||
void freeunit(COMPILEUNIT* u);
|
||||
#endif
|
|
@ -0,0 +1,89 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "util.h"
|
||||
|
||||
char* heapstr(const char* str, int len) {
|
||||
int size = sizeof(char) * (len + 1);
|
||||
char* outstr = (char*)malloc(size);
|
||||
strcpy(outstr, str);
|
||||
return outstr;
|
||||
}
|
||||
|
||||
char* ezheapstr(const char* str) {
|
||||
return heapstr(str, strlen(str));
|
||||
}
|
||||
|
||||
void* copy(void* v, int size) {
|
||||
void* copy = malloc(size);
|
||||
memcpy(copy, v, size);
|
||||
return copy;
|
||||
}
|
||||
|
||||
int countplaces(int n) {
|
||||
int places = 1;
|
||||
int divisor = 1;
|
||||
if(n < 0) {
|
||||
n = -n;
|
||||
places++;
|
||||
}
|
||||
while(n / divisor >= 10) {
|
||||
places++;
|
||||
divisor *= 10;
|
||||
}
|
||||
return places;
|
||||
}
|
||||
|
||||
char* itoa(int i) {
|
||||
int size = sizeof(char)*(countplaces(i)+1);
|
||||
char* a = (char*)malloc(size);
|
||||
sprintf(a, "%i", i);
|
||||
return a;
|
||||
}
|
||||
|
||||
char* dotlabel(char* n1, char* n2) {
|
||||
int sz = (strlen(n1) + strlen(n2) + 2) * sizeof(char);
|
||||
char* result = (char*)malloc(sz);
|
||||
sprintf(result, "%s.%s", n1, n2);
|
||||
return result;
|
||||
}
|
||||
|
||||
STRINGLIST* onestr(const char* str) {
|
||||
STRINGLIST* strlist = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
strlist->content = ezheapstr(str);
|
||||
strlist->next = NULL;
|
||||
return strlist;
|
||||
}
|
||||
|
||||
STRINGLIST* initstrlist(const char** strs, int count) {
|
||||
STRINGLIST* strlist = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
STRINGLIST* curr = strlist;
|
||||
for(int i = 0; i < count-1; i++) {
|
||||
curr->content = ezheapstr(strs[i]);
|
||||
curr->next = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
curr = curr->next;
|
||||
}
|
||||
curr->content = ezheapstr(strs[count-1]);
|
||||
curr->next = NULL;
|
||||
return strlist;
|
||||
}
|
||||
|
||||
void printstrlist(STRINGLIST* strlist, FILE* stream) {
|
||||
while(strlist != NULL) {
|
||||
fprintf(stream, "%s\n", strlist->content);
|
||||
strlist = strlist->next;
|
||||
}
|
||||
}
|
||||
|
||||
void freestrlist(STRINGLIST* strlist) {
|
||||
STRINGLIST* next = strlist->next;
|
||||
free(strlist);
|
||||
if(next != NULL)
|
||||
freestrlist(next);
|
||||
}
|
||||
|
||||
bool existsinarray(STRINGARRAY* arr, const char* item) {
|
||||
for(int i = 0; i < arr->size; i++)
|
||||
if(!strcmp(arr->items[i], item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* util
|
||||
* Random utilities. */
|
||||
|
||||
// Macros
|
||||
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
|
||||
#define count(array, type) ((sizeof(array)) / (sizeof(type)))
|
||||
#define strcount(array) count(array, char*)
|
||||
#define mkstrlist(name, array) STRINGARRAY name = { .items = array, .size = strcount(array) }
|
||||
|
||||
typedef struct stringlist {
|
||||
char* content;
|
||||
struct stringlist* next;
|
||||
} STRINGLIST;
|
||||
|
||||
typedef struct {
|
||||
const char** items;
|
||||
const int size;
|
||||
} STRINGARRAY;
|
||||
|
||||
char* heapstr(const char* str, int len);
|
||||
char* ezheapstr(const char* str);
|
||||
int countplaces(int n);
|
||||
char* itoa(int i);
|
||||
void* copy(void* v, int size);
|
||||
char* dotlabel(char* n1, char* n2);
|
||||
|
||||
STRINGLIST* onestr(const char* str);
|
||||
STRINGLIST* initstrlist(const char** strs, int count);
|
||||
void printstrlist(STRINGLIST* strlist, FILE* stream);
|
||||
void freestrlist(STRINGLIST* strlist);
|
||||
|
||||
bool existsinarray(STRINGARRAY* arr, const char* item);
|
||||
#endif
|
|
@ -0,0 +1,247 @@
|
|||
#include <stdlib.h>
|
||||
#include "util.h"
|
||||
#include "parser-util.h"
|
||||
#include "parser-expressions.h"
|
||||
|
||||
const char* keywordsarr[] = { "true", "false", "null", "this" };
|
||||
const char* opsarr[] = { "+", "-", "*", "/", "&", "|", "<", ">", "=" };
|
||||
mkstrlist(keywordconstants, keywordsarr);
|
||||
mkstrlist(operators, opsarr);
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
bool isop(TOKEN* t);
|
||||
|
||||
// Parsing methods
|
||||
TERM* parsetermnullified(PARSER* p);
|
||||
TERM* parseterm(PARSER* p);
|
||||
TERM* mkterm(TERMTYPE type);
|
||||
TERM* parseint(PARSER* p);
|
||||
TERM* parsestr(PARSER* p);
|
||||
TERM* parsekeyword(PARSER* p);
|
||||
TERM* parseunaryopterm(PARSER* p);
|
||||
TERM* parseinnerexpression(PARSER* p);
|
||||
TERM* parsecalltermnullified(PARSER* p);
|
||||
TERM* parsearrayterm(PARSER* p);
|
||||
TERM* parsevarterm(PARSER* p);
|
||||
TERM* parseidentifierterm(PARSER* p);
|
||||
SUBROUTCALL* nullsubroutcall(PARSER* p, SUBROUTCALL* c);
|
||||
SUBROUTCALL* parsesubroutcallnullified(PARSER* p);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
bool isop(TOKEN* t) {
|
||||
for(int i = 0; i < operators.size; i++)
|
||||
if(!strcmp(t->token, operators.items[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parsing methods
|
||||
TERM* parsetermnullified(PARSER* p) {
|
||||
TOKENTYPE type = p->current->type;
|
||||
if(type == integer) return parseint(p);
|
||||
if(type == string) return parsestr(p);
|
||||
if(type == keyword) return parsekeyword(p);
|
||||
if(type == identifier) return parseidentifierterm(p);
|
||||
if(equals(p, "-") || equals(p, "~")) return parseunaryopterm(p);
|
||||
if(equals(p, "(")) return parseinnerexpression(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TERM* parseterm(PARSER* p) {
|
||||
TERM* t = parsetermnullified(p);
|
||||
if(t == NULL)
|
||||
unexpected(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* mkterm(TERMTYPE type) {
|
||||
TERM* t = (TERM*)malloc(sizeof(TERM));
|
||||
t->type = type;
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parseint(PARSER* p) {
|
||||
TERM* t = mkterm(intconstant);
|
||||
t->integer = atoi(p->current->token);
|
||||
next(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parsestr(PARSER* p) {
|
||||
TERM* t = mkterm(stringconstant);
|
||||
t->string = p->current->token;
|
||||
next(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parsekeyword(PARSER* p) {
|
||||
TERM* t = mkterm(keywordconstant);
|
||||
if(!existsinarray(&keywordconstants, p->current->token))
|
||||
unexpected(p);
|
||||
t->string = p->current->token;
|
||||
next(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parseunaryopterm(PARSER* p) {
|
||||
TERM* t = mkterm(unaryopterm);
|
||||
t->unaryop = p->current->token[0];
|
||||
next(p);
|
||||
t->expression = parseterm(p);
|
||||
t->expression->next = NULL;
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parseinnerexpression(PARSER* p) {
|
||||
TERM* t = mkterm(innerexpression);
|
||||
next(p);
|
||||
t->expression = parseexpression(p);
|
||||
checkcontent(p, ")");
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parsecalltermnullified(PARSER* p) {
|
||||
SUBROUTCALL* call = parsesubroutcallnullified(p);
|
||||
if(call == NULL)
|
||||
return NULL;
|
||||
TERM* t = mkterm(subroutcall);
|
||||
t->call = call;
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parsearrayterm(PARSER* p) {
|
||||
TERM* t = mkterm(arrayitem);
|
||||
t->array = (ARRAY*)malloc(sizeof(ARRAY));
|
||||
t->array->name = p->current->token;
|
||||
next(p);
|
||||
checkcontent(p, "[");
|
||||
t->array->exp = parseexpression(p);
|
||||
checkcontent(p, "]");
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parsevarterm(PARSER* p) {
|
||||
TERM* t = mkterm(varname);
|
||||
t->string = p->current->token;
|
||||
next(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parseidentifierterm(PARSER* p) {
|
||||
TERM* t = parsecalltermnullified(p);
|
||||
if(t == NULL)
|
||||
if(nextequals(p, "["))
|
||||
return parsearrayterm(p);
|
||||
else
|
||||
return parsevarterm(p);
|
||||
else
|
||||
return t;
|
||||
}
|
||||
|
||||
TERM* parseexpressionnullified(PARSER* p) {
|
||||
TERM* head = parsetermnullified(p);
|
||||
TERM* current = head;
|
||||
TERM* nextt;
|
||||
while(isop(p->current)) {
|
||||
current->op = p->current->token[0];
|
||||
next(p);
|
||||
nextt = parseterm(p);
|
||||
current->next = nextt;
|
||||
current = nextt;
|
||||
}
|
||||
if(current != NULL)
|
||||
current->next = NULL;
|
||||
return head;
|
||||
}
|
||||
|
||||
TERM* parseexpression(PARSER* p) {
|
||||
TERM* t = parseexpressionnullified(p);
|
||||
if(t == NULL)
|
||||
unexpected(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
SUBROUTCALL* nullsubroutcall(PARSER* p, SUBROUTCALL* c) {
|
||||
free(c->debug);
|
||||
free(c);
|
||||
rewindparser(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SUBROUTCALL* parsesubroutcallnullified(PARSER* p) {
|
||||
if(p->current->type != identifier)
|
||||
return NULL;
|
||||
|
||||
anchorparser(p);
|
||||
SUBROUTCALL* c = (SUBROUTCALL*)malloc(sizeof(SUBROUTCALL));
|
||||
|
||||
c->debug = getdebug(p);
|
||||
|
||||
if(nextequals(p, ".")) {
|
||||
c->parentname = p->current->token;
|
||||
next(p);
|
||||
next(p);
|
||||
}
|
||||
else
|
||||
c->parentname = NULL;
|
||||
|
||||
if(p->current->type != identifier)
|
||||
return nullsubroutcall(p, c);
|
||||
c->name = p->current->token;
|
||||
next(p);
|
||||
|
||||
if(differs(p, "("))
|
||||
return nullsubroutcall(p, c);
|
||||
next(p);
|
||||
|
||||
c->parameters = parseexpressionlist(p);
|
||||
|
||||
if(differs(p, ")"))
|
||||
return nullsubroutcall(p, c);
|
||||
next(p);
|
||||
return c;
|
||||
}
|
||||
|
||||
SUBROUTCALL* parsesubroutcall(PARSER* p) {
|
||||
SUBROUTCALL* c = (SUBROUTCALL*)malloc(sizeof(SUBROUTCALL));
|
||||
c->debug = getdebug(p);
|
||||
|
||||
if(nextequals(p, ".")) {
|
||||
c->parentname = parseidentifier(p);
|
||||
next(p);
|
||||
}
|
||||
else
|
||||
c->parentname = NULL;
|
||||
|
||||
c->name = parseidentifier(p);
|
||||
|
||||
checkcontent(p, "(");
|
||||
|
||||
c->parameters = parseexpressionlist(p);
|
||||
|
||||
checkcontent(p, ")");
|
||||
return c;
|
||||
}
|
||||
|
||||
EXPRESSIONLIST* parseexpressionlist(PARSER* p) {
|
||||
if(!strcmp(p->current->token, ")"))
|
||||
return NULL;
|
||||
EXPRESSIONLIST* head = (EXPRESSIONLIST*)malloc(sizeof(EXPRESSIONLIST));
|
||||
head->expression = parseexpressionnullified(p);
|
||||
EXPRESSIONLIST* current = head;
|
||||
EXPRESSIONLIST* nextls;
|
||||
while(!strcmp(p->current->token, ",")) {
|
||||
next(p);
|
||||
nextls = (EXPRESSIONLIST*)malloc(sizeof(EXPRESSIONLIST));
|
||||
nextls->expression = parseexpression(p);
|
||||
current->next = nextls;
|
||||
current = nextls;
|
||||
}
|
||||
if(current != NULL)
|
||||
current->next = NULL;
|
||||
return head;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef PARSER_EXPRESSIONS_H
|
||||
#define PARSER_EXPRESSIONS_H
|
||||
#include "parser.h"
|
||||
|
||||
/* parser-expressions
|
||||
* Functions for parsing expressions. */
|
||||
|
||||
TERM* parseexpressionnullified(PARSER* p);
|
||||
TERM* parseexpression(PARSER* p);
|
||||
SUBROUTCALL* parsesubroutcall(PARSER* p);
|
||||
EXPRESSIONLIST* parseexpressionlist(PARSER* p);
|
||||
#endif
|
|
@ -0,0 +1,143 @@
|
|||
#include <stdlib.h>
|
||||
#include "parser-expressions.h"
|
||||
#include "parser-util.h"
|
||||
#include "parser-statements.h"
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
STATEMENT* mkstatement(PARSER* p, STATEMENTTYPE t);
|
||||
|
||||
STATEMENT* parsestatementnullified(PARSER* p);
|
||||
STATEMENT* parselet(PARSER* p);
|
||||
CONDSTATEMENT* parsecond(PARSER* p);
|
||||
STATEMENT* parseif(PARSER* p);
|
||||
STATEMENT* parsewhile(PARSER* p);
|
||||
STATEMENT* parsedo(PARSER* p);
|
||||
STATEMENT* parsereturn(PARSER* p);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
STATEMENT* mkstatement(PARSER* p, STATEMENTTYPE t) {
|
||||
STATEMENT* s = (STATEMENT*)malloc(sizeof(STATEMENT));
|
||||
s->type = t;
|
||||
s->debug = getdebug(p);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Parsing methods
|
||||
|
||||
// Though nullified, will throw errors if the parsing fails while on-going
|
||||
STATEMENT* parsestatementnullified(PARSER* p) {
|
||||
if(equals(p, "let")) return parselet(p);
|
||||
if(equals(p, "if")) return parseif(p);
|
||||
if(equals(p, "while")) return parsewhile(p);
|
||||
if(equals(p, "do")) return parsedo(p);
|
||||
if(equals(p, "return")) return parsereturn(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
STATEMENT* parsestatements(PARSER* p) {
|
||||
STATEMENT* head = parsestatementnullified(p);
|
||||
STATEMENT* curr = head;
|
||||
STATEMENT* next;
|
||||
while(next = parsestatementnullified(p), next != NULL) {
|
||||
curr->next = next;
|
||||
curr = next;
|
||||
}
|
||||
if(curr != NULL)
|
||||
curr->next = NULL;
|
||||
return head;
|
||||
}
|
||||
|
||||
STATEMENT* parselet(PARSER* p) {
|
||||
next(p);
|
||||
STATEMENT* s = mkstatement(p, letstatement);
|
||||
LETSTATEMENT* letst = (LETSTATEMENT*)malloc(sizeof(LETSTATEMENT));
|
||||
|
||||
letst->varname = parseidentifier(p);
|
||||
|
||||
if(equals(p, "[")) {
|
||||
next(p);
|
||||
letst->arrayind = parseexpression(p);
|
||||
checkcontent(p, "]");
|
||||
}
|
||||
else
|
||||
letst->arrayind = NULL;
|
||||
|
||||
checkcontent(p, "=");
|
||||
|
||||
letst->expression = parseexpression(p);
|
||||
|
||||
checkcontent(p, ";");
|
||||
|
||||
s->type = letstatement;
|
||||
s->letstatement = letst;
|
||||
return s;
|
||||
}
|
||||
|
||||
CONDSTATEMENT* parsecond(PARSER* p) {
|
||||
checkcontent(p, "(");
|
||||
|
||||
CONDSTATEMENT* st = (CONDSTATEMENT*)malloc(sizeof(CONDSTATEMENT));
|
||||
|
||||
st->expression = parseexpression(p);
|
||||
|
||||
checkcontent(p, ")");
|
||||
checkcontent(p, "{");
|
||||
|
||||
st->statements = parsestatements(p);
|
||||
|
||||
checkcontent(p, "}");
|
||||
return st;
|
||||
}
|
||||
|
||||
STATEMENT* parseif(PARSER* p) {
|
||||
next(p);
|
||||
STATEMENT* s = mkstatement(p, ifstatement);
|
||||
IFSTATEMENT* ifst = (IFSTATEMENT*)malloc(sizeof(IFSTATEMENT));
|
||||
|
||||
ifst->base = parsecond(p);
|
||||
|
||||
if(equals(p, "else")) {
|
||||
next(p);
|
||||
checkcontent(p, "{");
|
||||
ifst->elsestatements = parsestatements(p);
|
||||
checkcontent(p, "}");
|
||||
}
|
||||
else
|
||||
ifst->elsestatements = NULL;
|
||||
|
||||
s->type = ifstatement;
|
||||
s->ifstatement = ifst;
|
||||
return s;
|
||||
}
|
||||
|
||||
STATEMENT* parsewhile(PARSER* p) {
|
||||
next(p);
|
||||
STATEMENT* s = mkstatement(p, whilestatement);
|
||||
|
||||
s->whilestatement = parsecond(p);
|
||||
return s;
|
||||
}
|
||||
|
||||
STATEMENT* parsedo(PARSER* p) {
|
||||
next(p);
|
||||
STATEMENT* s = mkstatement(p, dostatement);
|
||||
|
||||
s->dostatement = parsesubroutcall(p);
|
||||
|
||||
checkcontent(p, ";");
|
||||
return s;
|
||||
}
|
||||
|
||||
STATEMENT* parsereturn(PARSER* p) {
|
||||
next(p);
|
||||
STATEMENT* s = mkstatement(p, returnstatement);
|
||||
|
||||
s->retstatement = parseexpressionnullified(p);
|
||||
|
||||
checkcontent(p, ";");
|
||||
return s;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef PARSER_STATEMENTS_H
|
||||
#define PARSER_STATEMENTS_H
|
||||
#include "parser.h"
|
||||
|
||||
/* parser-statements
|
||||
* Function for parsing statements. */
|
||||
|
||||
STATEMENT* parsestatements(PARSER* p);
|
||||
#endif
|
|
@ -0,0 +1,253 @@
|
|||
#include <stdlib.h>
|
||||
#include "parser-util.h"
|
||||
#include "parser-structure.h"
|
||||
#include "parser-statements.h"
|
||||
|
||||
const char* classvartypesarr[] = { "static", "field" };
|
||||
const char* vartypesarr[] = { "int", "char", "boolean" };
|
||||
const char* subroutclassesarr[] = { "constructor", "function", "method" };
|
||||
mkstrlist(classvartypes, classvartypesarr);
|
||||
mkstrlist(vartypes, vartypesarr);
|
||||
mkstrlist(subroutclasses, subroutclassesarr);
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
bool isprimitive(TOKEN* tk);
|
||||
char* parsetype(PARSER* p);
|
||||
int parsepossibilities(PARSER* p, STRINGARRAY* poss);
|
||||
|
||||
// Parsing methods
|
||||
CLASS* parseclass(PARSER* p);
|
||||
CLASSVARTYPE parseclassvartype(PARSER* p);
|
||||
CLASSVARDEC* parseclassvardec(PARSER* p);
|
||||
CLASSVARDEC* parseclassvardecs(PARSER* p);
|
||||
SUBROUTCLASS parsesubroutclass(PARSER* p);
|
||||
SUBROUTDEC* parsesubroutdec(PARSER* p, CLASS* c);
|
||||
SUBROUTDEC* parsesubroutdecs(PARSER* p, CLASS* c);
|
||||
PARAMETER* parseparameter(PARSER* p);
|
||||
PARAMETER* parseparameters(PARSER* p);
|
||||
SUBROUTBODY* parsesubroutbody(PARSER* p);
|
||||
void parsevardeccommon(PARSER* p, VARDEC* v);
|
||||
VARDEC* parsevardec(PARSER* p);
|
||||
VARDEC* parsevardecs(PARSER* p);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
|
||||
// Miscelaneous
|
||||
bool isprimitive(TOKEN* tk) {
|
||||
if(tk->type == keyword)
|
||||
if(existsinarray(&vartypes, tk->token))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
char* parsetype(PARSER* p) {
|
||||
if(p->current->type != identifier && p->current->type != keyword)
|
||||
unexpected(p);
|
||||
|
||||
char* result = p->current->token;
|
||||
next(p);
|
||||
return result;
|
||||
}
|
||||
|
||||
int parsepossibilities(PARSER* p, STRINGARRAY* poss) {
|
||||
for(int i = 0; i < poss->size; i++)
|
||||
if(equals(p, poss->items[i]))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parsing methods
|
||||
CLASS* parseclass(PARSER* p) {
|
||||
checkcontent(p, "class");
|
||||
|
||||
CLASS* class = (CLASS*)malloc(sizeof(CLASS));
|
||||
|
||||
class->debug = getdebug(p);
|
||||
|
||||
class->name = parseidentifier(p);
|
||||
|
||||
checkcontent(p, "{");
|
||||
|
||||
class->vardecs = parseclassvardecs(p);
|
||||
|
||||
class->subroutdecs = parsesubroutdecs(p, class);
|
||||
|
||||
checkcontent(p, "}");
|
||||
|
||||
if(p->current != NULL)
|
||||
unexpected(p);
|
||||
|
||||
return class;
|
||||
}
|
||||
|
||||
CLASSVARTYPE parseclassvartype(PARSER* p) {
|
||||
return parsepossibilities(p, &classvartypes);
|
||||
}
|
||||
|
||||
CLASSVARDEC* parseclassvardec(PARSER* p) {
|
||||
CLASSVARTYPE classvartype = parseclassvartype(p);
|
||||
if(classvartype == -1)
|
||||
return NULL;
|
||||
next(p);
|
||||
|
||||
CLASSVARDEC* classvardec = (CLASSVARDEC*)malloc(sizeof(CLASSVARDEC));
|
||||
classvardec->type = classvartype;
|
||||
|
||||
classvardec->base = (VARDEC*)malloc(sizeof(VARDEC));
|
||||
|
||||
parsevardeccommon(p, classvardec->base);
|
||||
|
||||
return classvardec;
|
||||
}
|
||||
|
||||
CLASSVARDEC* parseclassvardecs(PARSER* p) {
|
||||
CLASSVARDEC* head = parseclassvardec(p);
|
||||
CLASSVARDEC* curr = head;
|
||||
CLASSVARDEC* nextc;
|
||||
while(nextc = parseclassvardec(p), nextc != NULL) {
|
||||
curr->next = nextc;
|
||||
curr = nextc;
|
||||
}
|
||||
if(curr != NULL)
|
||||
curr->next = NULL;
|
||||
return head;
|
||||
}
|
||||
|
||||
SUBROUTCLASS parsesubroutclass(PARSER* p) {
|
||||
return parsepossibilities(p, &subroutclasses);
|
||||
}
|
||||
|
||||
SUBROUTDEC* parsesubroutdec(PARSER* p, CLASS* c) {
|
||||
SUBROUTCLASS subroutclass = parsesubroutclass(p);
|
||||
if(subroutclass == -1)
|
||||
return NULL;
|
||||
|
||||
next(p);
|
||||
SUBROUTDEC* subroutdec = (SUBROUTDEC*)malloc(sizeof(SUBROUTDEC));
|
||||
subroutdec->subroutclass = subroutclass;
|
||||
|
||||
if(differs(p, "void"))
|
||||
subroutdec->type = parsetype(p);
|
||||
else {
|
||||
subroutdec->type = p->current->token;
|
||||
next(p);
|
||||
}
|
||||
|
||||
subroutdec->debug = getdebug(p);
|
||||
|
||||
subroutdec->name = parseidentifier(p);
|
||||
|
||||
checkcontent(p, "(");
|
||||
subroutdec->parameters = parseparameters(p);
|
||||
checkcontent(p, ")");
|
||||
|
||||
checkcontent(p, "{");
|
||||
subroutdec->body = parsesubroutbody(p);
|
||||
checkcontent(p, "}");
|
||||
|
||||
subroutdec->class = c;
|
||||
|
||||
return subroutdec;
|
||||
}
|
||||
|
||||
SUBROUTDEC* parsesubroutdecs(PARSER* p, CLASS* c) {
|
||||
SUBROUTDEC* head = parsesubroutdec(p, c);
|
||||
SUBROUTDEC* curr = head;
|
||||
SUBROUTDEC* nexts;
|
||||
while(nexts = parsesubroutdec(p, c), nexts != NULL) {
|
||||
curr->next = nexts;
|
||||
curr = nexts;
|
||||
}
|
||||
if(curr != NULL)
|
||||
curr->next = NULL;
|
||||
return head;
|
||||
}
|
||||
|
||||
PARAMETER* parseparameter(PARSER* p) {
|
||||
if(equals(p, ")"))
|
||||
return NULL;
|
||||
PARAMETER* param = (PARAMETER*)malloc(sizeof(PARAMETER));
|
||||
param->debug = getdebug(p);
|
||||
param->primitive = isprimitive(p->current);
|
||||
param->type = parsetype(p);
|
||||
param->name = parseidentifier(p);
|
||||
return param;
|
||||
}
|
||||
|
||||
PARAMETER* parseparameters(PARSER* p) {
|
||||
PARAMETER* head = parseparameter(p);
|
||||
PARAMETER* curr = head;
|
||||
PARAMETER* nextp;
|
||||
while(equals(p, ",")) {
|
||||
next(p);
|
||||
nextp = parseparameter(p);
|
||||
if(nextp == NULL)
|
||||
unexpected(p);
|
||||
curr->next = nextp;
|
||||
curr = curr->next;
|
||||
}
|
||||
if(curr != NULL)
|
||||
curr->next = NULL;
|
||||
return head;
|
||||
}
|
||||
|
||||
SUBROUTBODY* parsesubroutbody(PARSER* p) {
|
||||
SUBROUTBODY* subroutbody = (SUBROUTBODY*)malloc(sizeof(SUBROUTBODY));
|
||||
subroutbody->vardecs = parsevardecs(p);
|
||||
subroutbody->statements = parsestatements(p);
|
||||
|
||||
return subroutbody;
|
||||
}
|
||||
|
||||
void parsevardeccommon(PARSER* p, VARDEC* v) {
|
||||
v->typeclass = p->current->type;
|
||||
v->primitive = isprimitive(p->current);
|
||||
v->type = parsetype(p);
|
||||
|
||||
STRINGLIST* currstr = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
v->names = currstr;
|
||||
|
||||
v->debug = getdebug(p);
|
||||
|
||||
v->names->content = parseidentifier(p);
|
||||
|
||||
while(!strcmp(p->current->token, ",")) {
|
||||
next(p);
|
||||
STRINGLIST* nextstr = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
nextstr->content = parseidentifier(p);
|
||||
currstr->next = nextstr;
|
||||
currstr = nextstr;
|
||||
}
|
||||
currstr->next = NULL;
|
||||
|
||||
checkcontent(p, ";");
|
||||
}
|
||||
|
||||
|
||||
VARDEC* parsevardec(PARSER* p) {
|
||||
if(strcmp(p->current->token, "var"))
|
||||
return NULL;
|
||||
next(p);
|
||||
|
||||
VARDEC* vardec = (VARDEC*)malloc(sizeof(VARDEC));
|
||||
|
||||
parsevardeccommon(p, vardec);
|
||||
|
||||
return vardec;
|
||||
}
|
||||
|
||||
VARDEC* parsevardecs(PARSER* p) {
|
||||
VARDEC* head = parsevardec(p);
|
||||
VARDEC* curr = head;
|
||||
VARDEC* nextv;
|
||||
while(nextv = parsevardec(p), nextv != NULL) {
|
||||
curr->next = nextv;
|
||||
curr = nextv;
|
||||
}
|
||||
if(curr != NULL)
|
||||
curr->next = NULL;
|
||||
return head;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef PARSER_STRUCTURE_H
|
||||
#define PARSER_STRUCTURE_H
|
||||
#include "parser.h"
|
||||
|
||||
/* parser-structure
|
||||
* Function for parsing a class. */
|
||||
|
||||
CLASS* parseclass(PARSER* p);
|
||||
#endif
|
|
@ -0,0 +1,141 @@
|
|||
#include <stdlib.h>
|
||||
#include "parser-tree.h"
|
||||
|
||||
void freeexpression(TERM* e);
|
||||
void freeexpressionlist(EXPRESSIONLIST* el);
|
||||
void freestatements(STATEMENT* s);
|
||||
|
||||
void freevardec(VARDEC* v) {
|
||||
freestrlist(v->names);
|
||||
free(v->debug);
|
||||
free(v);
|
||||
}
|
||||
|
||||
void freevardecs(VARDEC* v) {
|
||||
VARDEC* next = v->next;
|
||||
freevardec(v);
|
||||
if(next != NULL)
|
||||
freevardecs(next);
|
||||
}
|
||||
|
||||
void freeparameters(PARAMETER* p) {
|
||||
free(p->debug);
|
||||
PARAMETER* next = p->next;
|
||||
free(p);
|
||||
if(next != NULL)
|
||||
freeparameters(next);
|
||||
}
|
||||
|
||||
void freearray(ARRAY* a) {
|
||||
freeexpression(a->exp);
|
||||
free(a);
|
||||
}
|
||||
|
||||
void freesubroutcall(SUBROUTCALL* call) {
|
||||
if(call->parameters != NULL)
|
||||
freeexpressionlist(call->parameters);
|
||||
free(call->debug);
|
||||
free(call);
|
||||
}
|
||||
|
||||
void freeexpression(TERM* e) {
|
||||
if(e->type == arrayitem)
|
||||
freearray(e->array);
|
||||
else if(e->type == innerexpression || e->type == unaryopterm)
|
||||
freeexpression(e->expression);
|
||||
else if(e->type == subroutcall)
|
||||
freesubroutcall(e->call);
|
||||
TERM* next = e->next;
|
||||
free(e);
|
||||
if(next != NULL)
|
||||
freeexpression(next);
|
||||
}
|
||||
|
||||
void freeexpressionlist(EXPRESSIONLIST* el) {
|
||||
freeexpression(el->expression);
|
||||
EXPRESSIONLIST* next = el->next;
|
||||
free(el);
|
||||
if(next != NULL)
|
||||
freeexpressionlist(next);
|
||||
}
|
||||
|
||||
void freelet(LETSTATEMENT* l) {
|
||||
if(l->arrayind != NULL)
|
||||
freeexpression(l->arrayind);
|
||||
freeexpression(l->expression);
|
||||
free(l);
|
||||
}
|
||||
|
||||
void freecond(CONDSTATEMENT* cond) {
|
||||
freeexpression(cond->expression);
|
||||
if(cond->statements != NULL)
|
||||
freestatements(cond->statements);
|
||||
free(cond);
|
||||
}
|
||||
|
||||
void freeif(IFSTATEMENT* st) {
|
||||
freecond(st->base);
|
||||
if(st->elsestatements != NULL)
|
||||
freestatements(st->elsestatements);
|
||||
free(st);
|
||||
}
|
||||
|
||||
void freestatements(STATEMENT* s) {
|
||||
if(s->type == letstatement)
|
||||
freelet(s->letstatement);
|
||||
else if(s->type == ifstatement)
|
||||
freeif(s->ifstatement);
|
||||
else if(s->type == whilestatement)
|
||||
freecond(s->whilestatement);
|
||||
else if(s->type == dostatement)
|
||||
freesubroutcall(s->dostatement);
|
||||
else if(s->retstatement != NULL)
|
||||
freeexpression(s->retstatement);
|
||||
|
||||
free(s->debug);
|
||||
STATEMENT* next = s->next;
|
||||
free(s);
|
||||
if(next != NULL)
|
||||
freestatements(next);
|
||||
}
|
||||
|
||||
void freesubroutbody(SUBROUTBODY* b) {
|
||||
if(b->vardecs != NULL)
|
||||
freevardecs(b->vardecs);
|
||||
if(b->statements != NULL)
|
||||
freestatements(b->statements);
|
||||
free(b);
|
||||
}
|
||||
|
||||
void freesubroutdecs(SUBROUTDEC* sr) {
|
||||
free(sr->debug);
|
||||
if(sr->parameters != NULL)
|
||||
freeparameters(sr->parameters);
|
||||
freesubroutbody(sr->body);
|
||||
SUBROUTDEC* next = sr->next;
|
||||
free(sr);
|
||||
if(next != NULL)
|
||||
freesubroutdecs(next);
|
||||
}
|
||||
|
||||
void freeclassvardecs(CLASSVARDEC* cvd) {
|
||||
freevardec(cvd->base);
|
||||
CLASSVARDEC* next = cvd->next;
|
||||
free(cvd);
|
||||
if(next != NULL)
|
||||
freeclassvardecs(next);
|
||||
}
|
||||
|
||||
void freetree(CLASS* c) {
|
||||
free(c->debug);
|
||||
|
||||
if(c->vardecs != NULL)
|
||||
freeclassvardecs(c->vardecs);
|
||||
if(c->subroutdecs != NULL)
|
||||
freesubroutdecs(c->subroutdecs);
|
||||
|
||||
CLASS* next = c->next;
|
||||
free(c);
|
||||
if(next != NULL)
|
||||
freetree(next);
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
#ifndef PARSER_TREE_H
|
||||
#define PARSER_TREE_H
|
||||
#include <stdbool.h>
|
||||
#include "tokenizer.h"
|
||||
#include "util.h"
|
||||
|
||||
/* parser-tree
|
||||
* Type definitions for the parsing tree. */
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
struct classvardec;
|
||||
struct parameter;
|
||||
struct subroutbody;
|
||||
struct subroutdec;
|
||||
struct vardec;
|
||||
struct letstatement;
|
||||
struct ifstatement;
|
||||
struct condstatement;
|
||||
struct subroutcall;
|
||||
struct term;
|
||||
struct expressionlist;
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
// Misc
|
||||
typedef struct {
|
||||
char* file;
|
||||
int definedat;
|
||||
} DEBUGINFO;
|
||||
|
||||
// Program structure
|
||||
|
||||
typedef struct class {
|
||||
char* name;
|
||||
struct classvardec* vardecs;
|
||||
struct subroutdec* subroutdecs;
|
||||
DEBUGINFO* debug;
|
||||
struct class* next;
|
||||
} CLASS;
|
||||
|
||||
typedef enum {
|
||||
stat, field
|
||||
} CLASSVARTYPE;
|
||||
|
||||
typedef struct classvardec {
|
||||
CLASSVARTYPE type;
|
||||
struct vardec* base;
|
||||
struct classvardec* next;
|
||||
} CLASSVARDEC;
|
||||
|
||||
typedef enum {
|
||||
constructor, function, method
|
||||
} SUBROUTCLASS;
|
||||
|
||||
typedef struct subroutdec {
|
||||
SUBROUTCLASS subroutclass;
|
||||
CLASS* class;
|
||||
char* type;
|
||||
char* name;
|
||||
struct parameter* parameters;
|
||||
struct subroutbody* body;
|
||||
DEBUGINFO* debug;
|
||||
struct subroutdec* next;
|
||||
} SUBROUTDEC;
|
||||
|
||||
typedef struct parameter {
|
||||
char* type;
|
||||
char* name;
|
||||
bool primitive;
|
||||
DEBUGINFO* debug;
|
||||
struct parameter* next;
|
||||
} PARAMETER;
|
||||
|
||||
typedef struct subroutbody {
|
||||
struct vardec* vardecs;
|
||||
struct statement* statements;
|
||||
} SUBROUTBODY;
|
||||
|
||||
typedef struct vardec {
|
||||
char* type;
|
||||
bool primitive;
|
||||
TOKENTYPE typeclass;
|
||||
STRINGLIST* names;
|
||||
DEBUGINFO* debug;
|
||||
struct vardec* next;
|
||||
} VARDEC;
|
||||
|
||||
// Statements
|
||||
typedef enum {
|
||||
ifstatement, whilestatement, letstatement, dostatement, returnstatement
|
||||
} STATEMENTTYPE;
|
||||
|
||||
typedef struct statement {
|
||||
STATEMENTTYPE type;
|
||||
union {
|
||||
struct letstatement* letstatement;
|
||||
struct ifstatement* ifstatement;
|
||||
struct condstatement* whilestatement;
|
||||
struct subroutcall* dostatement;
|
||||
struct term* retstatement;
|
||||
};
|
||||
DEBUGINFO* debug;
|
||||
struct statement* next;
|
||||
} STATEMENT;
|
||||
|
||||
typedef struct letstatement {
|
||||
char* varname;
|
||||
struct term* arrayind;
|
||||
struct term* expression;
|
||||
} LETSTATEMENT;
|
||||
|
||||
typedef struct ifstatement {
|
||||
struct condstatement* base;
|
||||
struct statement* elsestatements;
|
||||
} IFSTATEMENT;
|
||||
|
||||
typedef struct condstatement {
|
||||
struct term* expression;
|
||||
struct statement* statements;
|
||||
} CONDSTATEMENT;
|
||||
|
||||
// Expressions
|
||||
|
||||
typedef enum {
|
||||
varname, intconstant, stringconstant, keywordconstant, arrayitem, subroutcall, innerexpression, unaryopterm
|
||||
} TERMTYPE;
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
struct term* exp;
|
||||
} ARRAY;
|
||||
|
||||
typedef struct term {
|
||||
TERMTYPE type;
|
||||
union {
|
||||
char* string;
|
||||
int integer;
|
||||
struct subroutcall* call;
|
||||
struct term* expression;
|
||||
ARRAY* array;
|
||||
};
|
||||
char op;
|
||||
char unaryop;
|
||||
struct term* next;
|
||||
} TERM;
|
||||
|
||||
typedef struct subroutcall {
|
||||
char* parentname;
|
||||
char* name;
|
||||
struct expressionlist* parameters;
|
||||
DEBUGINFO* debug;
|
||||
} SUBROUTCALL;
|
||||
|
||||
typedef struct expressionlist {
|
||||
TERM* expression;
|
||||
struct expressionlist* next;
|
||||
} EXPRESSIONLIST;
|
||||
|
||||
|
||||
void freetree(CLASS* c);
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
#include <stdlib.h>
|
||||
#include "parser-util.h"
|
||||
#include "util.h"
|
||||
|
||||
const char* tokentypesarr[] = { "keyword", "identifier", "symbol",
|
||||
"integerConstant", "stringConstant" };
|
||||
mkstrlist(tokentypes, tokentypesarr);
|
||||
|
||||
void unexpected(PARSER* p) {
|
||||
eprintf("Unexpected token '%s' (of type %s); line %i, file '%s'\n",
|
||||
p->current->token, tokentypes.items[p->current->type],
|
||||
p->current->definedat, p->file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void checktype(PARSER* p, TOKENTYPE type) {
|
||||
if(p->current->type != type) {
|
||||
eprintf("Unexpected %s; file '%s', line %i\n",
|
||||
tokentypes.items[p->current->type], p->file,
|
||||
p->current->definedat);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void checkcontent(PARSER* p, const char* content) {
|
||||
if(differs(p, content))
|
||||
unexpected(p);
|
||||
next(p);
|
||||
}
|
||||
|
||||
char* parseidentifier(PARSER* p) {
|
||||
checktype(p, identifier);
|
||||
char* result = p->current->token;
|
||||
next(p);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEBUGINFO* getdebug(PARSER* p) {
|
||||
DEBUGINFO* d = (DEBUGINFO*)malloc(sizeof(DEBUGINFO));
|
||||
d->file = p->file;
|
||||
d->definedat = p->current->definedat;
|
||||
return d;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef PARSER_INTERNAL_H
|
||||
#define PARSER_INTERNAL_H
|
||||
#include <string.h>
|
||||
#include "parser.h"
|
||||
|
||||
/* parser-util
|
||||
* Random utilities used in the parser module. */
|
||||
|
||||
#define next(parser) parser->current = p->current->next
|
||||
#define anchorparser(parser) p->checkpoint = p->current
|
||||
#define rewindparser(parser) p->current = p->checkpoint
|
||||
#define differs(parser, str) strcmp(parser->current->token, str)
|
||||
#define nextdiffers(parser, str) strcmp(parser->current->next->token, str)
|
||||
#define equals(parser, str) !differs(parser, str)
|
||||
#define nextequals(parser, str) !nextdiffers(parser, str)
|
||||
|
||||
void unexpected(PARSER* p);
|
||||
char* parseidentifier(PARSER* p);
|
||||
void checkcontent(PARSER* p, const char* content);
|
||||
DEBUGINFO* getdebug(PARSER* p);
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "parser.h"
|
||||
#include "parser-structure.h"
|
||||
|
||||
PARSER* mkparser(TOKEN* t, char* file) {
|
||||
PARSER* parser = (PARSER*)malloc(sizeof(PARSER));
|
||||
parser->tokens = t;
|
||||
parser->current = t;
|
||||
parser->file = file;
|
||||
return parser;
|
||||
}
|
||||
|
||||
CLASS* parse(PARSER* p) {
|
||||
return parseclass(p);
|
||||
}
|
||||
|
||||
void freeparser(PARSER* p) {
|
||||
freetokens(p->tokens);
|
||||
free(p);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
#include "tokenizer.h"
|
||||
#include "parser-tree.h"
|
||||
|
||||
/* parser
|
||||
* This is the file that should be included in other modules
|
||||
* that want to parse a file. */
|
||||
|
||||
typedef struct {
|
||||
TOKEN* tokens;
|
||||
TOKEN* current;
|
||||
TOKEN* checkpoint;
|
||||
char* file;
|
||||
} PARSER;
|
||||
|
||||
PARSER* mkparser(TOKEN* t, char* file);
|
||||
CLASS* parse(PARSER* p);
|
||||
void freeparser(PARSER* p);
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef TOKENIZER_TABLES_H
|
||||
#define TOKENIZER_TABLES_H
|
||||
#include "util.h"
|
||||
|
||||
|
||||
const char* keywordsraw[] = {
|
||||
"class", "constructor", "function", "method", "field", "static",
|
||||
"var", "int", "char", "boolean", "void", "true", "false", "null",
|
||||
"this", "let", "do", "if", "else", "while", "return"
|
||||
};
|
||||
mkstrlist(keywords, keywordsraw);
|
||||
|
||||
const char* symbolsraw[] = {
|
||||
"{", "}", "(", ")", "[", "]", ".", ",", ";", "+", "-", "*", "/",
|
||||
"&", "|", "<", ">", "=", "~"
|
||||
};
|
||||
mkstrlist(symbols, symbolsraw);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,257 @@
|
|||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "tokenizer.h"
|
||||
#include "tokenizer-tables.h"
|
||||
|
||||
// Data types
|
||||
typedef enum {
|
||||
common, charsymbol, space
|
||||
} CHARTYPE;
|
||||
|
||||
typedef struct {
|
||||
char* str;
|
||||
int size;
|
||||
int count;
|
||||
} STRING;
|
||||
|
||||
// String manipulation
|
||||
STRING* mkstring(int size);
|
||||
void append(STRING* s, char c);
|
||||
void freestr(STRING* str);
|
||||
|
||||
// Token manipulation;
|
||||
TOKEN* appendtokenraw(TOKEN* curitem, STRING* token, int definedat, TOKENTYPE type);
|
||||
TOKEN* appendtoken(TOKEN* curitem, STRING* token, char* file, int definedat);
|
||||
#define mktoken() (TOKEN*)malloc(sizeof(TOKEN))
|
||||
|
||||
// Char types
|
||||
CHARTYPE getchartype(unsigned char c);
|
||||
bool iskeyword(STRING* tk);
|
||||
bool issymbol(STRING* tk);
|
||||
bool isint(char* str);
|
||||
bool isintcons(STRING* tk);
|
||||
bool isidentifier(STRING* tk);
|
||||
TOKENTYPE gettokentype(STRING* tk, char* file, int definedat);
|
||||
|
||||
// Stream handling
|
||||
void skipln(FILE* input);
|
||||
void skipmultiln(FILE* input, int* lnscount);
|
||||
bool handlecomment(FILE* input, int* lnscount);
|
||||
void readstr(FILE* input, STRING* tmp, int definedat);
|
||||
|
||||
// String manipulation
|
||||
STRING* mkstring(int size) {
|
||||
STRING* str = (STRING*)malloc(sizeof(STRING));
|
||||
str->size = sizeof(char) * size; // initial size
|
||||
str->str = (char*)malloc(str->size);
|
||||
str->count = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
void append(STRING* s, char c) {
|
||||
int targsize = sizeof(char) * (s->count + 1);
|
||||
if(s->size <= targsize) {
|
||||
s->size = targsize * 2;
|
||||
s->str = (char*)realloc(s->str, s->size);
|
||||
}
|
||||
|
||||
s->str[s->count] = c;
|
||||
s->count++;
|
||||
}
|
||||
|
||||
void freestr(STRING* str) {
|
||||
free(str->str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
// Token manipulation;
|
||||
TOKEN* appendtokenraw(TOKEN* curitem, STRING* token, int definedat, TOKENTYPE type) {
|
||||
curitem->token = (char*)malloc(sizeof(char)*token->count);
|
||||
strcpy(curitem->token, token->str);
|
||||
curitem->definedat = definedat;
|
||||
curitem->type = type;
|
||||
TOKEN* nextitem = mktoken();
|
||||
curitem->next = nextitem;
|
||||
token->count = 0;
|
||||
return nextitem;
|
||||
}
|
||||
|
||||
void freetokens(TOKEN* t) {
|
||||
free(t->token);
|
||||
TOKEN* next = t->next;
|
||||
free(t);
|
||||
if(next != NULL)
|
||||
freetokens(next);
|
||||
}
|
||||
|
||||
TOKEN* appendtoken(TOKEN* curitem, STRING* token, char* file, int definedat) {
|
||||
append(token, '\0');
|
||||
return appendtokenraw(curitem, token, definedat, gettokentype(token, file, definedat));
|
||||
}
|
||||
|
||||
// Char types
|
||||
CHARTYPE getchartype(unsigned char c) {
|
||||
if(isspace(c)) return space;
|
||||
if(isalnum(c) || c == '_' || c == '"') return common;
|
||||
return charsymbol;
|
||||
}
|
||||
|
||||
bool iskeyword(STRING* tk) {
|
||||
return existsinarray(&keywords, tk->str);
|
||||
}
|
||||
|
||||
bool issymbol(STRING* tk) {
|
||||
if(tk->count != 2)
|
||||
return false;
|
||||
return existsinarray(&symbols, tk->str);
|
||||
}
|
||||
|
||||
bool isint(char* str) {
|
||||
int i = 0;
|
||||
while(str[i] != '\0') {
|
||||
if(!isdigit(str[i]))
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isintcons(STRING* tk) {
|
||||
if(!isint(tk->str))
|
||||
return false;
|
||||
int val = atoi(tk->str);
|
||||
return val >= 0 && val <= 32767;
|
||||
}
|
||||
|
||||
bool isidentifier(STRING* tk) {
|
||||
if(isdigit(tk->str[0]))
|
||||
return false;
|
||||
|
||||
int count = tk->count - 1;
|
||||
for(int i = 0; i < count; i++)
|
||||
if(!isalnum(tk->str[i]) && tk->str[i] != '_')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
TOKENTYPE gettokentype(STRING* tk, char* file, int definedat) {
|
||||
if(iskeyword(tk)) return keyword;
|
||||
if(issymbol(tk)) return symbol;
|
||||
if(isintcons(tk)) return integer;
|
||||
if(isidentifier(tk)) return identifier;
|
||||
eprintf("Unexpected token '%s'; file '%s', line %i\n", tk->str, file, definedat);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Stream handling
|
||||
void skipln(FILE* input) {
|
||||
unsigned char c;
|
||||
while(c = fgetc(input), c != '\0')
|
||||
if(c == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
void skipmultiln(FILE* input, int* lnscount) {
|
||||
unsigned char c;
|
||||
while(c = fgetc(input), c != '\0')
|
||||
if(c == '\n')
|
||||
(*lnscount)++;
|
||||
else if(c == '*')
|
||||
if(fgetc(input) == '/')
|
||||
break;
|
||||
}
|
||||
|
||||
bool handlecomment(FILE* input, int* lnscount) {
|
||||
unsigned char nextc = fgetc(input);
|
||||
if(nextc == '/') {
|
||||
skipln(input);
|
||||
(*lnscount)++;
|
||||
return true;
|
||||
}
|
||||
else if(nextc == '*') {
|
||||
unsigned char furtherc = fgetc(input);
|
||||
if(furtherc == '*') {
|
||||
skipmultiln(input, lnscount);
|
||||
return true;
|
||||
}
|
||||
ungetc(furtherc, input);
|
||||
}
|
||||
ungetc(nextc, input);
|
||||
return false;
|
||||
}
|
||||
|
||||
void readstr(FILE* input, STRING* tmp, int definedat) {
|
||||
unsigned char c;
|
||||
while(c = fgetc(input), c != '\0') {
|
||||
if(c == '\n') {
|
||||
eprintf("Unexpected end of line; line %i", definedat);
|
||||
exit(1);
|
||||
}
|
||||
if(c == '"')
|
||||
break;
|
||||
append(tmp, c);
|
||||
}
|
||||
append(tmp, '\0');
|
||||
}
|
||||
|
||||
TOKEN* tokenize(char* file) {
|
||||
TOKEN* head = mktoken();
|
||||
TOKEN* lastitem = head;
|
||||
TOKEN* curitem = head;
|
||||
|
||||
STRING* tmptoken = mkstring(200);
|
||||
CHARTYPE lasttype = space;
|
||||
CHARTYPE curtype;
|
||||
|
||||
int lnscount = 1;
|
||||
FILE* input = fopen(file, "r");
|
||||
|
||||
unsigned char c;
|
||||
while(!feof(input)) {
|
||||
c = fgetc(input);
|
||||
if(c == '\n')
|
||||
lnscount++;
|
||||
else if(c == '/' && handlecomment(input, &lnscount))
|
||||
continue;
|
||||
else if(c == '"') {
|
||||
if(lasttype != space)
|
||||
curitem = appendtoken(curitem, tmptoken, file, lnscount);
|
||||
readstr(input, tmptoken, lnscount);
|
||||
lastitem = curitem;
|
||||
curitem = appendtokenraw(curitem, tmptoken, lnscount, string);
|
||||
lasttype = space;
|
||||
continue;
|
||||
}
|
||||
|
||||
curtype = getchartype(c);
|
||||
|
||||
if(curtype == common) {
|
||||
if(lasttype == charsymbol) {
|
||||
lastitem = curitem;
|
||||
curitem = appendtoken(curitem, tmptoken, file, lnscount);
|
||||
}
|
||||
append(tmptoken, c);
|
||||
} else {
|
||||
if(lasttype != space){
|
||||
lastitem = curitem;
|
||||
curitem = appendtoken(curitem, tmptoken, file, lnscount);
|
||||
}
|
||||
if(curtype == charsymbol)
|
||||
append(tmptoken, c);
|
||||
}
|
||||
lasttype = curtype;
|
||||
}
|
||||
|
||||
if(curitem == head) {
|
||||
eprintf("File '%s' is empty\n", file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lastitem->next = NULL;
|
||||
free(curitem);
|
||||
freestr(tmptoken);
|
||||
fclose(input);
|
||||
return head;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef TOKENIZER_H
|
||||
#define TOKENIZER_H
|
||||
#include <stdio.h>
|
||||
|
||||
/* tokenizer
|
||||
* Simple tool that splits a stream into many tokens. */
|
||||
|
||||
typedef enum {
|
||||
keyword, identifier, symbol, integer, string
|
||||
} TOKENTYPE;
|
||||
|
||||
typedef struct token {
|
||||
char* token;
|
||||
TOKENTYPE type;
|
||||
int definedat;
|
||||
struct token* next;
|
||||
} TOKEN;
|
||||
|
||||
TOKEN* tokenize(char* filename);
|
||||
void freetokens(TOKEN* t);
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
#include <stdlib.h>
|
||||
#include "vm-lines.h"
|
||||
|
||||
LINE* mkline(int size) {
|
||||
LINE* ln = (LINE*)malloc(sizeof(LINE));
|
||||
ln->tokens = (char**)malloc(sizeof(char*)*size);
|
||||
ln->count = 0;
|
||||
return ln;
|
||||
}
|
||||
|
||||
void addtoken(LINE* ln, char* token) {
|
||||
ln->tokens[ln->count] = token;
|
||||
ln->count++;
|
||||
}
|
||||
|
||||
void println(LINE* ln, FILE* stream) {
|
||||
for(int i = 0; i < ln->count; i++) {
|
||||
fprintf(stream, "%s", ln->tokens[i]);
|
||||
if(i + 1 < ln->count)
|
||||
fprintf(stream, " ");
|
||||
}
|
||||
fprintf(stream, "\n");
|
||||
}
|
||||
|
||||
void printlns(LINE* lns, FILE* stream) {
|
||||
while(lns != NULL) {
|
||||
println(lns, stream);
|
||||
lns = lns->next;
|
||||
}
|
||||
}
|
||||
|
||||
void freeln(LINE* ln) {
|
||||
for(int i = 0; i < ln->count; i++)
|
||||
free(ln->tokens[i]);
|
||||
free(ln->tokens);
|
||||
free(ln);
|
||||
}
|
||||
|
||||
void freelns(LINE* lns) {
|
||||
LINE* next = lns->next;
|
||||
freeln(lns);
|
||||
if(next != NULL)
|
||||
freelns(next);
|
||||
}
|
||||
|
||||
void freelnblk(LINEBLOCK* blk) {
|
||||
freelns(blk->head);
|
||||
free(blk);
|
||||
}
|
||||
|
||||
LINEBLOCK* mklnblk(LINE* start) {
|
||||
LINEBLOCK* blk = (LINEBLOCK*)malloc(sizeof(LINEBLOCK));
|
||||
blk->head = start;
|
||||
blk->tail = start;
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* mergelnblks(LINEBLOCK* head, LINEBLOCK* tail) {
|
||||
if(head == NULL)
|
||||
return tail;
|
||||
head->tail->next = tail->head;
|
||||
head->tail = tail->tail;
|
||||
free(tail);
|
||||
return head;
|
||||
}
|
||||
|
||||
void appendln(LINEBLOCK* lnblk, LINE* ln) {
|
||||
lnblk->tail->next = ln;
|
||||
lnblk->tail = ln;
|
||||
}
|
||||
|
||||
void appendlnbefore(LINEBLOCK* lnblk, LINE* ln) {
|
||||
ln->next = lnblk->head;
|
||||
lnblk->head = ln;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef VM_LINES_H
|
||||
#define VM_LINES_H
|
||||
#include <stdio.h>
|
||||
|
||||
/* vm-lines
|
||||
* Unified standard for the compiler's output and vm-translator's input.
|
||||
* It is also used by vm-parser when reading .vm files. */
|
||||
|
||||
// Data types
|
||||
typedef struct line {
|
||||
char** tokens;
|
||||
int count;
|
||||
struct line* next;
|
||||
} LINE;
|
||||
|
||||
typedef struct {
|
||||
LINE* head;
|
||||
LINE* tail;
|
||||
} LINEBLOCK;
|
||||
|
||||
// Line manipulation
|
||||
LINE* mkline(int size);
|
||||
void addtoken(LINE* ln, char* token);
|
||||
|
||||
// Line printing
|
||||
void println(LINE* ln, FILE* stream);
|
||||
void printlns(LINE* lns, FILE* stream);
|
||||
|
||||
// Line freeing
|
||||
void freeln(LINE* ln);
|
||||
void freelns(LINE* lns);
|
||||
void freelnblk(LINEBLOCK* blk);
|
||||
|
||||
// Line block manipulation
|
||||
LINEBLOCK* mklnblk(LINE* start);
|
||||
LINEBLOCK* mergelnblks(LINEBLOCK* head, LINEBLOCK* tail);
|
||||
void appendln(LINEBLOCK* lnblk, LINE* ln);
|
||||
void appendlnbefore(LINEBLOCK* lnblk, LINE* ln);
|
||||
#endif
|
|
@ -0,0 +1,313 @@
|
|||
#ifndef VM_TEMPLATES
|
||||
#define VM_TEMPLATES
|
||||
#else
|
||||
#error vm-templates may only be included once
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define mktemplate(name, array) TEMPLATE name = { .items = array, .count = strcount(array) };
|
||||
|
||||
typedef struct {
|
||||
char** items;
|
||||
int count;
|
||||
} TEMPLATE;
|
||||
|
||||
char* tpushlns[] = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"A=D+A",
|
||||
"D=M",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
};
|
||||
mktemplate(tpush, tpushlns);
|
||||
|
||||
char* tpushconslns[] = {
|
||||
"",
|
||||
"",
|
||||
"D=A",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
};
|
||||
mktemplate(tpushcons, tpushconslns);
|
||||
|
||||
char* tpushstatlns[] = {
|
||||
"",
|
||||
"",
|
||||
"D=M",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
};
|
||||
mktemplate(tpushstat, tpushstatlns);
|
||||
|
||||
|
||||
mktemplate(tpushtemp, tpushstatlns);
|
||||
mktemplate(tpushpointer, tpushstatlns);
|
||||
|
||||
char* tpoplns[] = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"D=D+A",
|
||||
"@R13",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"AM=M-1",
|
||||
"D=M",
|
||||
"@R13",
|
||||
"A=M",
|
||||
"M=D"
|
||||
};
|
||||
mktemplate(tpop, tpoplns);
|
||||
|
||||
char* tpopstatlns[] = {
|
||||
"",
|
||||
"@SP",
|
||||
"AM=M-1",
|
||||
"D=M",
|
||||
"",
|
||||
""
|
||||
};
|
||||
mktemplate(tpopstat, tpopstatlns);
|
||||
|
||||
mktemplate(tpoptemp, tpopstatlns);
|
||||
|
||||
mktemplate(tpoppointer, tpopstatlns);
|
||||
|
||||
char* tarithlns[] = {
|
||||
"",
|
||||
"@SP",
|
||||
"AM=M-1",
|
||||
"D=M",
|
||||
"A=A-1",
|
||||
""
|
||||
};
|
||||
mktemplate(tarith, tarithlns);
|
||||
|
||||
char* tneglns[] = {
|
||||
"",
|
||||
"@SP",
|
||||
"A=M-1",
|
||||
"M=-M",
|
||||
};
|
||||
mktemplate(tneg, tneglns);
|
||||
|
||||
char* tnotlns[] = {
|
||||
"",
|
||||
"@SP",
|
||||
"A=M-1",
|
||||
"M=!M",
|
||||
};
|
||||
mktemplate(tnot, tnotlns);
|
||||
|
||||
char* tcomplns[] = {
|
||||
"",
|
||||
"@SP",
|
||||
"AM=M-1",
|
||||
"D=M",
|
||||
"A=A-1",
|
||||
"D=D-M",
|
||||
"M=-1",
|
||||
"",
|
||||
"",
|
||||
"@SP",
|
||||
"A=M-1",
|
||||
"M=0",
|
||||
""
|
||||
};
|
||||
mktemplate(tcomp, tcomplns);
|
||||
|
||||
char* tlabellns[] = {
|
||||
"",
|
||||
""
|
||||
};
|
||||
mktemplate(tlabel, tlabellns);
|
||||
|
||||
char* tgotolns[] = {
|
||||
"",
|
||||
"",
|
||||
"0;JMP"
|
||||
};
|
||||
mktemplate(tgoto, tgotolns);
|
||||
|
||||
char* tifgotolns[] = {
|
||||
"",
|
||||
"@SP",
|
||||
"AM=M-1",
|
||||
"D=M",
|
||||
"",
|
||||
"D;JNE"
|
||||
};
|
||||
mktemplate(tifgoto, tifgotolns);
|
||||
|
||||
char* tcallstartlns[] = {
|
||||
"",
|
||||
"",
|
||||
"D=A",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
};
|
||||
mktemplate(tcallstart, tcallstartlns);
|
||||
|
||||
char* tcallpushlns[] = {
|
||||
"",
|
||||
"D=M",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
};
|
||||
mktemplate(tcallpush, tcallpushlns);
|
||||
|
||||
char* tcallsetarglns[] = {
|
||||
"@SP",
|
||||
"D=M",
|
||||
"@LCL",
|
||||
"M=D",
|
||||
"",
|
||||
"D=D-A",
|
||||
"@ARG",
|
||||
"M=D"
|
||||
};
|
||||
mktemplate(tcallsetarg, tcallsetarglns);
|
||||
|
||||
char* tcalljmplns[] = {
|
||||
"",
|
||||
"0;JMP",
|
||||
""
|
||||
};
|
||||
mktemplate(tcalljmp, tcalljmplns);
|
||||
|
||||
char* tframevarslns[] = {
|
||||
"@LCL",
|
||||
"@ARG",
|
||||
"@THIS",
|
||||
"@THAT"
|
||||
};
|
||||
mktemplate(tframevars, tframevarslns);
|
||||
|
||||
char* tfunctionlns[] = {
|
||||
"",
|
||||
""
|
||||
};
|
||||
mktemplate(tfunction, tfunctionlns);
|
||||
|
||||
char* tfunctionpushlns[] = {
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=0",
|
||||
"@SP",
|
||||
"M=M+1"
|
||||
};
|
||||
mktemplate(tfunctionpush, tfunctionpushlns);
|
||||
|
||||
char* tstartreturnlns[] = {
|
||||
"",
|
||||
"@LCL",
|
||||
"D=M",
|
||||
"@5",
|
||||
"A=D-A",
|
||||
"D=M",
|
||||
"@R13",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"A=M-1",
|
||||
"D=M",
|
||||
"@ARG",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@ARG",
|
||||
"D=M+1",
|
||||
"@SP",
|
||||
"M=D"
|
||||
};
|
||||
mktemplate(tstartreturn, tstartreturnlns);
|
||||
|
||||
char* tretpoplns[] = {
|
||||
"@LCL",
|
||||
"AM=M-1",
|
||||
"D=M",
|
||||
"",
|
||||
"M=D",
|
||||
};
|
||||
mktemplate(tretpop, tretpoplns);
|
||||
|
||||
char* tendreturnlns[] = {
|
||||
"@R13",
|
||||
"A=M",
|
||||
"0;JMP"
|
||||
};
|
||||
mktemplate(tendreturn, tendreturnlns);
|
||||
|
||||
char* tbootstraplns[] = {
|
||||
"@256",
|
||||
"D=A",
|
||||
"@SP",
|
||||
"M=D",
|
||||
"@BOOTSTRAP$ret",
|
||||
"D=A",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
"@LCL",
|
||||
"D=M",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
"@ARG",
|
||||
"D=M",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
"@THIS",
|
||||
"D=M",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
"@THAT",
|
||||
"D=M",
|
||||
"@SP",
|
||||
"A=M",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"M=M+1",
|
||||
"@5",
|
||||
"D=A",
|
||||
"@SP",
|
||||
"D=M-D",
|
||||
"@ARG",
|
||||
"M=D",
|
||||
"@SP",
|
||||
"D=M",
|
||||
"@LCL",
|
||||
"M=D",
|
||||
"@Sys.init",
|
||||
"0;JMP",
|
||||
"(BOOTSTRAP$ret)"
|
||||
};
|
||||
mktemplate(tbootstrap, tbootstraplns);
|
|
@ -0,0 +1,471 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "vm-templates.h"
|
||||
#include "vm-translator.h"
|
||||
#include "util.h"
|
||||
#define eq(translator, index, str) !strcmp(translator->currln->tokens[index], str)
|
||||
|
||||
typedef struct {
|
||||
STRINGLIST* head;
|
||||
STRINGLIST* tail;
|
||||
} ASMBLK;
|
||||
|
||||
STRINGLIST* asmln(char* content) {
|
||||
STRINGLIST* ln = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
ln->content = content;
|
||||
return ln;
|
||||
}
|
||||
|
||||
void togarbage(VMTRANSLATOR* t, char* str) {
|
||||
STRINGLIST* garb = asmln(str);
|
||||
garb->next = t->garbage;
|
||||
t->garbage = garb;
|
||||
}
|
||||
|
||||
char* atraw(VMTRANSLATOR* t, char* n, int len) {
|
||||
int sz = sizeof(char) * (len + 2);
|
||||
char* atstr = (char*)malloc(sz);
|
||||
sprintf(atstr, "@%s", n);
|
||||
togarbage(t, atstr);
|
||||
return atstr;
|
||||
}
|
||||
|
||||
char* at(VMTRANSLATOR* t, char* n) {
|
||||
return atraw(t, n, strlen(n));
|
||||
}
|
||||
|
||||
char* atn(VMTRANSLATOR* t, int n) {
|
||||
char* str = itoa(n);
|
||||
togarbage(t, str);
|
||||
return at(t, str);
|
||||
}
|
||||
|
||||
char* mkstr(VMTRANSLATOR* t, char* str) {
|
||||
char* heapstr = ezheapstr(str);
|
||||
togarbage(t, heapstr);
|
||||
return heapstr;
|
||||
}
|
||||
|
||||
char* mkpointerind(VMTRANSLATOR* t) {
|
||||
if(t->currln->tokens[2][0] == 0)
|
||||
return mkstr(t, "@THIS");
|
||||
else
|
||||
return mkstr(t, "@THAT");
|
||||
}
|
||||
|
||||
char* mktempind(VMTRANSLATOR* t) {
|
||||
int index = atoi(t->currln->tokens[2]);
|
||||
char* actualind = itoa(index+5);
|
||||
togarbage(t, actualind);
|
||||
return at(t, actualind);
|
||||
}
|
||||
|
||||
char* dotat(VMTRANSLATOR* t, char* name, char* n) {
|
||||
int sz = sizeof(char) * (strlen(name) + strlen(n) + 3);
|
||||
char* atstr = (char*)malloc(sz);
|
||||
sprintf(atstr, "@%s.%s", name, n);
|
||||
togarbage(t, atstr);
|
||||
return atstr;
|
||||
}
|
||||
|
||||
char* comment(VMTRANSLATOR* t) {
|
||||
int sz = (4 + strlen(t->currln->tokens[0])) * sizeof(char);
|
||||
for(int i = 1; i < t->currln->count; i++)
|
||||
sz += (1 + strlen(t->currln->tokens[i])) * sizeof(char);
|
||||
|
||||
char* com = (char*)malloc(sz);
|
||||
if(t->currln->count == 1)
|
||||
sprintf(com, "// %s", t->currln->tokens[0]);
|
||||
else if(t->currln->count == 2)
|
||||
sprintf(com, "// %s %s", t->currln->tokens[0],
|
||||
t->currln->tokens[1]);
|
||||
else if(t->currln->count == 3)
|
||||
sprintf(com, "// %s %s %s", t->currln->tokens[0],
|
||||
t->currln->tokens[1],
|
||||
t->currln->tokens[2]);
|
||||
|
||||
togarbage(t, com);
|
||||
return com;
|
||||
}
|
||||
|
||||
char* switchsegment(VMTRANSLATOR* t) {
|
||||
if(eq(t, 1, "local"))
|
||||
return mkstr(t, "@LCL");
|
||||
if(eq(t, 1, "argument"))
|
||||
return mkstr(t, "@ARG");
|
||||
if(eq(t, 1, "this"))
|
||||
return mkstr(t, "@THIS");
|
||||
return mkstr(t, "@THAT");
|
||||
}
|
||||
|
||||
char* mkspeciallab(VMTRANSLATOR* t, char* suffix, int* ind, int* len) {
|
||||
(*ind)++;
|
||||
*len = t->classnamelen + countplaces(*ind) + strlen(suffix) + 2;
|
||||
int sz = ((*len)+1) * sizeof(char);
|
||||
char* lab = (char*)malloc(sz);
|
||||
sprintf(lab, "%s$%s.%i", t->classname, suffix, (*ind));
|
||||
togarbage(t, lab);
|
||||
return lab;
|
||||
}
|
||||
|
||||
char* mkcmplab(VMTRANSLATOR* t, int* len) {
|
||||
return mkspeciallab(t, "cmp", &(t->cmpind), len);
|
||||
}
|
||||
|
||||
char* mkretlab(VMTRANSLATOR* t, int* len) {
|
||||
return mkspeciallab(t, "ret", &(t->retind), len);
|
||||
}
|
||||
|
||||
char* enclosingparenthesis(VMTRANSLATOR* t, char* content, int len) {
|
||||
int sz = sizeof(char) * (len + 3);
|
||||
char* str = (char*)malloc(sz);
|
||||
sprintf(str, "(%s)", content);
|
||||
togarbage(t, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
char* mklab(VMTRANSLATOR* t) {
|
||||
int sz = (t->classnamelen + strlen(t->currln->tokens[1]) + 4) * sizeof(char);
|
||||
char* lab = (char*)malloc(sz);
|
||||
sprintf(lab, "(%s$%s)", t->classname, t->currln->tokens[1]);
|
||||
togarbage(t, lab);
|
||||
return lab;
|
||||
}
|
||||
|
||||
char* mkgotolab(VMTRANSLATOR* t) {
|
||||
int sz = sizeof(char) * (t->classnamelen + strlen(t->currln->tokens[1]) + 3);
|
||||
char* lab = (char*)malloc(sz);
|
||||
sprintf(lab, "@%s$%s", t->classname, t->currln->tokens[1]);
|
||||
togarbage(t, lab);
|
||||
return lab;
|
||||
}
|
||||
|
||||
ASMBLK* copytemplate(TEMPLATE* t) {
|
||||
ASMBLK* blk = (ASMBLK*)malloc(sizeof(ASMBLK));
|
||||
blk->head = asmln(t->items[0]);
|
||||
STRINGLIST* curr = blk->head;
|
||||
for(int i = 1; i < t->count; i++) {
|
||||
STRINGLIST* newln = asmln(t->items[i]);
|
||||
curr->next = newln;
|
||||
curr = newln;
|
||||
}
|
||||
curr->next = NULL;
|
||||
blk->tail = curr;
|
||||
return blk;
|
||||
}
|
||||
|
||||
ASMBLK* mkasmlns(VMTRANSLATOR* t, TEMPLATE* tp) {
|
||||
// instruction comment
|
||||
tp->items[0] = comment(t);
|
||||
|
||||
return copytemplate(tp);
|
||||
}
|
||||
|
||||
void mergeasmblks(ASMBLK* a, ASMBLK* b) {
|
||||
a->tail->next = b->head;
|
||||
a->tail = b->tail;
|
||||
free(b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* START STACK MANIPULATION */
|
||||
|
||||
ASMBLK* translatepushconst(VMTRANSLATOR* t) {
|
||||
// @i
|
||||
tpushcons.items[1] = at(t, t->currln->tokens[2]);
|
||||
|
||||
return mkasmlns(t, &tpushcons);
|
||||
}
|
||||
|
||||
ASMBLK* translatepushstatic(VMTRANSLATOR* t) {
|
||||
// @classname.i
|
||||
tpushstat.items[1] = dotat(t, t->classname, t->currln->tokens[2]);
|
||||
|
||||
return mkasmlns(t, &tpushstat);
|
||||
}
|
||||
|
||||
ASMBLK* translatepushpointer(VMTRANSLATOR* t) {
|
||||
// @THIS/@THAT
|
||||
tpushpointer.items[1] = mkpointerind(t);
|
||||
|
||||
return mkasmlns(t, &tpushpointer);
|
||||
}
|
||||
|
||||
ASMBLK* translatepushtemp(VMTRANSLATOR* t) {
|
||||
// @5+i
|
||||
tpushtemp.items[1] = mktempind(t);
|
||||
|
||||
return mkasmlns(t, &tpushtemp);
|
||||
}
|
||||
|
||||
void pushpopcommon(VMTRANSLATOR* t, TEMPLATE* tp) {
|
||||
// @segment
|
||||
tp->items[1] = switchsegment(t);
|
||||
|
||||
// D=M
|
||||
tp->items[2] = mkstr(t, "D=M");
|
||||
|
||||
// @i
|
||||
tp->items[3] = at(t, t->currln->tokens[2]);
|
||||
}
|
||||
|
||||
ASMBLK* translatepushgeneric(VMTRANSLATOR* t) {
|
||||
pushpopcommon(t, &tpush);
|
||||
|
||||
return mkasmlns(t, &tpush);
|
||||
}
|
||||
|
||||
ASMBLK* translatepush(VMTRANSLATOR* t) {
|
||||
if(eq(t, 1, "constant"))
|
||||
return translatepushconst(t);
|
||||
if(eq(t, 1, "static"))
|
||||
return translatepushstatic(t);
|
||||
if(eq(t, 1, "pointer"))
|
||||
return translatepushpointer(t);
|
||||
if(eq(t, 1, "temp"))
|
||||
return translatepushtemp(t);
|
||||
return translatepushgeneric(t);
|
||||
}
|
||||
|
||||
ASMBLK* translatepopstatic(VMTRANSLATOR* t) {
|
||||
// @classname.i
|
||||
tpopstat.items[tpopstat.count-2] = dotat(t, t->classname, t->currln->tokens[2]);
|
||||
|
||||
// M=D
|
||||
tpopstat.items[tpopstat.count-1] = mkstr(t, "M=D");
|
||||
|
||||
return mkasmlns(t, &tpopstat);
|
||||
}
|
||||
|
||||
ASMBLK* translatepoppointer(VMTRANSLATOR* t) {
|
||||
// @THIS/@THAT
|
||||
tpoppointer.items[tpoppointer.count-2] = mkpointerind(t);
|
||||
|
||||
// M=D
|
||||
tpoppointer.items[tpoppointer.count-1] = mkstr(t, "M=D");
|
||||
|
||||
return mkasmlns(t, &tpoppointer);
|
||||
}
|
||||
|
||||
ASMBLK* translatepoptemp(VMTRANSLATOR* t) {
|
||||
// @5+i
|
||||
tpoptemp.items[tpoptemp.count-2] = mktempind(t);
|
||||
|
||||
tpoptemp.items[tpoptemp.count-1] = mkstr(t, "M=D");
|
||||
|
||||
return mkasmlns(t, &tpoptemp);
|
||||
}
|
||||
|
||||
ASMBLK* translatepopgeneric(VMTRANSLATOR* t) {
|
||||
pushpopcommon(t, &tpop);
|
||||
|
||||
return mkasmlns(t, &tpop);
|
||||
}
|
||||
|
||||
ASMBLK* translatepop(VMTRANSLATOR* t) {
|
||||
if(eq(t, 1, "static"))
|
||||
return translatepopstatic(t);
|
||||
if(eq(t, 1, "pointer"))
|
||||
return translatepoppointer(t);
|
||||
if(eq(t, 1, "temp"))
|
||||
return translatepoptemp(t);
|
||||
return translatepopgeneric(t);
|
||||
}
|
||||
|
||||
/* END STACK MANIPULATION */
|
||||
|
||||
|
||||
/* BEGIN OPERATIONS */
|
||||
|
||||
ASMBLK* translatearith(VMTRANSLATOR* t, char* op) {
|
||||
tarith.items[tarith.count-1] = mkstr(t, op);
|
||||
|
||||
return mkasmlns(t, &tarith);
|
||||
}
|
||||
|
||||
ASMBLK* translatecomp(VMTRANSLATOR* t, char* op) {
|
||||
int labellen;
|
||||
char* label = mkcmplab(t, &labellen);
|
||||
|
||||
// @label
|
||||
tcomp.items[tcomp.count-6] = atraw(t, label, labellen);
|
||||
|
||||
// D;J(op)
|
||||
int sz = sizeof(char) * 6;
|
||||
char* trueop = (char*)malloc(sz);
|
||||
sprintf(trueop, "D;J%s", op);
|
||||
tcomp.items[tcomp.count-5] = trueop;
|
||||
togarbage(t, trueop);
|
||||
|
||||
// (label)
|
||||
tcomp.items[tcomp.count-1] = enclosingparenthesis(t, label, labellen);
|
||||
|
||||
return mkasmlns(t, &tcomp);
|
||||
}
|
||||
|
||||
/* END OPERATIONS */
|
||||
|
||||
ASMBLK* translatelabel(VMTRANSLATOR* t) {
|
||||
// (classname$label)
|
||||
tlabel.items[tlabel.count-1] = mklab(t);
|
||||
|
||||
return mkasmlns(t, &tlabel);
|
||||
}
|
||||
|
||||
ASMBLK* translategoto(VMTRANSLATOR* t) {
|
||||
// @label
|
||||
tgoto.items[tgoto.count-2] = mkgotolab(t);
|
||||
|
||||
return mkasmlns(t, &tgoto);
|
||||
}
|
||||
|
||||
ASMBLK* translateifgoto(VMTRANSLATOR* t) {
|
||||
// @label
|
||||
tifgoto.items[tifgoto.count-2] = mkgotolab(t);
|
||||
|
||||
return mkasmlns(t, &tifgoto);
|
||||
}
|
||||
|
||||
ASMBLK* translatereturn(VMTRANSLATOR* t) {
|
||||
ASMBLK* blk = mkasmlns(t, &tstartreturn);
|
||||
|
||||
for(int i = tframevars.count-1; i >= 0; i--) {
|
||||
tretpop.items[tretpop.count-2] = tframevars.items[i];
|
||||
mergeasmblks(blk, copytemplate(&tretpop));
|
||||
}
|
||||
|
||||
mergeasmblks(blk, copytemplate(&tendreturn));
|
||||
return blk;
|
||||
}
|
||||
|
||||
ASMBLK* translatefunction(VMTRANSLATOR* t) {
|
||||
t->retind = 0;
|
||||
t->cmpind = 0;
|
||||
|
||||
// (funcname)
|
||||
tfunction.items[1] = mklab(t);
|
||||
ASMBLK* blk = mkasmlns(t, &tfunction);
|
||||
|
||||
// repeat nVars times:
|
||||
int nlocals = atoi(t->currln->tokens[2]);
|
||||
|
||||
for(int i = 0; i < nlocals; i++)
|
||||
mergeasmblks(blk, copytemplate(&tfunctionpush));
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
ASMBLK* pushframe(VMTRANSLATOR* t, char* retlab, int retlablen, int* framesize) {
|
||||
tcallstart.items[1] = atraw(t, retlab, retlablen);
|
||||
|
||||
ASMBLK* blk = mkasmlns(t, &tcallstart);
|
||||
|
||||
for(int i = 0; i < tframevars.count; i++) {
|
||||
tcallpush.items[0] = tframevars.items[i];
|
||||
mergeasmblks(blk, copytemplate((&tcallpush)));
|
||||
}
|
||||
|
||||
*framesize = tframevars.count + 1;
|
||||
return blk;
|
||||
}
|
||||
|
||||
ASMBLK* translatecall(VMTRANSLATOR* t) {
|
||||
// return label
|
||||
int retlablen;
|
||||
char* retlab = mkretlab(t, &retlablen);
|
||||
|
||||
// push frame
|
||||
int framesize;
|
||||
ASMBLK* blk = pushframe(t, retlab, retlablen, &framesize);
|
||||
|
||||
// setting ARG
|
||||
int nargs = atoi(t->currln->tokens[2]);
|
||||
tcallsetarg.items[tcallsetarg.count-4] = atn(t, nargs + framesize);
|
||||
mergeasmblks(blk, copytemplate(&tcallsetarg));
|
||||
|
||||
// jmp
|
||||
tcalljmp.items[tcalljmp.count-3] = at(t, t->currln->tokens[1]);
|
||||
tcalljmp.items[tcalljmp.count-1] = enclosingparenthesis(t, retlab, retlablen);
|
||||
mergeasmblks(blk, copytemplate(&tcalljmp));
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
ASMBLK* translateln(VMTRANSLATOR* t) {
|
||||
if(eq(t, 0, "push"))
|
||||
return translatepush(t);
|
||||
if(eq(t, 0, "pop"))
|
||||
return translatepop(t);
|
||||
|
||||
if(eq(t, 0, "add"))
|
||||
return translatearith(t, "M=D+M");
|
||||
if(eq(t, 0, "sub"))
|
||||
return translatearith(t, "M=M-D");
|
||||
if(eq(t, 0, "and"))
|
||||
return translatearith(t, "M=D&M");
|
||||
if(eq(t, 0, "or"))
|
||||
return translatearith(t, "M=D|M");
|
||||
|
||||
if(eq(t, 0, "neg"))
|
||||
return mkasmlns(t, &tneg);
|
||||
if(eq(t, 0, "not"))
|
||||
return mkasmlns(t, &tnot);
|
||||
|
||||
if(eq(t, 0, "eq"))
|
||||
return translatecomp(t, "EQ");
|
||||
if(eq(t, 0, "gt"))
|
||||
return translatecomp(t, "LT");
|
||||
if(eq(t, 0, "lt"))
|
||||
return translatecomp(t, "GT");
|
||||
|
||||
if(eq(t, 0, "label"))
|
||||
return translatelabel(t);
|
||||
if(eq(t, 0, "goto"))
|
||||
return translategoto(t);
|
||||
if(eq(t, 0, "if-goto"))
|
||||
return translateifgoto(t);
|
||||
|
||||
if(eq(t, 0, "return"))
|
||||
return translatereturn(t);
|
||||
if(eq(t, 0, "function"))
|
||||
return translatefunction(t);
|
||||
return translatecall(t);
|
||||
}
|
||||
|
||||
STRINGLIST* translatevm(VMTRANSLATOR* t) {
|
||||
ASMBLK* blk = copytemplate(&tbootstrap);
|
||||
while(t->currln != NULL) {
|
||||
mergeasmblks(blk, translateln(t));
|
||||
t->currln = t->currln->next;
|
||||
}
|
||||
STRINGLIST* output = blk->head;
|
||||
free(blk);
|
||||
return output;
|
||||
}
|
||||
|
||||
VMTRANSLATOR* mkvmtranslator(char* classname, LINEBLOCK* vmlines) {
|
||||
VMTRANSLATOR* transl = (VMTRANSLATOR*)malloc(sizeof(VMTRANSLATOR));
|
||||
transl->currln = vmlines->head;
|
||||
transl->start = vmlines->head;
|
||||
transl->garbage = NULL;
|
||||
transl->retind = 0;
|
||||
transl->cmpind = 0;
|
||||
transl->classname = classname;
|
||||
transl->classnamelen = strlen(classname);
|
||||
return transl;
|
||||
}
|
||||
|
||||
void freegarbage(STRINGLIST* garbage) {
|
||||
if(garbage != NULL) {
|
||||
free(garbage->content);
|
||||
STRINGLIST* next = garbage->next;
|
||||
free(garbage);
|
||||
freegarbage(next);
|
||||
}
|
||||
}
|
||||
|
||||
void freevmtranslator(VMTRANSLATOR* t) {
|
||||
freegarbage(t->garbage);
|
||||
free(t);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef VM_TRANSLATOR
|
||||
#define VM_TRANSLATOR
|
||||
#include "vm-lines.h"
|
||||
|
||||
typedef struct {
|
||||
char* classname;
|
||||
int classnamelen;
|
||||
LINE* start;
|
||||
LINE* currln;
|
||||
STRINGLIST* garbage;
|
||||
int cmpind;
|
||||
int retind;
|
||||
} VMTRANSLATOR;
|
||||
|
||||
|
||||
STRINGLIST* translatevm(VMTRANSLATOR* t);
|
||||
VMTRANSLATOR* mkvmtranslator(char* classname, LINEBLOCK* vmlines);
|
||||
void freevmtranslator(VMTRANSLATOR* t);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue