Merge pull request #20 from ValKmjolnir/develop
🎇 refactor ast structure & build process
This commit is contained in:
commit
8ca7ee1d90
|
@ -4,7 +4,7 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 16 * * *"
|
- cron: "0 16 * * *"
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master,develop ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
@ -16,9 +16,9 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: make
|
- name: make
|
||||||
run: |
|
run: |
|
||||||
make
|
make -j4
|
||||||
cd module
|
cd module
|
||||||
make all
|
make all -j4
|
||||||
cd ..
|
cd ..
|
||||||
make test
|
make test
|
||||||
tar -czf nasal-mac-nightly.tgz .
|
tar -czf nasal-mac-nightly.tgz .
|
||||||
|
@ -42,9 +42,9 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: make
|
- name: make
|
||||||
run: |
|
run: |
|
||||||
make
|
make -j4
|
||||||
cd module
|
cd module
|
||||||
make all
|
make all -j4
|
||||||
cd ..
|
cd ..
|
||||||
make test
|
make test
|
||||||
touch nasal-linux-x86_64-nightly.tgz
|
touch nasal-linux-x86_64-nightly.tgz
|
||||||
|
|
|
@ -4,37 +4,44 @@ project(nasal VERSION 10.1)
|
||||||
|
|
||||||
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
|
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
|
||||||
|
|
||||||
# -std=c++14 -Wshadow -Wall
|
# -std=c++17 -Wshadow -Wall
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
|
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
|
||||||
|
|
||||||
|
add_compile_options(-fPIC)
|
||||||
|
|
||||||
# generate release executables
|
# generate release executables
|
||||||
set(CMAKE_BUILD_TYPE "Release")
|
set(CMAKE_BUILD_TYPE "Release")
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
|
|
||||||
|
|
||||||
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
|
# build nasal used object
|
||||||
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR})
|
set(NASAL_OBJECT_SOURCE_FILE
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_builtin.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_dbg.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_err.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_import.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_lexer.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp)
|
||||||
|
add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE})
|
||||||
|
target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
|
# build nasal
|
||||||
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR})
|
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/main.cpp)
|
||||||
|
target_link_libraries(nasal nasal-object)
|
||||||
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
|
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||||
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR})
|
|
||||||
|
|
||||||
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
|
|
||||||
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR})
|
|
||||||
|
|
||||||
add_executable(nasal main.cpp)
|
|
||||||
|
|
||||||
if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
|
||||||
message("Ignore linking dl lib")
|
|
||||||
else()
|
|
||||||
target_link_libraries(nasal dl)
|
target_link_libraries(nasal dl)
|
||||||
endif()
|
endif()
|
||||||
|
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||||
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR})
|
|
||||||
|
|
||||||
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET nasal POST_BUILD
|
TARGET nasal POST_BUILD
|
||||||
|
@ -42,4 +49,28 @@ if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||||
${CMAKE_SOURCE_DIR}/build/nasal
|
${CMAKE_SOURCE_DIR}/build/nasal
|
||||||
${CMAKE_SOURCE_DIR}/nasal
|
${CMAKE_SOURCE_DIR}/nasal
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# build module
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
|
||||||
|
|
||||||
|
set(MODULE_USED_OBJECT_SOURCE_FILE
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
|
||||||
|
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
|
||||||
|
|
||||||
|
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
|
||||||
|
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(fib module-used-object)
|
||||||
|
|
||||||
|
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
|
||||||
|
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(key module-used-object)
|
||||||
|
|
||||||
|
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
|
||||||
|
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(mat module-used-object)
|
||||||
|
|
||||||
|
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
|
||||||
|
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(nasock module-used-object)
|
352
LICENSE
352
LICENSE
|
@ -1,21 +1,339 @@
|
||||||
MIT License
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
Copyright (c) 2019-2023 ValKmjolnir
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Preamble
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The licenses for most software are designed to take away your
|
||||||
copies or substantial portions of the Software.
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
When we speak of free software, we are referring to freedom, not
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
have the freedom to distribute copies of free software (and charge for
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
this service if you wish), that you receive source code or can get it
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
if you want it, that you can change the software or use pieces of it
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
in new free programs; and that you know you can do these things.
|
||||||
SOFTWARE.
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. 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.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
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
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the 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 a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE 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.
|
||||||
|
|
||||||
|
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
|
||||||
|
convey 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 2 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, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision 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, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This 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.
|
21
README.md
21
README.md
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
[](./LICENSE)
|
[](./LICENSE)
|
||||||
|
|
||||||
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
|
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
|
||||||
|
|
||||||
|
@ -32,11 +32,11 @@ __Contact us if having great ideas to share!__
|
||||||
is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/).
|
is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/).
|
||||||
The designer is [Andy Ross](https://github.com/andyross).
|
The designer is [Andy Ross](https://github.com/andyross).
|
||||||
|
|
||||||
This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++14`)
|
This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`)
|
||||||
without reusing the code in [Andy Ross's nasal interpreter](https://github.com/andyross/nasal).
|
without reusing the code in [Andy Ross's nasal interpreter](https://github.com/andyross/nasal).
|
||||||
But we really appreciate that Andy created this amazing programming language.
|
But we really appreciate that Andy created this amazing programming language.
|
||||||
|
|
||||||
This project uses __MIT license__ (2021/5/4).
|
This project uses __MIT license__ (2019/7~2021/5/4~2023/5), __GPL v2 license__ (since 2023/6).
|
||||||
|
|
||||||
### __Why writing this nasal interpreter?__
|
### __Why writing this nasal interpreter?__
|
||||||
|
|
||||||
|
@ -59,7 +59,6 @@ the interpreter a useful tool in your own projects.
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
@ -68,19 +67,17 @@ Better download the latest update source of the interpreter and build it! It's q
|
||||||
|
|
||||||
__CAUTION__: If want to use the release zip/tar.gz file to build the interpreter, please read the [__Release Notes__](./doc/dev.md#release-notes) to make sure this release file has no fatal bugs.
|
__CAUTION__: If want to use the release zip/tar.gz file to build the interpreter, please read the [__Release Notes__](./doc/dev.md#release-notes) to make sure this release file has no fatal bugs.
|
||||||
|
|
||||||
Use g++(`MinGW-w64`) or MSVC(`Visual Studio`) on `Windows` .
|
### __`Windows (MinGW-w64)`__
|
||||||
|
|
||||||
Use g++/clang++ on `Linux/macOS/Unix` platform (we suggest `clang`).
|
|
||||||
|
|
||||||
On `Windows (MinGW-w64)`:
|
|
||||||
|
|
||||||
Make sure your MinGW thread model is `posix thread model`, otherwise it may not have the thread library.
|
Make sure your MinGW thread model is `posix thread model`, otherwise it may not have the thread library.
|
||||||
|
|
||||||
> mingw32-make nasal.exe
|
> mingw32-make nasal.exe
|
||||||
|
|
||||||
You could create project in `Visual Studio` by this way: [__Click__](./doc/vs.md).
|
### __`Windows (Visual Studio)`__
|
||||||
|
|
||||||
On `Linux/macOS/Unix`:
|
This project gives a [__CMakelists.txt__](./CMakeLists.txt) for you to create project in `Visual Studio`.
|
||||||
|
|
||||||
|
### __`Linux/macOS/Unix`__
|
||||||
|
|
||||||
> make nasal
|
> make nasal
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
[](../LICENSE)
|
[](../LICENSE)
|
||||||
|
|
||||||
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
|
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ __如果有好的意见或建议,欢迎联系我们!__
|
||||||
|
|
||||||
该解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++14`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码,我们依然非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言。
|
该解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++14`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码,我们依然非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言。
|
||||||
|
|
||||||
该项目已使用 __MIT__ 协议开源 (2021/5/4)。
|
该项目使用 __MIT__ 协议开源 (2019/7~2021/5/4~2023/5),从 2023/6 开始使用 __GPL v2__ 协议。
|
||||||
|
|
||||||
### __我们为什么想要重新写一个nasal解释器?__
|
### __我们为什么想要重新写一个nasal解释器?__
|
||||||
|
|
||||||
|
@ -49,29 +49,26 @@ __如果有好的意见或建议,欢迎联系我们!__
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
我们推荐你下载最新更新的代码包来直接编译,这个项目非常小巧,没有使用任何第三方库,因此编译起来非常轻松,
|
我们推荐你下载最新代码包编译,这个项目非常小巧,没有使用任何第三方库,因此编译起来非常轻松,
|
||||||
你只需要这两样东西: C++ 编译器以及make程序。
|
你只需要这两样东西: C++ 编译器以及make程序。
|
||||||
|
|
||||||
__注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构建这个解释器,在下载之前请阅读[__发行日志__](../doc/dev_zh.md#发行日志)以保证这个发行版的文件中不包含非常严重的bug。
|
__注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构建这个解释器,在下载之前请阅读[__发行日志__](../doc/dev_zh.md#发行日志)以保证这个发行版的文件中不包含非常严重的bug。
|
||||||
|
|
||||||
`Windows` 用户通过 g++(`MinGW-w64`) 或者使用 MSVC(`Visual Studio`) 来进行编译。
|
### __`Windows` 平台(`MinGW-w64`)__
|
||||||
|
|
||||||
`Linux/macOS/Unix` 用户可以使用 g++ 或者 clang++ 来进行编译 (建议您使用 `clang`)。
|
|
||||||
|
|
||||||
`Windows` 平台(`MinGW-w64`):
|
|
||||||
|
|
||||||
一定要确保您的 MinGW thread model 是 `posix thread model`, 否则可能存在没有 thread 库的问题。
|
一定要确保您的 MinGW thread model 是 `posix thread model`, 否则可能存在没有 thread 库的问题。
|
||||||
|
|
||||||
> mingw32-make nasal.exe
|
> mingw32-make nasal.exe
|
||||||
|
|
||||||
你也可以在`Visual Studio`中用这种方式来创建项目:[__点击跳转__](../doc/vs.md)。
|
### __`Windows` 平台(`Vistual Studio`)__
|
||||||
|
|
||||||
`Linux/macOS/Unix` 平台:
|
项目提供了 [__CMakeLists.txt__](../CMakeLists.txt) 用于在`Visual Studio`中用这种方式来创建项目。
|
||||||
|
|
||||||
|
### __`Linux/macOS/Unix` 平台__
|
||||||
|
|
||||||
> make nasal
|
> make nasal
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v10.0-blue?style=flat-square&logo=github"></img></a>
|
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v10.0-blue?style=flat-square&logo=github"></img></a>
|
||||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square&logo=github"><br/></img></a>
|
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github"><br/></img></a>
|
||||||
</div>
|
</div>
|
||||||
<h2> Introduction | 介绍</h2>
|
<h2> Introduction | 介绍</h2>
|
||||||
<text>
|
<text>
|
||||||
|
@ -51,12 +51,11 @@
|
||||||
<p>
|
<p>
|
||||||
The interpreter is totally rewritten by <a href="https://github.com/ValKmjolnir">ValKmjolnir</a> using C++(-std=c++11) without reusing the code in Andy Ross's nasal interpreter.
|
The interpreter is totally rewritten by <a href="https://github.com/ValKmjolnir">ValKmjolnir</a> using C++(-std=c++11) without reusing the code in Andy Ross's nasal interpreter.
|
||||||
But we really appreciate that Andy created this amazing programming language and his interpreter project.
|
But we really appreciate that Andy created this amazing programming language and his interpreter project.
|
||||||
Now this project uses <a href="/license">MIT license</a> (2021/5/4). Edit it if you want, use this project to learn or create more interesting things(But don't forget me XD).<br/>
|
Now this project uses <a href="/license">GPL-2.0 license</a> (2021/5/4).<br/>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
这个解释器是由<a href="https://github.com/ValKmjolnir">ValKmjolnir</a>用C++11编写的,完全没有复用Andy Ross版解释器的代码。但是我们仍然非常感谢Andy为我们带来了这么一款有趣的编程语言。
|
这个解释器是由<a href="https://github.com/ValKmjolnir">ValKmjolnir</a>用C++11编写的,完全没有复用Andy Ross版解释器的代码。但是我们仍然非常感谢Andy为我们带来了这么一款有趣的编程语言。
|
||||||
现在这个项目使用<a href="/license">MIT协议</a>开源 (2021/5/4)。
|
现在这个项目使用<a href="/license">GPL-2.0协议</a>开源。
|
||||||
如果你有需求,可以自行修改,用这个解释器项目来学习或者创造更有意思的东西吧!(不过别忘了在项目协议中留下原作者名字哦)
|
|
||||||
</p>
|
</p>
|
||||||
</text>
|
</text>
|
||||||
<h2> Benchmark | 执行效率</h2>
|
<h2> Benchmark | 执行效率</h2>
|
||||||
|
|
43
doc/vs.md
43
doc/vs.md
|
@ -1,43 +0,0 @@
|
||||||
# Create VS project | 创建 VS 工程
|
|
||||||
|
|
||||||
## First | 首先
|
|
||||||
|
|
||||||
We give a __CMakeLists.txt__ for you to create new VS project from it.
|
|
||||||
|
|
||||||
我们为你提供了 __CMakeLists.txt__ 用于创建新的 VS 工程。
|
|
||||||
|
|
||||||
If you are using this way, you will __not need__ to continue reading.
|
|
||||||
|
|
||||||
如果你使用的是这种方式,下面的内容你就 __不需要__ 再读了。
|
|
||||||
|
|
||||||
Creating VS project from a CMake file is the __simplest__ way.
|
|
||||||
|
|
||||||
从 CMake 文件创建 VS 工程是 __最简单__ 的方式。
|
|
||||||
|
|
||||||
## How to Create VS project
|
|
||||||
|
|
||||||
Make sure you are using VS 2022. You may not get the dynamic libraries by using this way to compile.
|
|
||||||
|
|
||||||
1. Get code from this repo using `git`.
|
|
||||||
|
|
||||||
2. In Visual Studio, click `File`->`New`->`Project From Existing Code...`.
|
|
||||||
|
|
||||||
3. Select `Visual C++`->`Next`->choose `project file location`->write the `project name` at ease->`Finish`.
|
|
||||||
|
|
||||||
4. Remove cpp files in `module` to avoid compilation problems.(they should be compiled to dynamic libraries)
|
|
||||||
|
|
||||||
5. Click `Source Files` in `Search Solution Explorer` at left, right click `main.cpp`, compile.
|
|
||||||
|
|
||||||
## 如何创建VS工程
|
|
||||||
|
|
||||||
确保你使用的是 VS 2022。这个方式可能无法生成一些动态库。
|
|
||||||
|
|
||||||
1. 用 `git` 从这个仓库获取代码。
|
|
||||||
|
|
||||||
2. 在VS的界面,点击文件(F)->新建(N)->从现有代码创建项目(E)。
|
|
||||||
|
|
||||||
3. 选择创建 `Visual C++` 项目->下一步->项目文件位置选择你下载的代码存放的文件夹->填项目名称,随便写->完成。
|
|
||||||
|
|
||||||
4. 从项目中去掉 `module` 里的cpp文件,以防止编译错误。(那些本应该编译到动态库)
|
|
||||||
|
|
||||||
5. 点开左侧解决方案资源管理器中的 `Source Files`,右键点击 `main.cpp`,编译。
|
|
176
main.cpp
176
main.cpp
|
@ -1,176 +0,0 @@
|
||||||
#include "nasal.h"
|
|
||||||
#include "nasal_err.h"
|
|
||||||
#include "nasal_lexer.h"
|
|
||||||
#include "nasal_ast.h"
|
|
||||||
#include "nasal_parse.h"
|
|
||||||
#include "nasal_import.h"
|
|
||||||
#include "nasal_opt.h"
|
|
||||||
#include "nasal_gc.h"
|
|
||||||
#include "nasal_builtin.h"
|
|
||||||
#include "nasal_codegen.h"
|
|
||||||
#include "nasal_vm.h"
|
|
||||||
#include "nasal_dbg.h"
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
const u32 VM_AST =0x01;
|
|
||||||
const u32 VM_CODE =0x02;
|
|
||||||
const u32 VM_TIME =0x04;
|
|
||||||
const u32 VM_EXEC =0x08;
|
|
||||||
const u32 VM_DETAIL=0x10;
|
|
||||||
const u32 VM_DEBUG =0x20;
|
|
||||||
|
|
||||||
std::ostream& help(std::ostream& out) {
|
|
||||||
out
|
|
||||||
<<" ,--#-,\n"
|
|
||||||
<<"<3 / \\____\\ <3\n"
|
|
||||||
<<" |_|__A_|\n"
|
|
||||||
#ifdef _WIN32
|
|
||||||
<<"use command <chcp 65001> to use unicode.\n"
|
|
||||||
#endif
|
|
||||||
<<"\nnasal <option>\n"
|
|
||||||
<<"option:\n"
|
|
||||||
<<" -h, --help | get help.\n"
|
|
||||||
<<"\nnasal [option] <file> [argv]\n"
|
|
||||||
<<"option:\n"
|
|
||||||
<<" -a, --ast | view abstract syntax tree.\n"
|
|
||||||
<<" -c, --code | view bytecode.\n"
|
|
||||||
<<" -e, --exec | execute.\n"
|
|
||||||
<<" -t, --time | show execute time.\n"
|
|
||||||
<<" -d, --detail | get detail info.\n"
|
|
||||||
<<" -dbg, --debug | debug mode.\n"
|
|
||||||
<<"file:\n"
|
|
||||||
<<" <filename> | execute file.\n"
|
|
||||||
<<"argv:\n"
|
|
||||||
<<" <args> | cmd arguments used in program.\n";
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& logo(std::ostream& out) {
|
|
||||||
out
|
|
||||||
<<" __ _\n"
|
|
||||||
<<" /\\ \\ \\__ _ ___ __ _| |\n"
|
|
||||||
<<" / \\/ / _` / __|/ _` | |\n"
|
|
||||||
<<" / /\\ / (_| \\__ \\ (_| | |\n"
|
|
||||||
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
|
||||||
<<"ver : "<<__nasver<<" ("<<__DATE__<<" "<<__TIME__<<")\n"
|
|
||||||
<<"std : c++ "<<__cplusplus<<"\n"
|
|
||||||
<<"repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
|
||||||
<<"repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
|
||||||
<<"wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
|
||||||
<<"input <nasal -h> to get help .\n";
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]]
|
|
||||||
void err() {
|
|
||||||
std::cerr
|
|
||||||
<<"invalid argument(s).\n"
|
|
||||||
<<"use <nasal -h> to get help.\n";
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute(
|
|
||||||
const string& file,
|
|
||||||
const std::vector<string>& argv,
|
|
||||||
const u32 cmd
|
|
||||||
) {
|
|
||||||
using clk=std::chrono::high_resolution_clock;
|
|
||||||
const auto den=clk::duration::period::den;
|
|
||||||
|
|
||||||
error err;
|
|
||||||
lexer lex(err);
|
|
||||||
parse parse(err);
|
|
||||||
linker ld(err);
|
|
||||||
codegen gen(err);
|
|
||||||
vm ctx;
|
|
||||||
|
|
||||||
// lexer scans file to get tokens
|
|
||||||
lex.scan(file).chkerr();
|
|
||||||
|
|
||||||
// parser gets lexer's token list to compile
|
|
||||||
parse.compile(lex).chkerr();
|
|
||||||
|
|
||||||
// linker gets parser's ast and load import files to this ast
|
|
||||||
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
|
|
||||||
|
|
||||||
// optimizer does simple optimization on ast
|
|
||||||
optimize(parse.tree());
|
|
||||||
if (cmd&VM_AST) {
|
|
||||||
parse.tree().dump();
|
|
||||||
}
|
|
||||||
|
|
||||||
// code generator gets parser's ast and import file list to generate code
|
|
||||||
gen.compile(parse, ld).chkerr();
|
|
||||||
if (cmd&VM_CODE) {
|
|
||||||
gen.print();
|
|
||||||
}
|
|
||||||
|
|
||||||
// run
|
|
||||||
auto start=clk::now();
|
|
||||||
if (cmd&VM_DEBUG) {
|
|
||||||
dbg(err).run(gen, ld, argv);
|
|
||||||
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
|
||||||
ctx.run(gen, ld, argv, cmd&VM_DETAIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get running time
|
|
||||||
if (cmd&VM_TIME) {
|
|
||||||
f64 tm=(clk::now()-start).count()*1.0/den;
|
|
||||||
std::clog<<"process exited after "<<tm<<"s.\n\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i32 main(i32 argc, const char* argv[]) {
|
|
||||||
// output version info
|
|
||||||
if (argc<=1) {
|
|
||||||
std::clog<<logo;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// run directly or show help
|
|
||||||
if (argc==2) {
|
|
||||||
string s(argv[1]);
|
|
||||||
if (s=="-h" || s=="--help") {
|
|
||||||
std::clog<<help;
|
|
||||||
} else if (s[0]!='-') {
|
|
||||||
execute(s, {}, VM_EXEC);
|
|
||||||
} else {
|
|
||||||
err();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute with arguments
|
|
||||||
const std::unordered_map<string,u32> cmdlst={
|
|
||||||
{"--ast", VM_AST},
|
|
||||||
{"-a", VM_AST},
|
|
||||||
{"--code", VM_CODE},
|
|
||||||
{"-c", VM_CODE},
|
|
||||||
{"--exec", VM_EXEC},
|
|
||||||
{"-e", VM_EXEC},
|
|
||||||
{"--time", VM_TIME|VM_EXEC},
|
|
||||||
{"-t", VM_TIME|VM_EXEC},
|
|
||||||
{"--detail", VM_DETAIL|VM_EXEC},
|
|
||||||
{"-d", VM_DETAIL|VM_EXEC},
|
|
||||||
{"--debug", VM_DEBUG},
|
|
||||||
{"-dbg", VM_DEBUG}
|
|
||||||
};
|
|
||||||
u32 cmd=0;
|
|
||||||
string filename="";
|
|
||||||
std::vector<string> vm_argv;
|
|
||||||
for(i32 i=1; i<argc; ++i) {
|
|
||||||
if (cmdlst.count(argv[i])) {
|
|
||||||
cmd|=cmdlst.at(argv[i]);
|
|
||||||
} else if (!filename.length()) {
|
|
||||||
filename=argv[i];
|
|
||||||
} else {
|
|
||||||
vm_argv.push_back(argv[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!filename.length()) {
|
|
||||||
err();
|
|
||||||
}
|
|
||||||
execute(filename, vm_argv, cmd?cmd:VM_EXEC);
|
|
||||||
return 0;
|
|
||||||
}
|
|
102
makefile
102
makefile
|
@ -1,37 +1,89 @@
|
||||||
.PHONY:test clean
|
STD=c++17
|
||||||
|
|
||||||
SRC=\
|
NASAL_OBJECT=\
|
||||||
main.cpp\
|
nasal_err.o\
|
||||||
nasal_ast.h\
|
nasal_ast.o\
|
||||||
nasal_err.h\
|
ast_visitor.o\
|
||||||
nasal_builtin.h\
|
ast_dumper.o\
|
||||||
nasal_opcode.h\
|
nasal_lexer.o\
|
||||||
nasal_opt.h\
|
nasal_parse.o\
|
||||||
nasal_codegen.h\
|
nasal_import.o\
|
||||||
nasal_gc.h\
|
optimizer.o\
|
||||||
nasal_import.h\
|
nasal_opcode.o\
|
||||||
nasal_lexer.h\
|
symbol_finder.o\
|
||||||
nasal_parse.h\
|
nasal_codegen.o\
|
||||||
nasal_vm.h\
|
nasal_misc.o\
|
||||||
nasal_dbg.h\
|
nasal_gc.o\
|
||||||
nasal.h
|
nasal_builtin.o\
|
||||||
|
nasal_vm.o\
|
||||||
|
nasal_dbg.o\
|
||||||
|
main.o
|
||||||
|
|
||||||
STD=c++14
|
# for test
|
||||||
|
nasal: $(NASAL_OBJECT)
|
||||||
|
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl
|
||||||
|
|
||||||
nasal:$(SRC)
|
nasal.exe: $(NASAL_OBJECT)
|
||||||
$(CXX) -std=$(STD) -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
|
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
|
||||||
nasal.exe:$(SRC)
|
|
||||||
$(CXX) -std=$(STD) -O3 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
|
|
||||||
|
|
||||||
stable-release:$(SRC)
|
main.o: src/main.cpp
|
||||||
$(CXX) -std=$(STD) -O2 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
|
$(CXX) -std=$(STD) -c -O3 src/main.cpp -fno-exceptions -fPIC -o main.o -I .
|
||||||
stable-release-mingw:$(SRC)
|
|
||||||
$(CXX) -std=$(STD) -O2 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
|
|
||||||
|
|
||||||
|
nasal_misc.o: src/nasal.h src/nasal_misc.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_misc.cpp -fno-exceptions -fPIC -o nasal_misc.o -I .
|
||||||
|
|
||||||
|
nasal_err.o: src/nasal_err.h src/nasal_err.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_err.cpp -fno-exceptions -fPIC -o nasal_err.o -I .
|
||||||
|
|
||||||
|
nasal_gc.o: src/nasal_gc.h src/nasal_gc.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_gc.cpp -fno-exceptions -fPIC -o nasal_gc.o -I .
|
||||||
|
|
||||||
|
nasal_import.o: src/nasal_import.h src/nasal_import.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_import.cpp -fno-exceptions -fPIC -o nasal_import.o -I .
|
||||||
|
|
||||||
|
nasal_lexer.o: src/nasal_lexer.h src/nasal_lexer.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_lexer.cpp -fno-exceptions -fPIC -o nasal_lexer.o -I .
|
||||||
|
|
||||||
|
nasal_ast.o: src/nasal_ast.h src/nasal_ast.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_ast.cpp -fno-exceptions -fPIC -o nasal_ast.o -I .
|
||||||
|
|
||||||
|
nasal_builtin.o: src/nasal_builtin.h src/nasal_builtin.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_builtin.cpp -fno-exceptions -fPIC -o nasal_builtin.o -I .
|
||||||
|
|
||||||
|
nasal_codegen.o: src/nasal_codegen.h src/nasal_codegen.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_codegen.cpp -fno-exceptions -fPIC -o nasal_codegen.o -I .
|
||||||
|
|
||||||
|
nasal_opcode.o: src/nasal_opcode.h src/nasal_opcode.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_opcode.cpp -fno-exceptions -fPIC -o nasal_opcode.o -I .
|
||||||
|
|
||||||
|
nasal_parse.o: src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_parse.cpp -fno-exceptions -fPIC -o nasal_parse.o -I .
|
||||||
|
|
||||||
|
optimizer.o: src/optimizer.h src/optimizer.cpp src/nasal_ast.h
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/optimizer.cpp -fno-exceptions -fPIC -o optimizer.o -I .
|
||||||
|
|
||||||
|
symbol_finder.o: src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/symbol_finder.cpp -fno-exceptions -fPIC -o symbol_finder.o -I .
|
||||||
|
|
||||||
|
ast_visitor.o: src/nasal_ast.h src/ast_visitor.h src/ast_visitor.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/ast_visitor.cpp -fno-exceptions -fPIC -o ast_visitor.o -I .
|
||||||
|
|
||||||
|
ast_dumper.o: src/nasal_ast.h src/ast_visitor.h src/ast_dumper.h src/ast_dumper.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/ast_dumper.cpp -fno-exceptions -fPIC -o ast_dumper.o -I .
|
||||||
|
|
||||||
|
nasal_vm.o: src/nasal_vm.h src/nasal_vm.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_vm.cpp -fno-exceptions -fPIC -o nasal_vm.o -I .
|
||||||
|
|
||||||
|
nasal_dbg.o: src/nasal_dbg.h src/nasal_dbg.cpp
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_dbg.cpp -fno-exceptions -fPIC -o nasal_dbg.o -I .
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@ echo "[clean] nasal" && if [ -e nasal ]; then rm nasal; fi
|
@ echo "[clean] nasal" && if [ -e nasal ]; then rm nasal; fi
|
||||||
@ echo "[clean] nasal.exe" && if [ -e nasal.exe ]; then rm nasal.exe; fi
|
@ echo "[clean] nasal.exe" && if [ -e nasal.exe ]; then rm nasal.exe; fi
|
||||||
|
@ rm $(NASAL_OBJECT)
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
test:nasal
|
test:nasal
|
||||||
@ ./nasal -e test/ascii-art.nas
|
@ ./nasal -e test/ascii-art.nas
|
||||||
@ ./nasal -t -d test/bfs.nas
|
@ ./nasal -t -d test/bfs.nas
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// module for test
|
// module for test
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "../nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
|
||||||
namespace nasal_fib_module {
|
namespace nasal_fib_module {
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../nasal.h"
|
#include "../src/nasal.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
|
|
|
@ -1,59 +1,60 @@
|
||||||
.PHONY=clean all winall
|
.PHONY=clean all winall
|
||||||
|
|
||||||
dynamic_libs_so=libfib.so libkey.so libnasock.so libmat.so
|
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
||||||
dynamic_libs_dll=libfib.dll libkey.dll libnasock.dll libmat.dll
|
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
||||||
|
|
||||||
used_header= ../nasal.h ../nasal_gc.h
|
used_header = ../src/nasal.h ../src/nasal_gc.h
|
||||||
|
used_object = ../nasal_misc.o ../nasal_gc.o
|
||||||
|
|
||||||
STD=c++14
|
STD=c++17
|
||||||
|
|
||||||
all: $(dynamic_libs_so)
|
all: $(dynamic_libs_so)
|
||||||
@ echo "[Compiling] done"
|
@ echo "[Compiling] done"
|
||||||
winall: $(dynamic_libs_dll)
|
winall: $(dynamic_libs_dll)
|
||||||
@ echo [Compiling] done
|
@ echo [Compiling] done
|
||||||
|
|
||||||
libfib.so: fib.cpp $(used_header)
|
libfib.so: fib.cpp $(used_header) $(used_object)
|
||||||
@ echo "[Compiling] libfib.so"
|
@ echo "[Compiling] libfib.so"
|
||||||
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
||||||
@ $(CXX) -shared -o libfib.so fib.o
|
@ $(CXX) -shared -o libfib.so fib.o $(used_object)
|
||||||
@ rm fib.o
|
@ rm fib.o
|
||||||
libfib.dll: fib.cpp $(used_header)
|
libfib.dll: fib.cpp $(used_header) $(used_object)
|
||||||
@ echo [Compiling] libfib.dll
|
@ echo [Compiling] libfib.dll
|
||||||
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
||||||
@ $(CXX) -shared -o libfib.dll fib.o
|
@ $(CXX) -shared -o libfib.dll fib.o $(used_object) -static
|
||||||
@ del fib.o
|
@ del fib.o
|
||||||
|
|
||||||
libkey.so: keyboard.cpp $(used_header)
|
libkey.so: keyboard.cpp $(used_header) $(used_object)
|
||||||
@ echo "[Compiling] libkey.so"
|
@ echo "[Compiling] libkey.so"
|
||||||
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o
|
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o
|
||||||
@ $(CXX) -shared -o libkey.so keyboard.o
|
@ $(CXX) -shared -o libkey.so keyboard.o $(used_object)
|
||||||
@ rm keyboard.o
|
@ rm keyboard.o
|
||||||
libkey.dll: keyboard.cpp $(used_header)
|
libkey.dll: keyboard.cpp $(used_header) $(used_object)
|
||||||
@ echo [Compiling] libkey.dll
|
@ echo [Compiling] libkey.dll
|
||||||
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
|
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
|
||||||
@ $(CXX) -shared -o libkey.dll keyboard.o -static
|
@ $(CXX) -shared -o libkey.dll keyboard.o $(used_object) -static
|
||||||
@ del keyboard.o
|
@ del keyboard.o
|
||||||
|
|
||||||
libnasock.so: nasocket.cpp $(used_header)
|
libnasock.so: nasocket.cpp $(used_header) $(used_object)
|
||||||
@ echo "[Compiling] libnasock.so"
|
@ echo "[Compiling] libnasock.so"
|
||||||
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o
|
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o
|
||||||
@ $(CXX) -shared -o libnasock.so nasocket.o
|
@ $(CXX) -shared -o libnasock.so nasocket.o $(used_object)
|
||||||
@ rm nasocket.o
|
@ rm nasocket.o
|
||||||
libnasock.dll: nasocket.cpp $(used_header)
|
libnasock.dll: nasocket.cpp $(used_header) $(used_object)
|
||||||
@ echo [Compiling] libnasock.dll
|
@ echo [Compiling] libnasock.dll
|
||||||
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
|
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
|
||||||
@ $(CXX) -shared -o libnasock.dll nasocket.o -lwsock32 -static
|
@ $(CXX) -shared -o libnasock.dll nasocket.o $(used_object) -lwsock32 -static
|
||||||
@ del nasocket.o
|
@ del nasocket.o
|
||||||
|
|
||||||
libmat.so: matrix.cpp $(used_header)
|
libmat.so: matrix.cpp $(used_header) $(used_object)
|
||||||
@ echo "[Compiling] libmat.so"
|
@ echo "[Compiling] libmat.so"
|
||||||
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o
|
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o
|
||||||
@ $(CXX) -shared -o libmat.so matrix.o
|
@ $(CXX) -shared -o libmat.so matrix.o $(used_object)
|
||||||
@ rm matrix.o
|
@ rm matrix.o
|
||||||
libmat.dll: matrix.cpp $(used_header)
|
libmat.dll: matrix.cpp $(used_header) $(used_object)
|
||||||
@ echo [Compiling] libmat.dll
|
@ echo [Compiling] libmat.dll
|
||||||
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o -static
|
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o -static
|
||||||
@ $(CXX) -shared -o libmat.dll matrix.o -static
|
@ $(CXX) -shared -o libmat.dll matrix.o $(used_object) -static
|
||||||
@ del matrix.o
|
@ del matrix.o
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../nasal.h"
|
#include "../src/nasal.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
var nas_vec2(var* args, usize size, gc* ngc) {
|
var nas_vec2(var* args, usize size, gc* ngc) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
301
nasal_ast.h
301
nasal_ast.h
|
@ -1,301 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "nasal.h"
|
|
||||||
#include "nasal_err.h"
|
|
||||||
|
|
||||||
enum ast_node:u32 {
|
|
||||||
ast_null=0, // null node
|
|
||||||
ast_root, // mark the root node of ast
|
|
||||||
ast_block, // expression block
|
|
||||||
ast_file, // used to store which file the sub-tree is on, only used in main block
|
|
||||||
ast_nil, // nil keyword
|
|
||||||
ast_num, // number, basic value type
|
|
||||||
ast_str, // string, basic value type
|
|
||||||
ast_id, // identifier
|
|
||||||
ast_bool, // bools
|
|
||||||
ast_func, // func keyword
|
|
||||||
ast_hash, // hash, basic value type
|
|
||||||
ast_vec, // vector, basic value type
|
|
||||||
ast_pair, // pair of key and value in hashmap
|
|
||||||
ast_call, // mark a sub-tree of calling an identifier
|
|
||||||
ast_callh, // id.name
|
|
||||||
ast_callv, // id[index]
|
|
||||||
ast_callf, // id()
|
|
||||||
ast_subvec, // id[index:index]
|
|
||||||
ast_params, // mark a sub-tree of function parameters
|
|
||||||
ast_default, // default parameter
|
|
||||||
ast_dynamic, // dynamic parameter
|
|
||||||
ast_and, // and keyword
|
|
||||||
ast_or, // or keyword
|
|
||||||
ast_equal, // =
|
|
||||||
ast_addeq, // +=
|
|
||||||
ast_subeq, // -=
|
|
||||||
ast_multeq, // *=
|
|
||||||
ast_diveq, // /=
|
|
||||||
ast_lnkeq, // ~=
|
|
||||||
ast_btandeq, // &=
|
|
||||||
ast_btoreq, // |=
|
|
||||||
ast_btxoreq, // ^=
|
|
||||||
ast_cmpeq, // ==
|
|
||||||
ast_neq, // !=
|
|
||||||
ast_less, // <
|
|
||||||
ast_leq, // <=
|
|
||||||
ast_grt, // >
|
|
||||||
ast_geq, // >=
|
|
||||||
ast_add, // +
|
|
||||||
ast_sub, // -
|
|
||||||
ast_mult, // *
|
|
||||||
ast_div, // /
|
|
||||||
ast_link, // ~
|
|
||||||
ast_neg, // unary -
|
|
||||||
ast_lnot, // unary !
|
|
||||||
ast_bnot, // unary ~ bitwise not
|
|
||||||
ast_bitor, // bitwise or
|
|
||||||
ast_bitxor, // bitwise xor
|
|
||||||
ast_bitand, // bitwise and
|
|
||||||
ast_trino, // ?:
|
|
||||||
ast_for, // for keyword
|
|
||||||
ast_forindex, // forindex keyword
|
|
||||||
ast_foreach, // foreach keyword
|
|
||||||
ast_while, // while
|
|
||||||
ast_iter, // iterator, used in forindex/foreach
|
|
||||||
ast_cond, // mark a sub-tree of conditional expression
|
|
||||||
ast_if, // if keyword
|
|
||||||
ast_elsif, // elsif keyword
|
|
||||||
ast_else, // else keyword
|
|
||||||
ast_multi_id, // multi identifiers sub-tree
|
|
||||||
ast_tuple, // tuple, only used in multiple assignment
|
|
||||||
ast_def, // definition
|
|
||||||
ast_multi_assign,// multi assignment sub-tree
|
|
||||||
ast_continue, // continue keyword, only used in loop
|
|
||||||
ast_break, // break keyword, only used in loop
|
|
||||||
ast_ret // return keyword, only used in function block
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* ast_name[]={
|
|
||||||
"Null",
|
|
||||||
"AbstractSyntaxTreeRoot",
|
|
||||||
"CodeBlock",
|
|
||||||
"FileIndex",
|
|
||||||
"NilLiteral",
|
|
||||||
"NumberLiteral",
|
|
||||||
"StringLiteral",
|
|
||||||
"Identifier",
|
|
||||||
"BoolLiteral",
|
|
||||||
"Function",
|
|
||||||
"HashMap",
|
|
||||||
"Vector",
|
|
||||||
"HashMapPair",
|
|
||||||
"IdentifierCall",
|
|
||||||
"HashMapCall",
|
|
||||||
"VectorCall",
|
|
||||||
"FunctionCall",
|
|
||||||
"SubVector",
|
|
||||||
"ParameterList",
|
|
||||||
"DefaultParameter",
|
|
||||||
"DynamicParameter",
|
|
||||||
"And",
|
|
||||||
"Or",
|
|
||||||
"Equal",
|
|
||||||
"AddEqual",
|
|
||||||
"SubEqual",
|
|
||||||
"MultEqual",
|
|
||||||
"DivEqual",
|
|
||||||
"LinkEqual",
|
|
||||||
"BitwiseAndEqual",
|
|
||||||
"BitwiseOrEqual",
|
|
||||||
"BitwiseXorEqual",
|
|
||||||
"CompareEqual",
|
|
||||||
"NotEqual",
|
|
||||||
"Less",
|
|
||||||
"LessOrEqual",
|
|
||||||
"Great",
|
|
||||||
"GreatOrEqual",
|
|
||||||
"Add",
|
|
||||||
"Sub",
|
|
||||||
"Mult",
|
|
||||||
"Div",
|
|
||||||
"Link",
|
|
||||||
"Negative",
|
|
||||||
"LogicalNot",
|
|
||||||
"BitwiseNot",
|
|
||||||
"BitwiseOr",
|
|
||||||
"BitwiseXor",
|
|
||||||
"BitwiseAnd",
|
|
||||||
"Trinocular",
|
|
||||||
"ForLoop",
|
|
||||||
"ForindexLoop",
|
|
||||||
"ForeachLoop",
|
|
||||||
"WhileLoop",
|
|
||||||
"Iterator",
|
|
||||||
"Condition",
|
|
||||||
"If",
|
|
||||||
"Elsif",
|
|
||||||
"Else",
|
|
||||||
"LeftTuple",
|
|
||||||
"Tuple",
|
|
||||||
"Definition",
|
|
||||||
"MultipleAssignment",
|
|
||||||
"Continue",
|
|
||||||
"Break",
|
|
||||||
"Return"
|
|
||||||
};
|
|
||||||
|
|
||||||
class ast {
|
|
||||||
public:
|
|
||||||
ast(const ast&) = delete;
|
|
||||||
ast& operator=(const ast&) = delete;
|
|
||||||
|
|
||||||
ast(ast&&) = default;
|
|
||||||
ast& operator=(ast&&) = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void print(u32, bool, std::vector<string>&) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
span loc;
|
|
||||||
u32 nd_type;
|
|
||||||
f64 nd_num;
|
|
||||||
string nd_str;
|
|
||||||
std::vector<ast> nd_child;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ast(const span& s, const u32 t)
|
|
||||||
: loc(s), nd_type(t), nd_num(0), nd_str("") {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
void dump() const;
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
public:
|
|
||||||
ast& operator[](usize n) {return nd_child[n];}
|
|
||||||
const ast& operator[](usize n) const {return nd_child[n];}
|
|
||||||
usize size() const {return nd_child.size();}
|
|
||||||
|
|
||||||
public:
|
|
||||||
void add(ast&& node) {nd_child.push_back(std::move(node));}
|
|
||||||
void set_begin(const u32, const u32);
|
|
||||||
void set_end(const u32, const u32);
|
|
||||||
void set_type(const u32 t) {nd_type=t;}
|
|
||||||
void set_str(const string& s) {nd_str=s;}
|
|
||||||
void set_num(const f64 n) {nd_num=n;}
|
|
||||||
|
|
||||||
public:
|
|
||||||
u32 line() const {return loc.end_line;}
|
|
||||||
u32 type() const {return nd_type;}
|
|
||||||
f64 num() const {return nd_num;}
|
|
||||||
const string& str() const {return nd_str;}
|
|
||||||
const string& file() const {return loc.file;}
|
|
||||||
const span& location() const {return loc;}
|
|
||||||
|
|
||||||
const std::vector<ast>& child() const {return nd_child;}
|
|
||||||
std::vector<ast>& child() {return nd_child;}
|
|
||||||
|
|
||||||
void update_span();
|
|
||||||
void update_span(const span&);
|
|
||||||
};
|
|
||||||
|
|
||||||
void ast::set_begin(const u32 l, const u32 c) {
|
|
||||||
loc.begin_line=l;
|
|
||||||
loc.begin_column=c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast::set_end(const u32 l, const u32 c) {
|
|
||||||
loc.end_line=l;
|
|
||||||
loc.end_column=c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast::clear() {
|
|
||||||
loc={0, 0, 0, 0, ""};
|
|
||||||
nd_num=0;
|
|
||||||
nd_str.clear();
|
|
||||||
nd_type=ast_null;
|
|
||||||
nd_child.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast::dump() const{
|
|
||||||
std::vector<string> tmp;
|
|
||||||
print(0, false, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast::print(u32 depth, bool last, std::vector<string>& indent) const{
|
|
||||||
// output the indentation first
|
|
||||||
for(auto& i:indent) {
|
|
||||||
std::cout<<i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// output ast node name
|
|
||||||
std::cout<<ast_name[nd_type];
|
|
||||||
|
|
||||||
// output string literal and number
|
|
||||||
if (nd_type==ast_str ||
|
|
||||||
nd_type==ast_id ||
|
|
||||||
nd_type==ast_bool ||
|
|
||||||
nd_type==ast_default ||
|
|
||||||
nd_type==ast_dynamic ||
|
|
||||||
nd_type==ast_callh) {
|
|
||||||
std::cout<<":"<<rawstr(nd_str);
|
|
||||||
} else if (nd_type==ast_num || nd_type==ast_file) {
|
|
||||||
std::cout<<":"<<nd_num;
|
|
||||||
}
|
|
||||||
|
|
||||||
// okay, we must know that begin_column starts from index 0
|
|
||||||
std::cout<<" --> "<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<"\n";
|
|
||||||
|
|
||||||
// output tree structure
|
|
||||||
if (last && depth) {
|
|
||||||
indent.back()=" ";
|
|
||||||
} else if (!last && depth) {
|
|
||||||
indent.back()=is_windows()? "| ":"│ ";
|
|
||||||
}
|
|
||||||
for(u32 i=0; i<nd_child.size(); ++i) {
|
|
||||||
if (is_windows()) {
|
|
||||||
indent.push_back(i==nd_child.size()-1?"+-":"|-");
|
|
||||||
} else {
|
|
||||||
indent.push_back(i==nd_child.size()-1?"└─":"├─");
|
|
||||||
}
|
|
||||||
nd_child[i].print(depth+1,i==nd_child.size()-1,indent);
|
|
||||||
indent.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast::update_span() {
|
|
||||||
if (!nd_child.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(const auto& i:nd_child) {
|
|
||||||
if (loc.begin_line>i.loc.begin_line) {
|
|
||||||
loc.begin_line=i.loc.begin_line;
|
|
||||||
loc.begin_column=i.loc.begin_column;
|
|
||||||
} else if (loc.begin_line==i.loc.begin_line && loc.begin_column>i.loc.begin_column) {
|
|
||||||
loc.begin_column=i.loc.begin_column;
|
|
||||||
}
|
|
||||||
if (loc.end_line<i.loc.end_line) {
|
|
||||||
loc.end_line=i.loc.end_line;
|
|
||||||
loc.end_column=i.loc.end_column;
|
|
||||||
} else if (loc.end_line==i.loc.end_line && loc.end_column<i.loc.end_column) {
|
|
||||||
loc.end_column=i.loc.end_column;
|
|
||||||
}
|
|
||||||
loc.file=i.loc.file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast::update_span(const span& tloc) {
|
|
||||||
update_span();
|
|
||||||
if (loc.begin_line>tloc.begin_line) {
|
|
||||||
loc.begin_line=tloc.begin_line;
|
|
||||||
loc.begin_column=tloc.begin_column;
|
|
||||||
} else if (loc.begin_line==tloc.begin_line && loc.begin_column>tloc.begin_column) {
|
|
||||||
loc.begin_column=tloc.begin_column;
|
|
||||||
}
|
|
||||||
if (loc.end_line<tloc.end_line) {
|
|
||||||
loc.end_line=tloc.end_line;
|
|
||||||
loc.end_column=tloc.end_column;
|
|
||||||
} else if (loc.end_line==tloc.end_line && loc.end_column<tloc.end_column) {
|
|
||||||
loc.end_column=tloc.end_column;
|
|
||||||
}
|
|
||||||
loc.file=tloc.file;
|
|
||||||
}
|
|
1087
nasal_codegen.h
1087
nasal_codegen.h
File diff suppressed because it is too large
Load Diff
922
nasal_gc.h
922
nasal_gc.h
|
@ -1,922 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning (disable:4244)
|
|
||||||
#pragma warning (disable:4267)
|
|
||||||
#pragma warning (disable:4102)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#else
|
|
||||||
#include <io.h>
|
|
||||||
#include <direct.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <iomanip>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <chrono>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "nasal.h"
|
|
||||||
#include "nasal_err.h"
|
|
||||||
|
|
||||||
enum vm_type:u8 {
|
|
||||||
/* none-gc object */
|
|
||||||
vm_none=0,
|
|
||||||
vm_cnt,
|
|
||||||
vm_addr,
|
|
||||||
vm_ret,
|
|
||||||
vm_nil,
|
|
||||||
vm_num,
|
|
||||||
/* gc object */
|
|
||||||
vm_str,
|
|
||||||
vm_vec,
|
|
||||||
vm_hash,
|
|
||||||
vm_func,
|
|
||||||
vm_upval,
|
|
||||||
vm_obj,
|
|
||||||
vm_co
|
|
||||||
};
|
|
||||||
|
|
||||||
const u32 gc_type_size=vm_co-vm_str+1;
|
|
||||||
|
|
||||||
enum class coroutine_status:u32 {
|
|
||||||
suspended,
|
|
||||||
running,
|
|
||||||
dead
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class gc_status:u8 {
|
|
||||||
uncollected=0,
|
|
||||||
collected,
|
|
||||||
found
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_vec; // vector
|
|
||||||
struct nas_hash; // hashmap(dict)
|
|
||||||
struct nas_func; // function(lambda)
|
|
||||||
struct nas_upval; // upvalue
|
|
||||||
struct nas_obj; // special objects
|
|
||||||
struct nas_co; // coroutine
|
|
||||||
struct nas_val; // nas_val includes gc-managed types
|
|
||||||
|
|
||||||
struct var {
|
|
||||||
public:
|
|
||||||
u8 type;
|
|
||||||
union {
|
|
||||||
u32 ret;
|
|
||||||
i64 cnt;
|
|
||||||
f64 num;
|
|
||||||
var* addr;
|
|
||||||
nas_val* gcobj;
|
|
||||||
} val;
|
|
||||||
|
|
||||||
private:
|
|
||||||
var(u8 t, u32 pc) {type=t;val.ret=pc;}
|
|
||||||
var(u8 t, i64 ct) {type=t;val.cnt=ct;}
|
|
||||||
var(u8 t, f64 n) {type=t;val.num=n;}
|
|
||||||
var(u8 t, var* p) {type=t;val.addr=p;}
|
|
||||||
var(u8 t, nas_val* p) {type=t;val.gcobj=p;}
|
|
||||||
|
|
||||||
public:
|
|
||||||
var() = default;
|
|
||||||
var(const var&) = default;
|
|
||||||
bool operator==(const var& nr) const {return type==nr.type && val.gcobj==nr.val.gcobj;}
|
|
||||||
bool operator!=(const var& nr) const {return type!=nr.type || val.gcobj!=nr.val.gcobj;}
|
|
||||||
friend std::ostream& operator<<(std::ostream&, var&);
|
|
||||||
|
|
||||||
// number and string can be translated to each other
|
|
||||||
f64 tonum();
|
|
||||||
string tostr();
|
|
||||||
bool objchk(usize);
|
|
||||||
|
|
||||||
// create new var object
|
|
||||||
static var none();
|
|
||||||
static var nil();
|
|
||||||
static var ret(u32);
|
|
||||||
static var cnt(i64);
|
|
||||||
static var num(f64);
|
|
||||||
static var gcobj(nas_val*);
|
|
||||||
static var addr(var*);
|
|
||||||
|
|
||||||
// get content
|
|
||||||
var* addr();
|
|
||||||
u32 ret ();
|
|
||||||
i64& cnt ();
|
|
||||||
f64 num ();
|
|
||||||
string& str ();
|
|
||||||
nas_vec& vec ();
|
|
||||||
nas_hash& hash();
|
|
||||||
nas_func& func();
|
|
||||||
nas_upval& upval();
|
|
||||||
nas_obj& obj ();
|
|
||||||
nas_co& co ();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_vec {
|
|
||||||
std::vector<var> elems;
|
|
||||||
|
|
||||||
// mark if this is printed, avoid stackoverflow
|
|
||||||
bool printed;
|
|
||||||
|
|
||||||
nas_vec():printed(false) {}
|
|
||||||
usize size() const {return elems.size();}
|
|
||||||
var get_val(const i32);
|
|
||||||
var* get_mem(const i32);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_hash {
|
|
||||||
std::unordered_map<string,var> elems;
|
|
||||||
|
|
||||||
// mark if this is printed, avoid stackoverflow
|
|
||||||
bool printed;
|
|
||||||
|
|
||||||
nas_hash():printed(false) {}
|
|
||||||
usize size() const {return elems.size();}
|
|
||||||
var get_val(const string&);
|
|
||||||
var* get_mem(const string&);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_func {
|
|
||||||
i32 dpara; // dynamic parameter name index in hash.
|
|
||||||
u32 entry; // pc will set to entry-1 to call this function
|
|
||||||
u32 psize; // used to load default parameters to a new function
|
|
||||||
u32 lsize; // used to expand memory space for local values on stack
|
|
||||||
std::vector<var> local; // local scope with default value(var)
|
|
||||||
std::vector<var> upval; // closure
|
|
||||||
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
|
|
||||||
|
|
||||||
nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {}
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_upval {
|
|
||||||
public:
|
|
||||||
/* on stack, use these variables */
|
|
||||||
bool onstk;
|
|
||||||
u32 size;
|
|
||||||
var* stk;
|
|
||||||
|
|
||||||
/* not on stack, use this */
|
|
||||||
std::vector<var> elems;
|
|
||||||
|
|
||||||
public:
|
|
||||||
nas_upval(): onstk(true), size(0), stk(nullptr) {}
|
|
||||||
|
|
||||||
var& operator[](usize n) {
|
|
||||||
return onstk? stk[n]:elems[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
onstk=true;
|
|
||||||
elems.clear();
|
|
||||||
size=0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void filehandle_destructor(void* ptr) {
|
|
||||||
if ((FILE*)ptr==stdin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fclose((FILE*)ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dir_entry_destructor(void* ptr) {
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
closedir((DIR*)ptr);
|
|
||||||
#else
|
|
||||||
FindClose(ptr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void dylib_destructor(void* ptr) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
FreeLibrary((HMODULE)ptr);
|
|
||||||
#else
|
|
||||||
dlclose(ptr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void func_addr_destructor(void* ptr) {}
|
|
||||||
|
|
||||||
struct ghost_register_table {
|
|
||||||
private:
|
|
||||||
using dtor=void (*)(void*);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<string,usize> mapper;
|
|
||||||
std::vector<string> ghost_name;
|
|
||||||
std::vector<dtor> destructors;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// reserved ghost type only for native functions
|
|
||||||
usize ghost_file;
|
|
||||||
usize ghost_dir;
|
|
||||||
usize ghost_dylib;
|
|
||||||
usize ghost_faddr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ghost_register_table() {
|
|
||||||
ghost_file=register_ghost_type("file", filehandle_destructor);
|
|
||||||
ghost_dir=register_ghost_type("dir", dir_entry_destructor);
|
|
||||||
ghost_dylib=register_ghost_type("dylib", dylib_destructor);
|
|
||||||
ghost_faddr=register_ghost_type("faddr", func_addr_destructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool exists(const string& name) const {
|
|
||||||
return mapper.count(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
usize get_ghost_type_index(const string& name) const {
|
|
||||||
return mapper.at(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const string& get_ghost_name(usize index) const {
|
|
||||||
return ghost_name.at(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
usize register_ghost_type(const std::string& name, dtor ptr) {
|
|
||||||
if (mapper.count(name)) {
|
|
||||||
std::cerr<<"nasal_gc.h: ghost_register_table::register_ghost_type: ";
|
|
||||||
std::cerr<<"ghost type \""<<name<<"\" already exists.\n";
|
|
||||||
std::exit(-1);
|
|
||||||
}
|
|
||||||
auto res=destructors.size();
|
|
||||||
mapper[name]=res;
|
|
||||||
ghost_name.push_back(name);
|
|
||||||
destructors.push_back(ptr);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtor destructor(usize index) {
|
|
||||||
return destructors.at(index);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_obj {
|
|
||||||
public:
|
|
||||||
usize type;
|
|
||||||
void* ptr;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ghost_register_table* ghost_type_table;
|
|
||||||
|
|
||||||
public:
|
|
||||||
nas_obj(): type(0), ptr(nullptr), ghost_type_table(nullptr) {}
|
|
||||||
~nas_obj() {clear();}
|
|
||||||
void set(usize, void*, ghost_register_table*);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
public:
|
|
||||||
friend std::ostream& operator<<(std::ostream& out, nas_obj& ghost) {
|
|
||||||
out<<"<object "<<ghost.ghost_type_table->get_ghost_name(ghost.type);
|
|
||||||
out<<" at 0x"<<std::hex<<(u64)ghost.ptr<<std::dec<<">";
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct context {
|
|
||||||
u32 pc;
|
|
||||||
var* localr;
|
|
||||||
var* memr;
|
|
||||||
var funcr;
|
|
||||||
var upvalr;
|
|
||||||
var* canary;
|
|
||||||
var* stack;
|
|
||||||
var* top;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_co {
|
|
||||||
var stack[STACK_DEPTH];
|
|
||||||
context ctx;
|
|
||||||
coroutine_status status;
|
|
||||||
|
|
||||||
nas_co() {clear();}
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_val {
|
|
||||||
gc_status mark;
|
|
||||||
u8 type; // value type
|
|
||||||
u8 unmut; // used to mark if a string is unmutable
|
|
||||||
union {
|
|
||||||
string* str;
|
|
||||||
nas_vec* vec;
|
|
||||||
nas_hash* hash;
|
|
||||||
nas_func* func;
|
|
||||||
nas_upval* upval;
|
|
||||||
nas_obj* obj;
|
|
||||||
nas_co* co;
|
|
||||||
} ptr;
|
|
||||||
|
|
||||||
nas_val(u8);
|
|
||||||
~nas_val();
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
var nas_vec::get_val(const i32 n) {
|
|
||||||
i32 size=elems.size();
|
|
||||||
if (n<-size || n>=size) {
|
|
||||||
return var::none();
|
|
||||||
}
|
|
||||||
return elems[n>=0?n:n+size];
|
|
||||||
}
|
|
||||||
|
|
||||||
var* nas_vec::get_mem(const i32 n) {
|
|
||||||
i32 size=elems.size();
|
|
||||||
if (n<-size || n>=size) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &elems[n>=0?n:n+size];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
|
||||||
if (!vec.elems.size() || vec.printed) {
|
|
||||||
out<<(vec.elems.size()?"[..]":"[]");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
vec.printed=true;
|
|
||||||
usize iter=0,size=vec.elems.size();
|
|
||||||
out<<'[';
|
|
||||||
for(auto& i:vec.elems) {
|
|
||||||
out<<i<<",]"[(++iter)==size];
|
|
||||||
}
|
|
||||||
vec.printed=false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nas_hash::get_val(const string& key) {
|
|
||||||
if (elems.count(key)) {
|
|
||||||
return elems.at(key);
|
|
||||||
} else if (!elems.count("parents")) {
|
|
||||||
return var::none();
|
|
||||||
}
|
|
||||||
var ret=var::none();
|
|
||||||
var val=elems.at("parents");
|
|
||||||
if (val.type!=vm_vec) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
for(auto& i:val.vec().elems) {
|
|
||||||
if (i.type==vm_hash) {
|
|
||||||
ret=i.hash().get_val(key);
|
|
||||||
}
|
|
||||||
if (ret.type!=vm_none) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var* nas_hash::get_mem(const string& key) {
|
|
||||||
if (elems.count(key)) {
|
|
||||||
return &elems.at(key);
|
|
||||||
} else if (!elems.count("parents")) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
var* addr=nullptr;
|
|
||||||
var val=elems.at("parents");
|
|
||||||
if (val.type!=vm_vec) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
for(auto& i:val.vec().elems) {
|
|
||||||
if (i.type==vm_hash) {
|
|
||||||
addr=i.hash().get_mem(key);
|
|
||||||
}
|
|
||||||
if (addr) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
|
||||||
if (!hash.elems.size() || hash.printed) {
|
|
||||||
out<<(hash.elems.size()?"{..}":"{}");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
hash.printed=true;
|
|
||||||
usize iter=0,size=hash.elems.size();
|
|
||||||
out<<'{';
|
|
||||||
for(auto& i:hash.elems) {
|
|
||||||
out<<i.first<<':'<<i.second<<",}"[(++iter)==size];
|
|
||||||
}
|
|
||||||
hash.printed=false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_func::clear() {
|
|
||||||
dpara=-1;
|
|
||||||
local.clear();
|
|
||||||
upval.clear();
|
|
||||||
keys.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_obj::set(usize t, void* p, ghost_register_table* table) {
|
|
||||||
type=t;
|
|
||||||
ptr=p;
|
|
||||||
ghost_type_table=table;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_obj::clear() {
|
|
||||||
if (!ptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ghost_type_table->destructor(type)(ptr);
|
|
||||||
ptr=nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_co::clear() {
|
|
||||||
for(u32 i=0;i<STACK_DEPTH;++i) {
|
|
||||||
stack[i]=var::nil();
|
|
||||||
}
|
|
||||||
ctx.pc=0;
|
|
||||||
ctx.localr=nullptr;
|
|
||||||
ctx.memr=nullptr;
|
|
||||||
ctx.canary=stack+STACK_DEPTH-1;
|
|
||||||
ctx.top=stack;
|
|
||||||
ctx.funcr=var::nil();
|
|
||||||
ctx.upvalr=var::nil();
|
|
||||||
ctx.stack=stack;
|
|
||||||
|
|
||||||
status=coroutine_status::suspended;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_val::nas_val(u8 val_type) {
|
|
||||||
mark=gc_status::collected;
|
|
||||||
type=val_type;
|
|
||||||
unmut=0;
|
|
||||||
switch(val_type) {
|
|
||||||
case vm_str: ptr.str=new string; break;
|
|
||||||
case vm_vec: ptr.vec=new nas_vec; break;
|
|
||||||
case vm_hash: ptr.hash=new nas_hash; break;
|
|
||||||
case vm_func: ptr.func=new nas_func; break;
|
|
||||||
case vm_upval:ptr.upval=new nas_upval;break;
|
|
||||||
case vm_obj: ptr.obj=new nas_obj; break;
|
|
||||||
case vm_co: ptr.co=new nas_co; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_val::~nas_val() {
|
|
||||||
switch(type) {
|
|
||||||
case vm_str: delete ptr.str; break;
|
|
||||||
case vm_vec: delete ptr.vec; break;
|
|
||||||
case vm_hash: delete ptr.hash; break;
|
|
||||||
case vm_func: delete ptr.func; break;
|
|
||||||
case vm_upval:delete ptr.upval;break;
|
|
||||||
case vm_obj: delete ptr.obj; break;
|
|
||||||
case vm_co: delete ptr.co; break;
|
|
||||||
}
|
|
||||||
type=vm_nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_val::clear() {
|
|
||||||
switch(type) {
|
|
||||||
case vm_str: ptr.str->clear(); break;
|
|
||||||
case vm_vec: ptr.vec->elems.clear(); break;
|
|
||||||
case vm_hash: ptr.hash->elems.clear();break;
|
|
||||||
case vm_func: ptr.func->clear(); break;
|
|
||||||
case vm_upval:ptr.upval->clear(); break;
|
|
||||||
case vm_obj: ptr.obj->clear(); break;
|
|
||||||
case vm_co: ptr.co->clear(); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f64 var::tonum() {
|
|
||||||
return type!=vm_str? val.num:str2num(str().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
string var::tostr() {
|
|
||||||
if (type==vm_str) {
|
|
||||||
return str();
|
|
||||||
} else if (type==vm_num) {
|
|
||||||
string tmp=std::to_string(num());
|
|
||||||
tmp.erase(tmp.find_last_not_of('0')+1, string::npos);
|
|
||||||
tmp.erase(tmp.find_last_not_of('.')+1, string::npos);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, var& ref) {
|
|
||||||
switch(ref.type) {
|
|
||||||
case vm_none: out<<"undefined"; break;
|
|
||||||
case vm_nil: out<<"nil"; break;
|
|
||||||
case vm_num: out<<ref.val.num; break;
|
|
||||||
case vm_str: out<<ref.str(); break;
|
|
||||||
case vm_vec: out<<ref.vec(); break;
|
|
||||||
case vm_hash: out<<ref.hash(); break;
|
|
||||||
case vm_func: out<<"func(..) {..}";break;
|
|
||||||
case vm_obj: out<<ref.obj(); break;
|
|
||||||
case vm_co: out<<"<coroutine>"; break;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool var::objchk(usize obj_type) {
|
|
||||||
return type==vm_obj && obj().type==obj_type && obj().ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::none() {
|
|
||||||
return {vm_none, (u32)0};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::nil() {
|
|
||||||
return {vm_nil, (u32)0};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::ret(u32 pc) {
|
|
||||||
return {vm_ret, pc};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::cnt(i64 n) {
|
|
||||||
return {vm_cnt, n};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::num(f64 n) {
|
|
||||||
return {vm_num, n};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::gcobj(nas_val* p) {
|
|
||||||
return {p->type, p};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::addr(var* p) {
|
|
||||||
return {vm_addr, p};
|
|
||||||
}
|
|
||||||
|
|
||||||
var* var::addr () {return val.addr; }
|
|
||||||
u32 var::ret () {return val.ret; }
|
|
||||||
i64& var::cnt () {return val.cnt; }
|
|
||||||
f64 var::num () {return val.num; }
|
|
||||||
string& var::str () {return *val.gcobj->ptr.str; }
|
|
||||||
nas_vec& var::vec () {return *val.gcobj->ptr.vec; }
|
|
||||||
nas_hash& var::hash () {return *val.gcobj->ptr.hash; }
|
|
||||||
nas_func& var::func () {return *val.gcobj->ptr.func; }
|
|
||||||
nas_upval& var::upval() {return *val.gcobj->ptr.upval;}
|
|
||||||
nas_obj& var::obj () {return *val.gcobj->ptr.obj; }
|
|
||||||
nas_co& var::co () {return *val.gcobj->ptr.co; }
|
|
||||||
|
|
||||||
const var zero=var::num(0);
|
|
||||||
const var one =var::num(1);
|
|
||||||
const var nil =var::nil();
|
|
||||||
|
|
||||||
struct gc {
|
|
||||||
ghost_register_table global_ghost_type_table;
|
|
||||||
/* main context temporary storage */
|
|
||||||
context mctx;
|
|
||||||
|
|
||||||
/* runtime context */
|
|
||||||
context* rctx;
|
|
||||||
nas_co* cort=nullptr; // running coroutine
|
|
||||||
|
|
||||||
/* temporary space used in builtin/module functions */
|
|
||||||
var temp=nil;
|
|
||||||
|
|
||||||
/* constants and memory pool */
|
|
||||||
std::vector<var> strs; // reserved address for const vm_str
|
|
||||||
std::vector<var> env_argv; // command line arguments
|
|
||||||
std::vector<nas_val*> memory; // gc memory
|
|
||||||
std::vector<nas_val*> unused[gc_type_size]; // gc free list
|
|
||||||
|
|
||||||
/* heap increase size */
|
|
||||||
u32 incr[gc_type_size]={
|
|
||||||
128, // vm_str
|
|
||||||
128, // vm_vec
|
|
||||||
64, // vm_hash
|
|
||||||
128, // vm_func
|
|
||||||
256, // vm_upval
|
|
||||||
16, // vm_obj
|
|
||||||
16 // vm_co
|
|
||||||
};
|
|
||||||
|
|
||||||
/* values for analysis */
|
|
||||||
u64 size[gc_type_size];
|
|
||||||
u64 gcnt[gc_type_size];
|
|
||||||
u64 acnt[gc_type_size];
|
|
||||||
i64 worktime=0;
|
|
||||||
|
|
||||||
gc(context* _ctx): rctx(_ctx) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* gc functions */
|
|
||||||
void mark();
|
|
||||||
void mark_context(std::vector<var>&);
|
|
||||||
void mark_var(std::vector<var>&, var&);
|
|
||||||
inline void mark_vec(std::vector<var>&, nas_vec&);
|
|
||||||
inline void mark_hash(std::vector<var>&, nas_hash&);
|
|
||||||
inline void mark_func(std::vector<var>&, nas_func&);
|
|
||||||
inline void mark_upval(std::vector<var>&, nas_upval&);
|
|
||||||
inline void mark_co(std::vector<var>&, nas_co&);
|
|
||||||
void sweep();
|
|
||||||
|
|
||||||
public:
|
|
||||||
void extend(u8);
|
|
||||||
void init(const std::vector<string>&, const std::vector<string>&);
|
|
||||||
void clear();
|
|
||||||
void info();
|
|
||||||
var alloc(const u8);
|
|
||||||
void ctxchg(nas_co&);
|
|
||||||
void ctxreserve();
|
|
||||||
|
|
||||||
public:
|
|
||||||
var newstr(char c) {
|
|
||||||
var s=alloc(vm_str);
|
|
||||||
s.str()=c;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newstr(const char* buff) {
|
|
||||||
var s=alloc(vm_str);
|
|
||||||
s.str()=buff;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newstr(const string& buff) {
|
|
||||||
var s=alloc(vm_str);
|
|
||||||
s.str()=buff;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void gc::mark() {
|
|
||||||
std::vector<var> bfs;
|
|
||||||
mark_context(bfs);
|
|
||||||
|
|
||||||
while(!bfs.empty()) {
|
|
||||||
var value=bfs.back();
|
|
||||||
bfs.pop_back();
|
|
||||||
if (value.type<=vm_num ||
|
|
||||||
value.val.gcobj->mark!=gc_status::uncollected) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mark_var(bfs, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::mark_context(std::vector<var>& bfs_queue) {
|
|
||||||
|
|
||||||
// scan now running context, this context maybe related to coroutine or main
|
|
||||||
for(var* i=rctx->stack;i<=rctx->top;++i) {
|
|
||||||
bfs_queue.push_back(*i);
|
|
||||||
}
|
|
||||||
bfs_queue.push_back(rctx->funcr);
|
|
||||||
bfs_queue.push_back(rctx->upvalr);
|
|
||||||
bfs_queue.push_back(temp);
|
|
||||||
|
|
||||||
if (!cort) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// coroutine is running, so scan main process stack from mctx
|
|
||||||
for(var* i=mctx.stack;i<=mctx.top;++i) {
|
|
||||||
bfs_queue.push_back(*i);
|
|
||||||
}
|
|
||||||
bfs_queue.push_back(mctx.funcr);
|
|
||||||
bfs_queue.push_back(mctx.upvalr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
|
|
||||||
value.val.gcobj->mark=gc_status::found;
|
|
||||||
switch(value.type) {
|
|
||||||
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
|
|
||||||
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
|
|
||||||
case vm_func: mark_func(bfs_queue, value.func()); break;
|
|
||||||
case vm_upval: mark_upval(bfs_queue, value.upval()); break;
|
|
||||||
case vm_co: mark_co(bfs_queue, value.co()); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
|
|
||||||
for(auto& i:vec.elems) {
|
|
||||||
bfs_queue.push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
|
|
||||||
for(auto& i:hash.elems) {
|
|
||||||
bfs_queue.push_back(i.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
|
|
||||||
for(auto& i:function.local) {
|
|
||||||
bfs_queue.push_back(i);
|
|
||||||
}
|
|
||||||
for(auto& i:function.upval) {
|
|
||||||
bfs_queue.push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
|
|
||||||
for(auto& i:upval.elems) {
|
|
||||||
bfs_queue.push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
|
|
||||||
bfs_queue.push_back(co.ctx.funcr);
|
|
||||||
bfs_queue.push_back(co.ctx.upvalr);
|
|
||||||
for(var* i=co.stack;i<=co.ctx.top;++i) {
|
|
||||||
bfs_queue.push_back(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::sweep() {
|
|
||||||
for(auto i:memory) {
|
|
||||||
if (i->mark==gc_status::uncollected) {
|
|
||||||
i->clear();
|
|
||||||
unused[i->type-vm_str].push_back(i);
|
|
||||||
i->mark=gc_status::collected;
|
|
||||||
} else if (i->mark==gc_status::found) {
|
|
||||||
i->mark=gc_status::uncollected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::extend(u8 type) {
|
|
||||||
const u8 index=type-vm_str;
|
|
||||||
size[index]+=incr[index];
|
|
||||||
|
|
||||||
for(u32 i=0;i<incr[index];++i) {
|
|
||||||
nas_val* tmp=new nas_val(type);
|
|
||||||
|
|
||||||
if (!tmp) {
|
|
||||||
std::cerr<<"nasal_gc.h: gc::extend: ";
|
|
||||||
std::cerr<<"failed to allocate memory\n";
|
|
||||||
std::exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to heap
|
|
||||||
memory.push_back(tmp);
|
|
||||||
unused[index].push_back(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
incr[index]*=2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::init(const std::vector<string>& s, const std::vector<string>& argv) {
|
|
||||||
// initialize function register
|
|
||||||
rctx->funcr=nil;
|
|
||||||
worktime=0;
|
|
||||||
|
|
||||||
// initialize counters
|
|
||||||
for(u8 i=0;i<gc_type_size;++i) {
|
|
||||||
size[i]=gcnt[i]=acnt[i]=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// coroutine pointer set to nullptr
|
|
||||||
cort=nullptr;
|
|
||||||
|
|
||||||
// init constant strings
|
|
||||||
strs.resize(s.size());
|
|
||||||
for(u32 i=0;i<strs.size();++i) {
|
|
||||||
strs[i]=var::gcobj(new nas_val(vm_str));
|
|
||||||
strs[i].val.gcobj->unmut=1;
|
|
||||||
strs[i].str()=s[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// record arguments
|
|
||||||
env_argv.resize(argv.size());
|
|
||||||
for(usize i=0;i<argv.size();++i) {
|
|
||||||
env_argv[i]=var::gcobj(new nas_val(vm_str));
|
|
||||||
env_argv[i].val.gcobj->unmut=1;
|
|
||||||
env_argv[i].str()=argv[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::clear() {
|
|
||||||
for(auto i:memory) {
|
|
||||||
delete i;
|
|
||||||
}
|
|
||||||
memory.clear();
|
|
||||||
for(u8 i=0;i<gc_type_size;++i) {
|
|
||||||
unused[i].clear();
|
|
||||||
}
|
|
||||||
for(auto& i:strs) {
|
|
||||||
delete i.val.gcobj;
|
|
||||||
}
|
|
||||||
strs.clear();
|
|
||||||
env_argv.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::info() {
|
|
||||||
using std::left;
|
|
||||||
using std::setw;
|
|
||||||
using std::setfill;
|
|
||||||
const char* name[]={"str ","vec ","hash ","func ","upval","obj ","co "};
|
|
||||||
std::clog<<"\ngc info (gc count|alloc count|memory size)\n";
|
|
||||||
|
|
||||||
usize ident=0;
|
|
||||||
for(u8 i=0;i<gc_type_size;++i) {
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
usize len=std::max({
|
|
||||||
std::to_string(gcnt[i]).length(),
|
|
||||||
std::to_string(acnt[i]).length(),
|
|
||||||
std::to_string(size[i]).length()
|
|
||||||
});
|
|
||||||
#else // VS is a piece of shit
|
|
||||||
usize len=std::to_string(gcnt[i]).length();
|
|
||||||
ident=ident<len?len:ident;
|
|
||||||
len=std::to_string(acnt[i]).length();
|
|
||||||
ident=ident<len?len:ident;
|
|
||||||
len=std::to_string(size[i]).length();
|
|
||||||
#endif
|
|
||||||
ident=ident<len?len:ident;
|
|
||||||
}
|
|
||||||
|
|
||||||
double total=0;
|
|
||||||
for(u8 i=0;i<gc_type_size;++i) {
|
|
||||||
if (gcnt[i] || acnt[i] || size[i]) {
|
|
||||||
total+=gcnt[i];
|
|
||||||
std::clog<<" "<<name[i];
|
|
||||||
std::clog<<" | "<<left<<setw(ident)<<setfill(' ')<<gcnt[i];
|
|
||||||
std::clog<<" | "<<left<<setw(ident)<<setfill(' ')<<acnt[i];
|
|
||||||
std::clog<<" | "<<left<<setw(ident)<<setfill(' ')<<size[i];
|
|
||||||
std::clog<<"\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double sec=worktime*1.0/1000000000; // seconds
|
|
||||||
std::clog<<" time | "<<(sec<0.1? sec*1000:sec)<<(sec<0.1? " ms\n":" s\n");
|
|
||||||
if (total) {
|
|
||||||
std::clog<<" avg | "<<sec/total*1000<<" ms\n";
|
|
||||||
}
|
|
||||||
std::clog<<"\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
var gc::alloc(u8 type) {
|
|
||||||
using clk=std::chrono::high_resolution_clock;
|
|
||||||
const u8 index=type-vm_str;
|
|
||||||
++acnt[index];
|
|
||||||
if (unused[index].empty()) {
|
|
||||||
++gcnt[index];
|
|
||||||
auto begin=clk::now();
|
|
||||||
mark();
|
|
||||||
sweep();
|
|
||||||
worktime+=(clk::now()-begin).count();
|
|
||||||
}
|
|
||||||
if (unused[index].empty()) {
|
|
||||||
extend(type);
|
|
||||||
}
|
|
||||||
var ret=var::gcobj(unused[index].back());
|
|
||||||
ret.val.gcobj->mark=gc_status::uncollected;
|
|
||||||
unused[index].pop_back();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::ctxchg(nas_co& co) {
|
|
||||||
// store running state to main context
|
|
||||||
mctx=*rctx;
|
|
||||||
|
|
||||||
// restore coroutine context state
|
|
||||||
*rctx=co.ctx;
|
|
||||||
|
|
||||||
// set coroutine pointer
|
|
||||||
cort=&co;
|
|
||||||
|
|
||||||
// set coroutine state to running
|
|
||||||
cort->status=coroutine_status::running;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::ctxreserve() {
|
|
||||||
// pc=0 means this coroutine is finished
|
|
||||||
cort->status=rctx->pc?
|
|
||||||
coroutine_status::suspended:
|
|
||||||
coroutine_status::dead;
|
|
||||||
|
|
||||||
// store running state to coroutine
|
|
||||||
cort->ctx=*rctx;
|
|
||||||
|
|
||||||
// restore main context state
|
|
||||||
*rctx=mctx;
|
|
||||||
|
|
||||||
// set coroutine pointer to nullptr
|
|
||||||
cort=nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use to print error log and return error value
|
|
||||||
var nas_err(const string& error_function_name, const string& info) {
|
|
||||||
std::cerr<<"[vm] "<<error_function_name<<": "<<info<<"\n";
|
|
||||||
return var::none();
|
|
||||||
}
|
|
||||||
|
|
||||||
// module function type
|
|
||||||
typedef var (*module_func)(var*, usize, gc*);
|
|
||||||
|
|
||||||
// module function stores in tables with this type, end with {nullptr,nullptr}
|
|
||||||
struct module_func_info {
|
|
||||||
const char* name;
|
|
||||||
module_func fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
// module function "get" type
|
|
||||||
typedef module_func_info* (*get_func_ptr)(ghost_register_table*);
|
|
226
nasal_import.h
226
nasal_import.h
|
@ -1,226 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
|
||||||
#else
|
|
||||||
#define _CRT_SECURE_NO_DEPRECATE 1
|
|
||||||
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
|
||||||
#include <io.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define F_OK 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "nasal.h"
|
|
||||||
#include "nasal_ast.h"
|
|
||||||
#include "nasal_lexer.h"
|
|
||||||
#include "nasal_parse.h"
|
|
||||||
|
|
||||||
class linker{
|
|
||||||
private:
|
|
||||||
bool show_path;
|
|
||||||
bool lib_loaded;
|
|
||||||
error& err;
|
|
||||||
std::vector<string> files;
|
|
||||||
std::vector<string> envpath;
|
|
||||||
|
|
||||||
bool imptchk(const ast&);
|
|
||||||
bool exist(const string&);
|
|
||||||
void link(ast&, ast&&);
|
|
||||||
string path(const ast&);
|
|
||||||
string findf(const string&);
|
|
||||||
ast fimpt(ast&);
|
|
||||||
ast libimpt();
|
|
||||||
ast load(ast&, u16);
|
|
||||||
public:
|
|
||||||
linker(error&);
|
|
||||||
const error& link(parse&, const string&, bool);
|
|
||||||
const std::vector<string>& filelist() const {return files;}
|
|
||||||
};
|
|
||||||
|
|
||||||
linker::linker(error& e): show_path(false), lib_loaded(false), err(e) {
|
|
||||||
char sep=is_windows()? ';':':';
|
|
||||||
string PATH=getenv("PATH");
|
|
||||||
usize last=0;
|
|
||||||
usize pos=PATH.find(sep, 0);
|
|
||||||
while(pos!=string::npos) {
|
|
||||||
string dirpath=PATH.substr(last, pos-last);
|
|
||||||
if (dirpath.length()) {
|
|
||||||
envpath.push_back(dirpath);
|
|
||||||
}
|
|
||||||
last=pos+1;
|
|
||||||
pos=PATH.find(sep, last);
|
|
||||||
}
|
|
||||||
if (last!=PATH.length()) {
|
|
||||||
envpath.push_back(PATH.substr(last));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string linker::path(const ast& node) {
|
|
||||||
if (node[1].type()==ast_callf) {
|
|
||||||
return node[1][0].str();
|
|
||||||
}
|
|
||||||
string fpath=".";
|
|
||||||
for(usize i=1;i<node.size();++i) {
|
|
||||||
fpath+=(is_windows()? "\\":"/")+node[i].str();
|
|
||||||
}
|
|
||||||
return fpath+".nas";
|
|
||||||
}
|
|
||||||
|
|
||||||
string linker::findf(const string& fname) {
|
|
||||||
// first add file name itself into the file path
|
|
||||||
std::vector<string> fpath={fname};
|
|
||||||
|
|
||||||
// generate search path from environ path
|
|
||||||
for(auto&p:envpath) {
|
|
||||||
fpath.push_back(p+(is_windows()? "\\":"/")+fname);
|
|
||||||
}
|
|
||||||
|
|
||||||
// search file
|
|
||||||
for(auto& i:fpath) {
|
|
||||||
if (access(i.c_str(), F_OK)!=-1) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we will find lib.nas in nasal std directory
|
|
||||||
if (fname=="lib.nas") {
|
|
||||||
return is_windows()? findf("stl\\lib.nas"):findf("stl/lib.nas");
|
|
||||||
}
|
|
||||||
if (!show_path) {
|
|
||||||
err.err("link", "cannot find file <"+fname+">");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
string paths="";
|
|
||||||
for(auto& i:fpath) {
|
|
||||||
paths+=" "+i+"\n";
|
|
||||||
}
|
|
||||||
err.err("link", "cannot find file <"+fname+"> in these paths:\n"+paths);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool linker::imptchk(const ast& node) {
|
|
||||||
// only these two kinds of node can be recognized as 'import':
|
|
||||||
/*
|
|
||||||
call
|
|
||||||
|_id:import
|
|
||||||
|_callh:stl
|
|
||||||
|_callh:file
|
|
||||||
*/
|
|
||||||
if (node.type()==ast_call && node[0].str()=="import" && node.size()>=2 && node[1].type()==ast_callh) {
|
|
||||||
for(usize i=1;i<node.size();++i) {
|
|
||||||
if (node[i].type()!=ast_callh) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
call
|
|
||||||
|_id:import
|
|
||||||
|_call_func
|
|
||||||
|_string:'filename'
|
|
||||||
*/
|
|
||||||
return (
|
|
||||||
node.type()==ast_call &&
|
|
||||||
node[0].str()=="import" &&
|
|
||||||
node.size()==2 &&
|
|
||||||
node[1].type()==ast_callf &&
|
|
||||||
node[1].size()==1 &&
|
|
||||||
node[1][0].type()==ast_str
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool linker::exist(const string& file) {
|
|
||||||
// avoid importing the same file
|
|
||||||
for(auto& fname:files) {
|
|
||||||
if (file==fname) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
files.push_back(file);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void linker::link(ast& root, ast&& add_root) {
|
|
||||||
// add children of add_root to the back of root
|
|
||||||
for(auto& i:add_root.child()) {
|
|
||||||
root.add(std::move(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ast linker::fimpt(ast& node) {
|
|
||||||
lexer lex(err);
|
|
||||||
parse par(err);
|
|
||||||
// get filename and set node to ast_null
|
|
||||||
string filename=path(node);
|
|
||||||
node.clear();
|
|
||||||
|
|
||||||
// avoid infinite loading loop
|
|
||||||
filename=findf(filename);
|
|
||||||
if (!filename.length() || exist(filename)) {
|
|
||||||
return {{0, 0, 0, 0, filename}, ast_root};
|
|
||||||
}
|
|
||||||
|
|
||||||
// start importing...
|
|
||||||
lex.scan(filename);
|
|
||||||
par.compile(lex);
|
|
||||||
ast tmp=std::move(par.tree());
|
|
||||||
|
|
||||||
// check if tmp has 'import'
|
|
||||||
return load(tmp, files.size()-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ast linker::libimpt() {
|
|
||||||
lexer lex(err);
|
|
||||||
parse par(err);
|
|
||||||
string filename=findf("lib.nas");
|
|
||||||
if (!filename.length()) {
|
|
||||||
return {{0, 0, 0, 0, filename}, ast_root};
|
|
||||||
}
|
|
||||||
|
|
||||||
// avoid infinite loading loop
|
|
||||||
if (exist(filename)) {
|
|
||||||
return {{0, 0, 0, 0, filename}, ast_root};
|
|
||||||
}
|
|
||||||
|
|
||||||
// start importing...
|
|
||||||
lex.scan(filename);
|
|
||||||
par.compile(lex);
|
|
||||||
ast tmp=std::move(par.tree());
|
|
||||||
|
|
||||||
// check if tmp has 'import'
|
|
||||||
return load(tmp, files.size()-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ast linker::load(ast& root, u16 fileindex) {
|
|
||||||
ast tree({0, 0, 0, 0, files[fileindex]}, ast_root);
|
|
||||||
if (!lib_loaded) {
|
|
||||||
link(tree, libimpt());
|
|
||||||
lib_loaded=true;
|
|
||||||
}
|
|
||||||
for(auto& i:root.child()) {
|
|
||||||
if (imptchk(i)) {
|
|
||||||
link(tree, fimpt(i));
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add root to the back of tree
|
|
||||||
ast file_head({0, 0, 0, 0, files[fileindex]}, ast_file);
|
|
||||||
file_head.set_num(fileindex);
|
|
||||||
tree.add(std::move(file_head));
|
|
||||||
link(tree, std::move(root));
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
const error& linker::link(parse& parse, const string& self, bool spath=false) {
|
|
||||||
show_path=spath;
|
|
||||||
// initializing
|
|
||||||
files={self};
|
|
||||||
// scan root and import files,then generate a new ast and return to import_ast
|
|
||||||
// the main file's index is 0
|
|
||||||
parse.tree()=load(parse.tree(), 0);
|
|
||||||
return err;
|
|
||||||
}
|
|
233
nasal_opcode.h
233
nasal_opcode.h
|
@ -1,233 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "nasal.h"
|
|
||||||
#include "nasal_builtin.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
enum op_code_type:u8 {
|
|
||||||
op_exit, // stop the virtual machine
|
|
||||||
op_intg, // global scope size, set stack top
|
|
||||||
op_intl, // local scope size
|
|
||||||
op_loadg, // load global value
|
|
||||||
op_loadl, // load local value
|
|
||||||
op_loadu, // load upvalue
|
|
||||||
op_pnum, // push constant number to the stack
|
|
||||||
op_pnil, // push constant nil to the stack
|
|
||||||
op_pstr, // push constant string to the stack
|
|
||||||
op_newv, // push new vector with initial values from stack
|
|
||||||
op_newh, // push new hash to the stack
|
|
||||||
op_newf, // push new function to the stack
|
|
||||||
op_happ, // hash append
|
|
||||||
op_para, // normal parameter
|
|
||||||
op_deft, // default parameter
|
|
||||||
op_dyn, // dynamic parameter
|
|
||||||
op_lnot, // ! logical negation
|
|
||||||
op_usub, // - negation
|
|
||||||
op_bnot, // ~ bitwise not static_cast<i32>
|
|
||||||
op_btor, // | bitwise or
|
|
||||||
op_btxor, // ^ bitwise xor
|
|
||||||
op_btand, // & bitwise and
|
|
||||||
op_add, // +
|
|
||||||
op_sub, // -
|
|
||||||
op_mul, // *
|
|
||||||
op_div, // /
|
|
||||||
op_lnk, // ~
|
|
||||||
op_addc, // + const
|
|
||||||
op_subc, // - const
|
|
||||||
op_mulc, // * const
|
|
||||||
op_divc, // / const
|
|
||||||
op_lnkc, // ~ const
|
|
||||||
op_addeq, // += maybe pop stack top
|
|
||||||
op_subeq, // -= maybe pop stack top
|
|
||||||
op_muleq, // *= maybe pop stack top
|
|
||||||
op_diveq, // /= maybe pop stack top
|
|
||||||
op_lnkeq, // ~= maybe pop stack top
|
|
||||||
op_btandeq,// &= maybe pop stack top
|
|
||||||
op_btoreq, // |= maybe pop stack top
|
|
||||||
op_btxoreq,// ^= maybe pop stack top
|
|
||||||
op_addeqc, // += const don't pop stack top
|
|
||||||
op_subeqc, // -= const don't pop stack top
|
|
||||||
op_muleqc, // *= const don't pop stack top
|
|
||||||
op_diveqc, // /= const don't pop stack top
|
|
||||||
op_lnkeqc, // ~= const don't pop stack top
|
|
||||||
op_addecp, // += const and pop stack top
|
|
||||||
op_subecp, // -= const and pop stack top
|
|
||||||
op_mulecp, // *= const and pop stack top
|
|
||||||
op_divecp, // /= const and pop stack top
|
|
||||||
op_lnkecp, // ~= concat const string and pop stack top
|
|
||||||
op_meq, // = maybe pop stack top
|
|
||||||
op_eq, // ==
|
|
||||||
op_neq, // !=
|
|
||||||
op_less, // <
|
|
||||||
op_leq, // <=
|
|
||||||
op_grt, // >
|
|
||||||
op_geq, // >=
|
|
||||||
op_lessc, // < const
|
|
||||||
op_leqc, // <= const
|
|
||||||
op_grtc, // > const
|
|
||||||
op_geqc, // >= const
|
|
||||||
op_pop, // pop a value out of stack top
|
|
||||||
op_jmp, // jump absolute address with no condition
|
|
||||||
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
|
|
||||||
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
|
|
||||||
op_cnt, // add counter for forindex/foreach
|
|
||||||
op_findex, // index counter on the top of forindex_stack plus 1
|
|
||||||
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
|
|
||||||
op_callg, // get value in global scope
|
|
||||||
op_calll, // get value in local scope
|
|
||||||
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
|
|
||||||
op_callv, // call vec[index]
|
|
||||||
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
|
|
||||||
op_callh, // call hash.label
|
|
||||||
op_callfv, // call function(vector as parameters)
|
|
||||||
op_callfh, // call function(hash as parameters)
|
|
||||||
op_callb, // call builtin-function
|
|
||||||
op_slcbeg, // begin of slice like: vec[1,2,3:6,0,-1]
|
|
||||||
op_slcend, // end of slice
|
|
||||||
op_slc, // slice like vec[1]
|
|
||||||
op_slc2, // slice like vec[nil:10]
|
|
||||||
op_mcallg, // get memory space of value in global scope
|
|
||||||
op_mcalll, // get memory space of value in local scope
|
|
||||||
op_mupval, // get memory space of value in closure
|
|
||||||
op_mcallv, // get memory space of vec[index]
|
|
||||||
op_mcallh, // get memory space of hash.label
|
|
||||||
op_ret // return
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* opname[]={
|
|
||||||
"exit ","intg ","intl ","loadg ",
|
|
||||||
"loadl ","loadu ","pnum ","pnil ",
|
|
||||||
"pstr ","newv ","newh ","newf ",
|
|
||||||
"happ ","para ","def ","dyn ",
|
|
||||||
"lnot ","usub ","bnot ","btor ",
|
|
||||||
"btxor ","btand ","add ","sub ",
|
|
||||||
"mult ","div ","lnk ","addc ",
|
|
||||||
"subc ","multc ","divc ","lnkc ",
|
|
||||||
"addeq ","subeq ","muleq ","diveq ",
|
|
||||||
"lnkeq ","bandeq","boreq ","bxoreq",
|
|
||||||
"addeqc","subeqc","muleqc","diveqc",
|
|
||||||
"lnkeqc","addecp","subecp","mulecp",
|
|
||||||
"divecp","lnkecp","meq ","eq ",
|
|
||||||
"neq ","less ","leq ","grt ",
|
|
||||||
"geq ","lessc ","leqc ","grtc ",
|
|
||||||
"geqc ","pop ","jmp ","jt ",
|
|
||||||
"jf ","cnt ","findx ","feach ",
|
|
||||||
"callg ","calll ","upval ","callv ",
|
|
||||||
"callvi","callh ","callfv","callfh",
|
|
||||||
"callb ","slcbeg","slcend","slc ",
|
|
||||||
"slc2 ","mcallg","mcalll","mupval",
|
|
||||||
"mcallv","mcallh","ret "
|
|
||||||
};
|
|
||||||
|
|
||||||
struct opcode {
|
|
||||||
u8 op; // opcode
|
|
||||||
u16 fidx; // source code file index
|
|
||||||
u32 num; // immediate num
|
|
||||||
u32 line; // location line of source code
|
|
||||||
opcode() = default;
|
|
||||||
opcode(const opcode&) = default;
|
|
||||||
opcode& operator=(const opcode&) = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class codestream {
|
|
||||||
private:
|
|
||||||
opcode code;
|
|
||||||
const u32 index;
|
|
||||||
static const f64* nums;
|
|
||||||
static const string* strs;
|
|
||||||
static const string* files;
|
|
||||||
public:
|
|
||||||
codestream(const opcode& c, const u32 i): code(c), index(i) {}
|
|
||||||
static void set(const f64*, const string*, const string*);
|
|
||||||
void dump(std::ostream&) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
const f64* codestream::nums=nullptr;
|
|
||||||
const string* codestream::strs=nullptr;
|
|
||||||
const string* codestream::files=nullptr;
|
|
||||||
|
|
||||||
void codestream::set(
|
|
||||||
const f64* numbuff,
|
|
||||||
const string* strbuff,
|
|
||||||
const string* filelist=nullptr
|
|
||||||
) {
|
|
||||||
nums=numbuff;
|
|
||||||
strs=strbuff;
|
|
||||||
files=filelist;
|
|
||||||
}
|
|
||||||
|
|
||||||
void codestream::dump(std::ostream& out) const {
|
|
||||||
using std::setw;
|
|
||||||
using std::setfill;
|
|
||||||
using std::hex;
|
|
||||||
using std::dec;
|
|
||||||
auto op=code.op;
|
|
||||||
auto num=code.num;
|
|
||||||
out<<hex<<"0x"
|
|
||||||
<<setw(6)<<setfill('0')<<index<<" "
|
|
||||||
<<setw(2)<<setfill('0')<<(u32)op<<" "
|
|
||||||
<<setw(2)<<setfill('0')<<((num>>16)&0xff)<<" "
|
|
||||||
<<setw(2)<<setfill('0')<<((num>>8)&0xff)<<" "
|
|
||||||
<<setw(2)<<setfill('0')<<(num&0xff)<<" "
|
|
||||||
<<opname[op]<<" "<<dec;
|
|
||||||
switch(op) {
|
|
||||||
case op_addeq: case op_subeq:
|
|
||||||
case op_muleq: case op_diveq:
|
|
||||||
case op_lnkeq: case op_meq:
|
|
||||||
case op_btandeq: case op_btoreq:
|
|
||||||
case op_btxoreq:
|
|
||||||
out<<hex<<"0x"<<num<<dec<<" sp-"<<num;break;
|
|
||||||
case op_addeqc: case op_subeqc:
|
|
||||||
case op_muleqc:case op_diveqc:
|
|
||||||
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
|
|
||||||
case op_lnkeqc:
|
|
||||||
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
|
|
||||||
case op_addecp: case op_subecp:
|
|
||||||
case op_mulecp: case op_divecp:
|
|
||||||
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<") sp-1";break;
|
|
||||||
case op_lnkecp:
|
|
||||||
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<") sp-1";break;
|
|
||||||
case op_addc: case op_subc:
|
|
||||||
case op_mulc: case op_divc:
|
|
||||||
case op_lessc: case op_leqc:
|
|
||||||
case op_grtc: case op_geqc:
|
|
||||||
case op_pnum:
|
|
||||||
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
|
|
||||||
case op_callvi: case op_newv:
|
|
||||||
case op_callfv: case op_intg:
|
|
||||||
case op_intl: case op_findex:
|
|
||||||
case op_feach: case op_newf:
|
|
||||||
case op_jmp: case op_jt:
|
|
||||||
case op_jf: case op_callg:
|
|
||||||
case op_mcallg: case op_loadg:
|
|
||||||
case op_calll: case op_mcalll:
|
|
||||||
case op_loadl:
|
|
||||||
out<<hex<<"0x"<<num<<dec;break;
|
|
||||||
case op_callb:
|
|
||||||
out<<hex<<"0x"<<num<<" <"<<builtin[num].name
|
|
||||||
<<"@0x"<<(u64)builtin[num].func<<dec<<">";break;
|
|
||||||
case op_upval: case op_mupval:
|
|
||||||
case op_loadu:
|
|
||||||
out<<hex<<"0x"<<((num>>16)&0xffff)
|
|
||||||
<<"[0x"<<(num&0xffff)<<"]"<<dec;break;
|
|
||||||
case op_happ: case op_pstr:
|
|
||||||
case op_lnkc: case op_callh:
|
|
||||||
case op_mcallh: case op_para:
|
|
||||||
case op_deft: case op_dyn:
|
|
||||||
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
|
|
||||||
default:
|
|
||||||
if (files) {
|
|
||||||
out<<hex<<"0x"<<num<<dec;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (files) {
|
|
||||||
out<<"("<<files[code.fidx]<<":"<<code.line<<")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, const codestream& ins) {
|
|
||||||
ins.dump(out);
|
|
||||||
return out;
|
|
||||||
}
|
|
75
nasal_opt.h
75
nasal_opt.h
|
@ -1,75 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "nasal_ast.h"
|
|
||||||
|
|
||||||
void const_str(ast& root) {
|
|
||||||
auto& vec=root.child();
|
|
||||||
root.set_str(vec[0].str()+vec[1].str());
|
|
||||||
root.child().clear();
|
|
||||||
root.set_type(ast_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void const_num(ast& root) {
|
|
||||||
auto& vec=root.child();
|
|
||||||
f64 res=0;
|
|
||||||
switch(root.type()) {
|
|
||||||
case ast_add: res=vec[0].num()+vec[1].num(); break;
|
|
||||||
case ast_sub: res=vec[0].num()-vec[1].num(); break;
|
|
||||||
case ast_mult: res=vec[0].num()*vec[1].num(); break;
|
|
||||||
case ast_div: res=vec[0].num()/vec[1].num(); break;
|
|
||||||
case ast_less: res=vec[0].num()<vec[1].num(); break;
|
|
||||||
case ast_leq: res=vec[0].num()<=vec[1].num();break;
|
|
||||||
case ast_grt: res=vec[0].num()>vec[1].num(); break;
|
|
||||||
case ast_geq: res=vec[0].num()>=vec[1].num();break;
|
|
||||||
case ast_bitor: res=i32(vec[0].num())|i32(vec[1].num()); break;
|
|
||||||
case ast_bitxor: res=i32(vec[0].num())^i32(vec[1].num()); break;
|
|
||||||
case ast_bitand: res=i32(vec[0].num())&i32(vec[1].num()); break;
|
|
||||||
}
|
|
||||||
// inf and nan will cause number hashmap error in codegen
|
|
||||||
if (std::isinf(res) || std::isnan(res)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
root.set_num(res);
|
|
||||||
root.child().clear();
|
|
||||||
root.set_type(ast_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
void calc_const(ast& root) {
|
|
||||||
auto& vec=root.child();
|
|
||||||
for(auto& i:vec) {
|
|
||||||
calc_const(i);
|
|
||||||
}
|
|
||||||
if (vec.size()==1 && root.type()==ast_neg && vec[0].type()==ast_num) {
|
|
||||||
f64 res=-vec[0].num();
|
|
||||||
root.set_num(res);
|
|
||||||
root.child().clear();
|
|
||||||
root.set_type(ast_num);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (vec.size()!=2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (root.type()!=ast_add && root.type()!=ast_sub &&
|
|
||||||
root.type()!=ast_mult && root.type()!=ast_div &&
|
|
||||||
root.type()!=ast_link && root.type()!=ast_less &&
|
|
||||||
root.type()!=ast_leq && root.type()!=ast_grt &&
|
|
||||||
root.type()!=ast_geq && root.type()!=ast_bitor &&
|
|
||||||
root.type()!=ast_bitxor && root.type()!=ast_bitand) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (root.type()==ast_link &&
|
|
||||||
vec[0].type()==ast_str && vec[1].type()==ast_str) {
|
|
||||||
const_str(root);
|
|
||||||
} else if (root.type()!=ast_link &&
|
|
||||||
vec[0].type()==ast_num && vec[1].type()==ast_num) {
|
|
||||||
const_num(root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void optimize(ast& root) {
|
|
||||||
for(auto& i:root.child()) {
|
|
||||||
calc_const(i);
|
|
||||||
}
|
|
||||||
}
|
|
1111
nasal_parse.h
1111
nasal_parse.h
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,473 @@
|
||||||
|
#include "ast_dumper.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
bool ast_dumper::visit_file_info(file_info* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "file \"" << node->get_file_name() << "\"";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_null_expr(null_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "null" << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_nil_expr(nil_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "nil" << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_number_literal(number_literal* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "number " << node->get_number();
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_string_literal(string_literal* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "string \"" << rawstr(node->get_content()) << "\"";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_identifier(identifier* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "identifier " << node->get_name();
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_bool_literal(bool_literal* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "bool " << node->get_flag();
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_vector_expr(vector_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "vector";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_elements()) {
|
||||||
|
if (i==node->get_elements().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_hash_expr(hash_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "hash";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_members()) {
|
||||||
|
if (i==node->get_members().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_hash_pair(hash_pair* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "pair " << node->get_name();
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
if (node->get_value()) {
|
||||||
|
push_indent();
|
||||||
|
set_last();
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_function(function* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "function";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_parameter_list()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
set_last();
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_code_block(code_block* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "block";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_expressions()) {
|
||||||
|
if (i==node->get_expressions().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_parameter(parameter* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "parameter " << node->get_parameter_name();
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
if (node->get_default_value()) {
|
||||||
|
push_indent();
|
||||||
|
set_last();
|
||||||
|
node->get_default_value()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_ternary_operator(ternary_operator* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "ternary_operator";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
node->get_left()->accept(this);
|
||||||
|
set_last();
|
||||||
|
node->get_right()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_binary_operator(binary_operator* node) {
|
||||||
|
if (node->get_optimized_number()) {
|
||||||
|
node->get_optimized_number()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node->get_optimized_string()) {
|
||||||
|
node->get_optimized_string()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "binary_operator ";
|
||||||
|
switch(node->get_operator_type()) {
|
||||||
|
case binary_operator::binary_type::add: std::cout << "+"; break;
|
||||||
|
case binary_operator::binary_type::sub: std::cout << "-"; break;
|
||||||
|
case binary_operator::binary_type::mult: std::cout << "*"; break;
|
||||||
|
case binary_operator::binary_type::div: std::cout << "/"; break;
|
||||||
|
case binary_operator::binary_type::concat: std::cout << "~"; break;
|
||||||
|
case binary_operator::binary_type::bitwise_and: std::cout << "&"; break;
|
||||||
|
case binary_operator::binary_type::bitwise_or: std::cout << "|"; break;
|
||||||
|
case binary_operator::binary_type::bitwise_xor: std::cout << "^"; break;
|
||||||
|
case binary_operator::binary_type::cmpeq: std::cout << "=="; break;
|
||||||
|
case binary_operator::binary_type::cmpneq: std::cout << "!="; break;
|
||||||
|
case binary_operator::binary_type::grt: std::cout << ">"; break;
|
||||||
|
case binary_operator::binary_type::geq: std::cout << ">="; break;
|
||||||
|
case binary_operator::binary_type::less: std::cout << "<"; break;
|
||||||
|
case binary_operator::binary_type::leq: std::cout << "<="; break;
|
||||||
|
case binary_operator::binary_type::condition_and: std::cout << "and"; break;
|
||||||
|
case binary_operator::binary_type::condition_or: std::cout << "or"; break;
|
||||||
|
}
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
node->get_left()->accept(this);
|
||||||
|
set_last();
|
||||||
|
node->get_right()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_unary_operator(unary_operator* node) {
|
||||||
|
if (node->get_optimized_number()) {
|
||||||
|
node->get_optimized_number()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "unary_operator ";
|
||||||
|
switch(node->get_operator_type()) {
|
||||||
|
case unary_operator::unary_type::negative: std::cout << "-"; break;
|
||||||
|
case unary_operator::unary_type::logical_not: std::cout << "!"; break;
|
||||||
|
case unary_operator::unary_type::bitwise_not: std::cout << "~"; break;
|
||||||
|
}
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
set_last();
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_call_expr(call_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "call_expr";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
if (!node->get_calls().size()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
node->get_first()->accept(this);
|
||||||
|
for(auto i : node->get_calls()) {
|
||||||
|
if (i==node->get_calls().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_call_hash(call_hash* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "call_hash " << node->get_field();
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_call_vector(call_vector* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "call_vector";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_slices()) {
|
||||||
|
if (i==node->get_slices().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_call_function(call_function* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "call_function";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_argument()) {
|
||||||
|
if (i==node->get_argument().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_slice_vector(slice_vector* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "slice";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
if (!node->get_end()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
node->get_begin()->accept(this);
|
||||||
|
if (node->get_end()) {
|
||||||
|
set_last();
|
||||||
|
node->get_end()->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_definition_expr(definition_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "definition";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
if (node->get_variable_name()) {
|
||||||
|
node->get_variable_name()->accept(this);
|
||||||
|
} else {
|
||||||
|
node->get_variables()->accept(this);
|
||||||
|
}
|
||||||
|
set_last();
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "assignment";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
node->get_left()->accept(this);
|
||||||
|
set_last();
|
||||||
|
node->get_right()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "multiple_definition";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_variables()) {
|
||||||
|
if (i==node->get_variables().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_tuple_expr(tuple_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "tuple";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
for(auto i : node->get_elements()) {
|
||||||
|
if (i==node->get_elements().back()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_multi_assign(multi_assign* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "multiple_assignment";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
node->get_tuple()->accept(this);
|
||||||
|
set_last();
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_while_expr(while_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "while";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
set_last();
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_for_expr(for_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "for";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
node->get_initial()->accept(this);
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
node->get_step()->accept(this);
|
||||||
|
set_last();
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_iter_expr(iter_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "iterator";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
set_last();
|
||||||
|
if (node->get_name()) {
|
||||||
|
node->get_name()->accept(this);
|
||||||
|
} else {
|
||||||
|
node->get_call()->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_forei_expr(forei_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
if (node->get_loop_type()==forei_expr::forei_loop_type::foreach) {
|
||||||
|
std::cout << "foreach";
|
||||||
|
} else {
|
||||||
|
std::cout << "forindex";
|
||||||
|
}
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
node->get_iterator()->accept(this);
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
set_last();
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_condition_expr(condition_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "condition";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
if (!node->get_elsif_stataments().size() &&
|
||||||
|
!node->get_else_statement()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
node->get_if_statement()->accept(this);
|
||||||
|
for(auto i : node->get_elsif_stataments()) {
|
||||||
|
if (i==node->get_elsif_stataments().back() &&
|
||||||
|
!node->get_else_statement()) {
|
||||||
|
set_last();
|
||||||
|
}
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
if (node->get_else_statement()) {
|
||||||
|
set_last();
|
||||||
|
node->get_else_statement()->accept(this);
|
||||||
|
}
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_if_expr(if_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "if";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
push_indent();
|
||||||
|
if (node->get_condition()) {
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
}
|
||||||
|
set_last();
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_continue_expr(continue_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "continue";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_break_expr(break_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "break";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_dumper::visit_return_expr(return_expr* node) {
|
||||||
|
dump_indent();
|
||||||
|
std::cout << "return";
|
||||||
|
std::cout << format_location(node->get_location());
|
||||||
|
if (node->get_value()) {
|
||||||
|
push_indent();
|
||||||
|
set_last();
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
pop_indent();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ast_visitor.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
class ast_dumper:public ast_visitor {
|
||||||
|
private:
|
||||||
|
std::vector<std::string> indent;
|
||||||
|
void push_indent() {
|
||||||
|
if (indent.size()) {
|
||||||
|
if (indent.back()=="|--") {
|
||||||
|
indent.back() = "| ";
|
||||||
|
} else if (indent.back()=="+--") {
|
||||||
|
indent.back() = " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indent.push_back("|--");
|
||||||
|
}
|
||||||
|
void pop_indent() {indent.pop_back();}
|
||||||
|
void set_last() {indent.back() = "+--";}
|
||||||
|
void dump_indent() {
|
||||||
|
if (indent.size() && indent.back()=="| ") {
|
||||||
|
indent.back() = "|--";
|
||||||
|
}
|
||||||
|
for(const auto& i : indent) {
|
||||||
|
std::cout << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string format_location(const span& location) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << " -> ";
|
||||||
|
ss << location.file << ":";
|
||||||
|
ss << location.begin_line << ":" << location.begin_column + 1;
|
||||||
|
ss << "\n";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool visit_file_info(file_info*) override;
|
||||||
|
bool visit_null_expr(null_expr*) override;
|
||||||
|
bool visit_nil_expr(nil_expr*) override;
|
||||||
|
bool visit_number_literal(number_literal*) override;
|
||||||
|
bool visit_string_literal(string_literal*) override;
|
||||||
|
bool visit_identifier(identifier*) override;
|
||||||
|
bool visit_bool_literal(bool_literal*) override;
|
||||||
|
bool visit_vector_expr(vector_expr*) override;
|
||||||
|
bool visit_hash_expr(hash_expr*) override;
|
||||||
|
bool visit_hash_pair(hash_pair*) override;
|
||||||
|
bool visit_function(function*) override;
|
||||||
|
bool visit_code_block(code_block*) override;
|
||||||
|
bool visit_parameter(parameter*) override;
|
||||||
|
bool visit_ternary_operator(ternary_operator*) override;
|
||||||
|
bool visit_binary_operator(binary_operator*) override;
|
||||||
|
bool visit_unary_operator(unary_operator*) override;
|
||||||
|
bool visit_call_expr(call_expr*) override;
|
||||||
|
bool visit_call_hash(call_hash*) override;
|
||||||
|
bool visit_call_vector(call_vector*) override;
|
||||||
|
bool visit_call_function(call_function*) override;
|
||||||
|
bool visit_slice_vector(slice_vector*) override;
|
||||||
|
bool visit_definition_expr(definition_expr*) override;
|
||||||
|
bool visit_assignment_expr(assignment_expr*) override;
|
||||||
|
bool visit_multi_identifier(multi_identifier*) override;
|
||||||
|
bool visit_tuple_expr(tuple_expr*) override;
|
||||||
|
bool visit_multi_assign(multi_assign*) override;
|
||||||
|
bool visit_while_expr(while_expr*) override;
|
||||||
|
bool visit_for_expr(for_expr*) override;
|
||||||
|
bool visit_iter_expr(iter_expr*) override;
|
||||||
|
bool visit_forei_expr(forei_expr*) override;
|
||||||
|
bool visit_condition_expr(condition_expr*) override;
|
||||||
|
bool visit_if_expr(if_expr*) override;
|
||||||
|
bool visit_continue_expr(continue_expr*) override;
|
||||||
|
bool visit_break_expr(break_expr*) override;
|
||||||
|
bool visit_return_expr(return_expr*) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void dump(code_block* root) {
|
||||||
|
root->accept(this);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,229 @@
|
||||||
|
#include "ast_visitor.h"
|
||||||
|
|
||||||
|
bool ast_visitor::visit_expr(expr* node) {
|
||||||
|
node->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_file_info(file_info* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_null_expr(null_expr* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_nil_expr(nil_expr* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_number_literal(number_literal* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_string_literal(string_literal* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_identifier(identifier* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_bool_literal(bool_literal* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_vector_expr(vector_expr* node) {
|
||||||
|
for(auto i : node->get_elements()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_hash_expr(hash_expr* node) {
|
||||||
|
for(auto i : node->get_members()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_hash_pair(hash_pair* node) {
|
||||||
|
if (node->get_value()) {
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_function(function* node) {
|
||||||
|
for(auto i : node->get_parameter_list()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_code_block(code_block* node) {
|
||||||
|
for(auto i : node->get_expressions()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_parameter(parameter* node) {
|
||||||
|
if (node->get_default_value()) {
|
||||||
|
node->get_default_value()->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_ternary_operator(ternary_operator* node) {
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
node->get_left()->accept(this);
|
||||||
|
node->get_right()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_binary_operator(binary_operator* node) {
|
||||||
|
node->get_left()->accept(this);
|
||||||
|
node->get_right()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_unary_operator(unary_operator* node) {
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_call_expr(call_expr* node) {
|
||||||
|
node->get_first()->accept(this);
|
||||||
|
for(auto i : node->get_calls()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_call_hash(call_hash* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_call_vector(call_vector* node) {
|
||||||
|
for(auto i : node->get_slices()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_call_function(call_function* node) {
|
||||||
|
for(auto i : node->get_argument()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_slice_vector(slice_vector* node) {
|
||||||
|
node->get_begin()->accept(this);
|
||||||
|
if (node->get_end()) {
|
||||||
|
node->get_end()->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_definition_expr(definition_expr* node) {
|
||||||
|
if (node->get_variable_name()) {
|
||||||
|
node->get_variable_name()->accept(this);
|
||||||
|
} else {
|
||||||
|
node->get_variables()->accept(this);
|
||||||
|
}
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_assignment_expr(assignment_expr* node) {
|
||||||
|
node->get_left()->accept(this);
|
||||||
|
node->get_right()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_multi_identifier(multi_identifier* node) {
|
||||||
|
for(auto i : node->get_variables()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_tuple_expr(tuple_expr* node) {
|
||||||
|
for(auto i : node->get_elements()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_multi_assign(multi_assign* node) {
|
||||||
|
node->get_tuple()->accept(this);
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_while_expr(while_expr* node) {
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_for_expr(for_expr* node) {
|
||||||
|
node->get_initial()->accept(this);
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
node->get_step()->accept(this);
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_iter_expr(iter_expr* node) {
|
||||||
|
if (node->get_name()) {
|
||||||
|
node->get_name()->accept(this);
|
||||||
|
} else {
|
||||||
|
node->get_call()->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_forei_expr(forei_expr* node) {
|
||||||
|
node->get_iterator()->accept(this);
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_condition_expr(condition_expr* node) {
|
||||||
|
node->get_if_statement()->accept(this);
|
||||||
|
for(auto i : node->get_elsif_stataments()) {
|
||||||
|
i->accept(this);
|
||||||
|
}
|
||||||
|
if (node->get_else_statement()) {
|
||||||
|
node->get_else_statement()->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_if_expr(if_expr* node) {
|
||||||
|
if (node->get_condition()) {
|
||||||
|
node->get_condition()->accept(this);
|
||||||
|
}
|
||||||
|
node->get_code_block()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_continue_expr(continue_expr* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_break_expr(break_expr* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ast_visitor::visit_return_expr(return_expr* node) {
|
||||||
|
if (node->get_value()) {
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
|
||||||
|
class ast_visitor {
|
||||||
|
public:
|
||||||
|
virtual bool visit_expr(expr*);
|
||||||
|
virtual bool visit_file_info(file_info*);
|
||||||
|
virtual bool visit_null_expr(null_expr*);
|
||||||
|
virtual bool visit_nil_expr(nil_expr*);
|
||||||
|
virtual bool visit_number_literal(number_literal*);
|
||||||
|
virtual bool visit_string_literal(string_literal*);
|
||||||
|
virtual bool visit_identifier(identifier*);
|
||||||
|
virtual bool visit_bool_literal(bool_literal*);
|
||||||
|
virtual bool visit_vector_expr(vector_expr*);
|
||||||
|
virtual bool visit_hash_expr(hash_expr*);
|
||||||
|
virtual bool visit_hash_pair(hash_pair*);
|
||||||
|
virtual bool visit_function(function*);
|
||||||
|
virtual bool visit_code_block(code_block*);
|
||||||
|
virtual bool visit_parameter(parameter*);
|
||||||
|
virtual bool visit_ternary_operator(ternary_operator*);
|
||||||
|
virtual bool visit_binary_operator(binary_operator*);
|
||||||
|
virtual bool visit_unary_operator(unary_operator*);
|
||||||
|
virtual bool visit_call_expr(call_expr*);
|
||||||
|
virtual bool visit_call_hash(call_hash*);
|
||||||
|
virtual bool visit_call_vector(call_vector*);
|
||||||
|
virtual bool visit_call_function(call_function*);
|
||||||
|
virtual bool visit_slice_vector(slice_vector*);
|
||||||
|
virtual bool visit_definition_expr(definition_expr*);
|
||||||
|
virtual bool visit_assignment_expr(assignment_expr*);
|
||||||
|
virtual bool visit_multi_identifier(multi_identifier*);
|
||||||
|
virtual bool visit_tuple_expr(tuple_expr*);
|
||||||
|
virtual bool visit_multi_assign(multi_assign*);
|
||||||
|
virtual bool visit_while_expr(while_expr*);
|
||||||
|
virtual bool visit_for_expr(for_expr*);
|
||||||
|
virtual bool visit_iter_expr(iter_expr*);
|
||||||
|
virtual bool visit_forei_expr(forei_expr*);
|
||||||
|
virtual bool visit_condition_expr(condition_expr*);
|
||||||
|
virtual bool visit_if_expr(if_expr*);
|
||||||
|
virtual bool visit_continue_expr(continue_expr*);
|
||||||
|
virtual bool visit_break_expr(break_expr*);
|
||||||
|
virtual bool visit_return_expr(return_expr*);
|
||||||
|
};
|
|
@ -0,0 +1,209 @@
|
||||||
|
#include "nasal.h"
|
||||||
|
#include "nasal_err.h"
|
||||||
|
#include "nasal_lexer.h"
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
#include "nasal_parse.h"
|
||||||
|
#include "nasal_import.h"
|
||||||
|
#include "ast_visitor.h"
|
||||||
|
#include "ast_dumper.h"
|
||||||
|
#include "symbol_finder.h"
|
||||||
|
#include "optimizer.h"
|
||||||
|
#include "nasal_codegen.h"
|
||||||
|
#include "nasal_vm.h"
|
||||||
|
#include "nasal_dbg.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <thread>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
const u32 VM_RAW_AST = 1;
|
||||||
|
const u32 VM_AST = 1<<1;
|
||||||
|
const u32 VM_CODE = 1<<2;
|
||||||
|
const u32 VM_TIME = 1<<3;
|
||||||
|
const u32 VM_EXEC = 1<<4;
|
||||||
|
const u32 VM_DETAIL = 1<<5;
|
||||||
|
const u32 VM_DEBUG = 1<<6;
|
||||||
|
|
||||||
|
std::ostream& help(std::ostream& out) {
|
||||||
|
out
|
||||||
|
<< " ,--#-,\n"
|
||||||
|
<< "<3 / \\____\\ <3\n"
|
||||||
|
<< " |_|__A_|\n"
|
||||||
|
#ifdef _WIN32
|
||||||
|
<< "use command <chcp 65001> to use unicode.\n"
|
||||||
|
#endif
|
||||||
|
<< "\nnasal <option>\n"
|
||||||
|
<< "option:\n"
|
||||||
|
<< " -h, --help | get help.\n"
|
||||||
|
<< " -v, --version | get version.\n"
|
||||||
|
<< "\nnasal [option] <file> [argv]\n"
|
||||||
|
<< "option:\n"
|
||||||
|
<< " -a, --ast | view ast after link/optimize process.\n"
|
||||||
|
<< " --raw-ast | view ast without after-processing.\n"
|
||||||
|
<< " -c, --code | view generated bytecode.\n"
|
||||||
|
<< " -e, --exec | execute directly.\n"
|
||||||
|
<< " -t, --time | show execute time.\n"
|
||||||
|
<< " -d, --detail | get detail info.\n"
|
||||||
|
<< " -dbg, --debug | debug mode.\n"
|
||||||
|
<< "file:\n"
|
||||||
|
<< " <filename> | execute file.\n"
|
||||||
|
<< "argv:\n"
|
||||||
|
<< " <args> | cmd arguments used in program.\n";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& logo(std::ostream& out) {
|
||||||
|
out
|
||||||
|
<< " __ _\n"
|
||||||
|
<< " /\\ \\ \\__ _ ___ __ _| |\n"
|
||||||
|
<< " / \\/ / _` / __|/ _` | |\n"
|
||||||
|
<< " / /\\ / (_| \\__ \\ (_| | |\n"
|
||||||
|
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||||
|
<< "ver : " << __nasver << " (" << __DATE__ << " " << __TIME__ << ")\n"
|
||||||
|
<< "std : c++ " << __cplusplus << "\n"
|
||||||
|
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
||||||
|
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||||
|
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||||
|
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||||
|
<< "input <nasal -h> to get help .\n";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& version(std::ostream& out) {
|
||||||
|
std::srand(std::time(nullptr));
|
||||||
|
f64 num = 0;
|
||||||
|
for(u32 i = 0; i<5; ++i) {
|
||||||
|
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||||
|
}
|
||||||
|
if (num<0.01) {
|
||||||
|
parse::easter_egg();
|
||||||
|
}
|
||||||
|
out << "version " << __nasver;
|
||||||
|
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
void err() {
|
||||||
|
std::cerr
|
||||||
|
<< "invalid argument(s).\n"
|
||||||
|
<< "use <nasal -h> to get help.\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(
|
||||||
|
const std::string& file,
|
||||||
|
const std::vector<std::string>& argv,
|
||||||
|
const u32 cmd) {
|
||||||
|
|
||||||
|
using clk = std::chrono::high_resolution_clock;
|
||||||
|
const auto den = clk::duration::period::den;
|
||||||
|
|
||||||
|
error err;
|
||||||
|
lexer lex(err);
|
||||||
|
parse parse(err);
|
||||||
|
linker ld(err);
|
||||||
|
codegen gen(err);
|
||||||
|
vm ctx;
|
||||||
|
|
||||||
|
// lexer scans file to get tokens
|
||||||
|
lex.scan(file).chkerr();
|
||||||
|
|
||||||
|
// parser gets lexer's token list to compile
|
||||||
|
parse.compile(lex).chkerr();
|
||||||
|
if (cmd&VM_RAW_AST) {
|
||||||
|
auto dumper = new ast_dumper;
|
||||||
|
dumper->dump(parse.tree());
|
||||||
|
delete dumper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// linker gets parser's ast and load import files to this ast
|
||||||
|
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
|
||||||
|
|
||||||
|
// optimizer does simple optimization on ast
|
||||||
|
auto opt = new optimizer;
|
||||||
|
opt->do_optimization(parse.tree());
|
||||||
|
delete opt;
|
||||||
|
if (cmd&VM_AST) {
|
||||||
|
auto dumper = new ast_dumper;
|
||||||
|
dumper->dump(parse.tree());
|
||||||
|
delete dumper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// code generator gets parser's ast and import file list to generate code
|
||||||
|
gen.compile(parse, ld).chkerr();
|
||||||
|
if (cmd&VM_CODE) {
|
||||||
|
gen.print();
|
||||||
|
}
|
||||||
|
|
||||||
|
// run
|
||||||
|
auto start=clk::now();
|
||||||
|
if (cmd&VM_DEBUG) {
|
||||||
|
dbg(err).run(gen, ld, argv);
|
||||||
|
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
||||||
|
ctx.run(gen, ld, argv, cmd&VM_DETAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get running time
|
||||||
|
if (cmd&VM_TIME) {
|
||||||
|
f64 tm=(clk::now()-start).count()*1.0/den;
|
||||||
|
std::clog << "process exited after " << tm << "s.\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 main(i32 argc, const char* argv[]) {
|
||||||
|
// output version info
|
||||||
|
if (argc<=1) {
|
||||||
|
std::clog << logo;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// run directly or show help
|
||||||
|
if (argc==2) {
|
||||||
|
std::string s(argv[1]);
|
||||||
|
if (s=="-h" || s=="--help") {
|
||||||
|
std::clog << help;
|
||||||
|
} else if (s=="-v" || s=="--version") {
|
||||||
|
std::clog << version;
|
||||||
|
} else if (s[0]!='-') {
|
||||||
|
execute(s, {}, VM_EXEC);
|
||||||
|
} else {
|
||||||
|
err();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute with arguments
|
||||||
|
const std::unordered_map<std::string,u32> cmdlst = {
|
||||||
|
{"--raw-ast", VM_RAW_AST},
|
||||||
|
{"--ast", VM_AST},
|
||||||
|
{"-a", VM_AST},
|
||||||
|
{"--code", VM_CODE},
|
||||||
|
{"-c", VM_CODE},
|
||||||
|
{"--exec", VM_EXEC},
|
||||||
|
{"-e", VM_EXEC},
|
||||||
|
{"--time", VM_TIME|VM_EXEC},
|
||||||
|
{"-t", VM_TIME|VM_EXEC},
|
||||||
|
{"--detail", VM_DETAIL|VM_EXEC},
|
||||||
|
{"-d", VM_DETAIL|VM_EXEC},
|
||||||
|
{"--debug", VM_DEBUG},
|
||||||
|
{"-dbg", VM_DEBUG}
|
||||||
|
};
|
||||||
|
u32 cmd = 0;
|
||||||
|
std::string filename = "";
|
||||||
|
std::vector<std::string> vm_argv;
|
||||||
|
for(i32 i = 1; i<argc; ++i) {
|
||||||
|
if (cmdlst.count(argv[i])) {
|
||||||
|
cmd |= cmdlst.at(argv[i]);
|
||||||
|
} else if (!filename.length()) {
|
||||||
|
filename = argv[i];
|
||||||
|
} else {
|
||||||
|
vm_argv.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!filename.length()) {
|
||||||
|
err();
|
||||||
|
}
|
||||||
|
execute(filename, vm_argv, cmd? cmd:VM_EXEC);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef __nasver
|
||||||
|
#define __nasver "11.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
bool is_windows();
|
||||||
|
bool is_linux();
|
||||||
|
bool is_macos();
|
||||||
|
bool is_x86();
|
||||||
|
bool is_amd64();
|
||||||
|
bool is_x86_64();
|
||||||
|
bool is_arm();
|
||||||
|
bool is_aarch64();
|
||||||
|
bool is_ia64();
|
||||||
|
bool is_powerpc();
|
||||||
|
bool is_superh();
|
||||||
|
|
||||||
|
using i32 = std::int32_t;
|
||||||
|
using i64 = std::int64_t;
|
||||||
|
using u8 = std::uint8_t;
|
||||||
|
using u16 = std::uint16_t;
|
||||||
|
using u32 = std::uint32_t;
|
||||||
|
using u64 = std::uint64_t;
|
||||||
|
using usize = std::size_t;
|
||||||
|
using f64 = double;
|
||||||
|
|
||||||
|
const u32 STACK_DEPTH=4096;
|
||||||
|
|
||||||
|
f64 hex2f(const char*);
|
||||||
|
f64 oct2f(const char*);
|
||||||
|
|
||||||
|
// we have the same reason not using atof here
|
||||||
|
// just as andy's interpreter does.
|
||||||
|
// it is not platform independent, and may have strange output.
|
||||||
|
// so we write a new function here to convert str to number manually.
|
||||||
|
// but this also makes 0.1+0.2==0.3,
|
||||||
|
// not another result that you may get in other languages.
|
||||||
|
f64 dec2f(const char*);
|
||||||
|
|
||||||
|
f64 str2num(const char*);
|
||||||
|
i32 utf8_hdchk(const char);
|
||||||
|
std::string chrhex(const char);
|
||||||
|
std::string rawstr(const std::string&, const usize maxlen=0);
|
||||||
|
|
||||||
|
#include "nasal_gc.h"
|
|
@ -0,0 +1,365 @@
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
#include "ast_visitor.h"
|
||||||
|
|
||||||
|
void expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_file_info(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void null_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_null_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nil_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_nil_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void number_literal::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_number_literal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void string_literal::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_string_literal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void identifier::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_identifier(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bool_literal::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_bool_literal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_expr::~vector_expr() {
|
||||||
|
for(auto i : elements) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vector_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_vector_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_expr::~hash_expr() {
|
||||||
|
for(auto i : members) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_hash_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_pair::~hash_pair() {
|
||||||
|
if (value) {
|
||||||
|
delete value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_pair::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_hash_pair(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function::~function() {
|
||||||
|
for(auto i : parameter_list) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
if (block) {
|
||||||
|
delete block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void function::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_function(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
code_block::~code_block() {
|
||||||
|
for(auto i : expressions) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void code_block::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_code_block(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter::~parameter() {
|
||||||
|
if (default_value) {
|
||||||
|
delete default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parameter::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_parameter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ternary_operator::~ternary_operator() {
|
||||||
|
if (condition) {
|
||||||
|
delete condition;
|
||||||
|
}
|
||||||
|
if (left) {
|
||||||
|
delete left;
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
delete right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ternary_operator::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_ternary_operator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
binary_operator::~binary_operator() {
|
||||||
|
if (left) {
|
||||||
|
delete left;
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
delete right;
|
||||||
|
}
|
||||||
|
if (optimized_const_number) {
|
||||||
|
delete optimized_const_number;
|
||||||
|
}
|
||||||
|
if (optimized_const_string) {
|
||||||
|
delete optimized_const_string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void binary_operator::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_binary_operator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unary_operator::~unary_operator() {
|
||||||
|
if (value) {
|
||||||
|
delete value;
|
||||||
|
}
|
||||||
|
if (optimized_number) {
|
||||||
|
delete optimized_number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unary_operator::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_unary_operator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
call_expr::~call_expr() {
|
||||||
|
if(first) {
|
||||||
|
delete first;
|
||||||
|
}
|
||||||
|
for(auto i : calls) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_call_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_hash::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_call_hash(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
call_vector::~call_vector() {
|
||||||
|
for(auto i : calls) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_vector::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_call_vector(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
call_function::~call_function() {
|
||||||
|
for(auto i : args) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_function::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_call_function(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice_vector::~slice_vector() {
|
||||||
|
if (begin) {
|
||||||
|
delete begin;
|
||||||
|
}
|
||||||
|
if (end) {
|
||||||
|
delete end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void slice_vector::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_slice_vector(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
definition_expr::~definition_expr() {
|
||||||
|
if (variable_name) {
|
||||||
|
delete variable_name;
|
||||||
|
}
|
||||||
|
if (variables) {
|
||||||
|
delete variables;
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
delete value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void definition_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_definition_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
assignment_expr::~assignment_expr() {
|
||||||
|
if (left) {
|
||||||
|
delete left;
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
delete right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assignment_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_assignment_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
multi_identifier::~multi_identifier() {
|
||||||
|
for(auto i : variables) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void multi_identifier::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_multi_identifier(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
tuple_expr::~tuple_expr() {
|
||||||
|
for(auto i : elements) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tuple_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_tuple_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
multi_assign::~multi_assign() {
|
||||||
|
if (tuple) {
|
||||||
|
delete tuple;
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
delete value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void multi_assign::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_multi_assign(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
while_expr::~while_expr() {
|
||||||
|
if (condition) {
|
||||||
|
delete condition;
|
||||||
|
}
|
||||||
|
if (block) {
|
||||||
|
delete block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void while_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_while_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
for_expr::~for_expr() {
|
||||||
|
if (initializing) {
|
||||||
|
delete initializing;
|
||||||
|
}
|
||||||
|
if (condition) {
|
||||||
|
delete condition;
|
||||||
|
}
|
||||||
|
if (step) {
|
||||||
|
delete step;
|
||||||
|
}
|
||||||
|
if (block) {
|
||||||
|
delete block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void for_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_for_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
iter_expr::~iter_expr() {
|
||||||
|
if (name) {
|
||||||
|
delete name;
|
||||||
|
}
|
||||||
|
if (call) {
|
||||||
|
delete call;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void iter_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_iter_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
forei_expr::~forei_expr() {
|
||||||
|
if (iterator) {
|
||||||
|
delete iterator;
|
||||||
|
}
|
||||||
|
if (vector_node) {
|
||||||
|
delete vector_node;
|
||||||
|
}
|
||||||
|
if (block) {
|
||||||
|
delete block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void forei_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_forei_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
condition_expr::~condition_expr() {
|
||||||
|
if (if_stmt) {
|
||||||
|
delete if_stmt;
|
||||||
|
}
|
||||||
|
for(auto i : elsif_stmt) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
if (else_stmt) {
|
||||||
|
delete else_stmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void condition_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_condition_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if_expr::~if_expr() {
|
||||||
|
if (condition) {
|
||||||
|
delete condition;
|
||||||
|
}
|
||||||
|
if (block) {
|
||||||
|
delete block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void if_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_if_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void continue_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_continue_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void break_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_break_expr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return_expr::~return_expr() {
|
||||||
|
if (value) {
|
||||||
|
delete value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_expr::accept(ast_visitor* visitor) {
|
||||||
|
visitor->visit_return_expr(this);
|
||||||
|
}
|
|
@ -0,0 +1,674 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
#include "nasal_err.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
enum class expr_type:u32 {
|
||||||
|
ast_null=0, // null node
|
||||||
|
ast_file_info, // stores file info
|
||||||
|
ast_block, // code block
|
||||||
|
ast_nil, // nil keyword
|
||||||
|
ast_num, // number, basic value type
|
||||||
|
ast_str, // string, basic value type
|
||||||
|
ast_id, // identifier
|
||||||
|
ast_bool, // bools
|
||||||
|
ast_func, // func keyword
|
||||||
|
ast_hash, // hash, basic value type
|
||||||
|
ast_vec, // vector, basic value type
|
||||||
|
ast_pair, // pair of key and value in hashmap
|
||||||
|
ast_call, // mark a sub-tree of calling an identifier
|
||||||
|
ast_callh, // id.name
|
||||||
|
ast_callv, // id[index]
|
||||||
|
ast_callf, // id()
|
||||||
|
ast_subvec, // id[index:index]
|
||||||
|
ast_param, // function parameter
|
||||||
|
ast_ternary,
|
||||||
|
ast_binary,
|
||||||
|
ast_unary,
|
||||||
|
ast_for, // for keyword
|
||||||
|
ast_forei, // foreach or forindex loop
|
||||||
|
ast_while, // while
|
||||||
|
ast_iter, // iterator, used in forindex/foreach
|
||||||
|
ast_cond, // mark a sub-tree of conditional expression
|
||||||
|
ast_if, // if keyword
|
||||||
|
ast_multi_id, // multi identifiers sub-tree
|
||||||
|
ast_tuple,
|
||||||
|
ast_def, // definition
|
||||||
|
ast_assign,
|
||||||
|
ast_multi_assign,
|
||||||
|
ast_continue, // continue keyword, only used in loop
|
||||||
|
ast_break, // break keyword, only used in loop
|
||||||
|
ast_ret // return keyword, only used in function block
|
||||||
|
};
|
||||||
|
|
||||||
|
class ast_visitor;
|
||||||
|
class hash_pair;
|
||||||
|
class parameter;
|
||||||
|
class slice_vector;
|
||||||
|
class multi_identifier;
|
||||||
|
class code_block;
|
||||||
|
class if_expr;
|
||||||
|
|
||||||
|
class expr {
|
||||||
|
protected:
|
||||||
|
span nd_loc;
|
||||||
|
expr_type nd_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
expr(const span& location, expr_type node_type):
|
||||||
|
nd_loc(location), nd_type(node_type) {}
|
||||||
|
~expr() = default;
|
||||||
|
void set_begin(u32 line, u32 column) {
|
||||||
|
nd_loc.begin_line = line;
|
||||||
|
nd_loc.begin_column = column;
|
||||||
|
}
|
||||||
|
const span& get_location() const {return nd_loc;}
|
||||||
|
const u32 get_line() const {return nd_loc.begin_line;}
|
||||||
|
expr_type get_type() const {return nd_type;}
|
||||||
|
void update_location(const span& location) {
|
||||||
|
nd_loc.end_line = location.end_line;
|
||||||
|
nd_loc.end_column = location.end_column;
|
||||||
|
}
|
||||||
|
virtual void accept(ast_visitor*);
|
||||||
|
};
|
||||||
|
|
||||||
|
class file_info:public expr {
|
||||||
|
private:
|
||||||
|
uint16_t index;
|
||||||
|
std::string filename;
|
||||||
|
|
||||||
|
public:
|
||||||
|
file_info(const span& location, uint16_t file_index, const std::string& name):
|
||||||
|
expr(location, expr_type::ast_file_info),
|
||||||
|
index(file_index), filename(name) {}
|
||||||
|
~file_info() = default;
|
||||||
|
uint16_t get_index() const {return index;}
|
||||||
|
const std::string& get_file_name() const {return filename;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class null_expr:public expr {
|
||||||
|
public:
|
||||||
|
null_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_null) {}
|
||||||
|
~null_expr() = default;
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class nil_expr:public expr {
|
||||||
|
public:
|
||||||
|
nil_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_nil) {}
|
||||||
|
~nil_expr() = default;
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class number_literal:public expr {
|
||||||
|
private:
|
||||||
|
f64 number;
|
||||||
|
|
||||||
|
public:
|
||||||
|
number_literal(const span& location, const f64 num):
|
||||||
|
expr(location, expr_type::ast_num), number(num) {}
|
||||||
|
~number_literal() = default;
|
||||||
|
f64 get_number() const {return number;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class string_literal:public expr {
|
||||||
|
private:
|
||||||
|
std::string content;
|
||||||
|
|
||||||
|
public:
|
||||||
|
string_literal(const span& location, const std::string& str):
|
||||||
|
expr(location, expr_type::ast_str), content(str) {}
|
||||||
|
~string_literal() = default;
|
||||||
|
const std::string get_content() const {return content;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class identifier:public expr {
|
||||||
|
private:
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
public:
|
||||||
|
identifier(const span& location, const std::string& str):
|
||||||
|
expr(location, expr_type::ast_id), name(str) {}
|
||||||
|
~identifier() = default;
|
||||||
|
const std::string get_name() const {return name;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class bool_literal:public expr {
|
||||||
|
private:
|
||||||
|
bool flag;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool_literal(const span& location, const bool bool_flag):
|
||||||
|
expr(location, expr_type::ast_bool), flag(bool_flag) {}
|
||||||
|
~bool_literal() = default;
|
||||||
|
bool get_flag() const {return flag;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class vector_expr:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<expr*> elements;
|
||||||
|
|
||||||
|
public:
|
||||||
|
vector_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_vec) {}
|
||||||
|
~vector_expr();
|
||||||
|
void add_element(expr* node) {elements.push_back(node);}
|
||||||
|
std::vector<expr*>& get_elements() {return elements;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class hash_expr:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<hash_pair*> members;
|
||||||
|
|
||||||
|
public:
|
||||||
|
hash_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_hash) {}
|
||||||
|
~hash_expr();
|
||||||
|
void add_member(hash_pair* node) {members.push_back(node);}
|
||||||
|
std::vector<hash_pair*>& get_members() {return members;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class hash_pair:public expr {
|
||||||
|
private:
|
||||||
|
std::string name;
|
||||||
|
expr* value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
hash_pair(const span& location):
|
||||||
|
expr(location, expr_type::ast_pair),
|
||||||
|
value(nullptr) {}
|
||||||
|
~hash_pair();
|
||||||
|
void set_name(const std::string& field_name) {name = field_name;}
|
||||||
|
void set_value(expr* node) {value = node;}
|
||||||
|
const std::string& get_name() const {return name;}
|
||||||
|
expr* get_value() {return value;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class function:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<parameter*> parameter_list;
|
||||||
|
code_block* block;
|
||||||
|
|
||||||
|
public:
|
||||||
|
function(const span& location):
|
||||||
|
expr(location, expr_type::ast_func),
|
||||||
|
block(nullptr) {}
|
||||||
|
~function();
|
||||||
|
void add_parameter(parameter* node) {parameter_list.push_back(node);}
|
||||||
|
void set_code_block(code_block* node) {block = node;}
|
||||||
|
std::vector<parameter*>& get_parameter_list() {return parameter_list;}
|
||||||
|
code_block* get_code_block() {return block;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class code_block:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<expr*> expressions;
|
||||||
|
|
||||||
|
public:
|
||||||
|
code_block(const span& location):
|
||||||
|
expr(location, expr_type::ast_block) {}
|
||||||
|
~code_block();
|
||||||
|
void add_expression(expr* node) {expressions.push_back(node);}
|
||||||
|
std::vector<expr*>& get_expressions() {return expressions;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class parameter:public expr {
|
||||||
|
public:
|
||||||
|
enum class param_type {
|
||||||
|
normal_parameter,
|
||||||
|
default_parameter,
|
||||||
|
dynamic_parameter
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
param_type type;
|
||||||
|
std::string name;
|
||||||
|
expr* default_value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
parameter(const span& location):
|
||||||
|
expr(location, expr_type::ast_param),
|
||||||
|
name(""), default_value(nullptr) {}
|
||||||
|
~parameter();
|
||||||
|
void set_parameter_type(param_type pt) {type = pt;}
|
||||||
|
void set_parameter_name(const std::string& pname) {name = pname;}
|
||||||
|
void set_default_value(expr* node) {default_value = node;}
|
||||||
|
param_type get_parameter_type() {return type;}
|
||||||
|
const std::string& get_parameter_name() const {return name;}
|
||||||
|
expr* get_default_value() {return default_value;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ternary_operator:public expr {
|
||||||
|
private:
|
||||||
|
expr* condition;
|
||||||
|
expr* left;
|
||||||
|
expr* right;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ternary_operator(const span& location):
|
||||||
|
expr(location, expr_type::ast_ternary),
|
||||||
|
condition(nullptr), left(nullptr), right(nullptr) {}
|
||||||
|
~ternary_operator();
|
||||||
|
void set_condition(expr* node) {condition = node;}
|
||||||
|
void set_left(expr* node) {left = node;}
|
||||||
|
void set_right(expr* node) {right = node;}
|
||||||
|
expr* get_condition() {return condition;}
|
||||||
|
expr* get_left() {return left;}
|
||||||
|
expr* get_right() {return right;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class binary_operator:public expr {
|
||||||
|
public:
|
||||||
|
enum class binary_type {
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
mult,
|
||||||
|
div,
|
||||||
|
concat,
|
||||||
|
cmpeq,
|
||||||
|
cmpneq,
|
||||||
|
less,
|
||||||
|
leq,
|
||||||
|
grt,
|
||||||
|
geq,
|
||||||
|
bitwise_or,
|
||||||
|
bitwise_xor,
|
||||||
|
bitwise_and,
|
||||||
|
condition_and,
|
||||||
|
condition_or
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
binary_type type;
|
||||||
|
expr* left;
|
||||||
|
expr* right;
|
||||||
|
number_literal* optimized_const_number;
|
||||||
|
string_literal* optimized_const_string;
|
||||||
|
|
||||||
|
public:
|
||||||
|
binary_operator(const span& location):
|
||||||
|
expr(location, expr_type::ast_binary),
|
||||||
|
left(nullptr), right(nullptr),
|
||||||
|
optimized_const_number(nullptr),
|
||||||
|
optimized_const_string(nullptr) {}
|
||||||
|
~binary_operator();
|
||||||
|
void set_operator_type(binary_type operator_type) {type = operator_type;}
|
||||||
|
void set_left(expr* node) {left = node;}
|
||||||
|
void set_right(expr* node) {right = node;}
|
||||||
|
void set_optimized_number(number_literal* node) {optimized_const_number = node;}
|
||||||
|
void set_optimized_string(string_literal* node) {optimized_const_string = node;}
|
||||||
|
binary_type get_operator_type() const {return type;}
|
||||||
|
expr* get_left() {return left;}
|
||||||
|
expr* get_right() {return right;}
|
||||||
|
number_literal* get_optimized_number() {return optimized_const_number;}
|
||||||
|
string_literal* get_optimized_string() {return optimized_const_string;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class unary_operator:public expr {
|
||||||
|
public:
|
||||||
|
enum class unary_type {
|
||||||
|
negative,
|
||||||
|
logical_not,
|
||||||
|
bitwise_not
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
unary_type type;
|
||||||
|
expr* value;
|
||||||
|
number_literal* optimized_number;
|
||||||
|
|
||||||
|
public:
|
||||||
|
unary_operator(const span& location):
|
||||||
|
expr(location, expr_type::ast_unary),
|
||||||
|
value(nullptr), optimized_number(nullptr) {}
|
||||||
|
~unary_operator();
|
||||||
|
void set_operator_type(unary_type operator_type) {type = operator_type;}
|
||||||
|
void set_value(expr* node) {value = node;}
|
||||||
|
void set_optimized_number(number_literal* node) {optimized_number = node;}
|
||||||
|
unary_type get_operator_type() const {return type;}
|
||||||
|
expr* get_value() {return value;}
|
||||||
|
number_literal* get_optimized_number() {return optimized_number;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class call_expr:public expr {
|
||||||
|
private:
|
||||||
|
expr* first;
|
||||||
|
std::vector<expr*> calls;
|
||||||
|
|
||||||
|
public:
|
||||||
|
call_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_call),
|
||||||
|
first(nullptr) {}
|
||||||
|
~call_expr();
|
||||||
|
void set_first(expr* node) {first = node;}
|
||||||
|
void add_call(expr* node) {calls.push_back(node);}
|
||||||
|
expr* get_first() {return first;}
|
||||||
|
std::vector<expr*>& get_calls() {return calls;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class call_hash:public expr {
|
||||||
|
private:
|
||||||
|
std::string field;
|
||||||
|
|
||||||
|
public:
|
||||||
|
call_hash(const span& location, const std::string& name):
|
||||||
|
expr(location, expr_type::ast_callh),
|
||||||
|
field(name) {}
|
||||||
|
~call_hash() = default;
|
||||||
|
const std::string& get_field() const {return field;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class call_vector:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<slice_vector*> calls;
|
||||||
|
|
||||||
|
public:
|
||||||
|
call_vector(const span& location):
|
||||||
|
expr(location, expr_type::ast_callv) {}
|
||||||
|
~call_vector();
|
||||||
|
void add_slice(slice_vector* node) {calls.push_back(node);}
|
||||||
|
std::vector<slice_vector*>& get_slices() {return calls;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class call_function:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<expr*> args;
|
||||||
|
|
||||||
|
public:
|
||||||
|
call_function(const span& location):
|
||||||
|
expr(location, expr_type::ast_callf) {}
|
||||||
|
~call_function();
|
||||||
|
void add_argument(expr* node) {args.push_back(node);}
|
||||||
|
std::vector<expr*>& get_argument() {return args;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class slice_vector:public expr {
|
||||||
|
private:
|
||||||
|
expr* begin;
|
||||||
|
expr* end;
|
||||||
|
|
||||||
|
public:
|
||||||
|
slice_vector(const span& location):
|
||||||
|
expr(location, expr_type::ast_subvec),
|
||||||
|
begin(nullptr), end(nullptr) {}
|
||||||
|
~slice_vector();
|
||||||
|
void set_begin(expr* node) {begin = node;}
|
||||||
|
void set_end(expr* node) {end = node;}
|
||||||
|
expr* get_begin() {return begin;}
|
||||||
|
expr* get_end() {return end;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class definition_expr:public expr {
|
||||||
|
private:
|
||||||
|
identifier* variable_name;
|
||||||
|
multi_identifier* variables;
|
||||||
|
expr* value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
definition_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_def),
|
||||||
|
variable_name(nullptr), variables(nullptr), value(nullptr) {}
|
||||||
|
~definition_expr();
|
||||||
|
void set_identifier(identifier* node) {variable_name = node;}
|
||||||
|
void set_multi_define(multi_identifier* node) {variables = node;}
|
||||||
|
void set_value(expr* node) {value = node;}
|
||||||
|
identifier* get_variable_name() {return variable_name;}
|
||||||
|
multi_identifier* get_variables() {return variables;}
|
||||||
|
expr* get_value() {return value;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class assignment_expr:public expr {
|
||||||
|
public:
|
||||||
|
enum class assign_type {
|
||||||
|
equal,
|
||||||
|
add_equal,
|
||||||
|
sub_equal,
|
||||||
|
mult_equal,
|
||||||
|
div_equal,
|
||||||
|
concat_equal,
|
||||||
|
bitwise_and_equal,
|
||||||
|
bitwise_or_equal,
|
||||||
|
bitwise_xor_equal
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
assign_type type;
|
||||||
|
expr* left;
|
||||||
|
expr* right;
|
||||||
|
|
||||||
|
public:
|
||||||
|
assignment_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_assign),
|
||||||
|
left(nullptr), right(nullptr) {}
|
||||||
|
~assignment_expr();
|
||||||
|
void set_assignment_type(assign_type operator_type) {type = operator_type;}
|
||||||
|
void set_left(expr* node) {left = node;}
|
||||||
|
void set_right(expr* node) {right = node;}
|
||||||
|
assign_type get_assignment_type() const {return type;}
|
||||||
|
expr* get_left() {return left;}
|
||||||
|
expr* get_right() {return right;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class multi_identifier:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<identifier*> variables;
|
||||||
|
|
||||||
|
public:
|
||||||
|
multi_identifier(const span& location):
|
||||||
|
expr(location, expr_type::ast_multi_id) {}
|
||||||
|
~multi_identifier();
|
||||||
|
void add_var(identifier* node) {variables.push_back(node);}
|
||||||
|
std::vector<identifier*>& get_variables() {return variables;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class tuple_expr:public expr {
|
||||||
|
private:
|
||||||
|
std::vector<expr*> elements;
|
||||||
|
|
||||||
|
public:
|
||||||
|
tuple_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_tuple) {}
|
||||||
|
~tuple_expr();
|
||||||
|
void add_element(expr* node) {elements.push_back(node);}
|
||||||
|
std::vector<expr*>& get_elements() {return elements;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class multi_assign:public expr {
|
||||||
|
private:
|
||||||
|
tuple_expr* tuple;
|
||||||
|
expr* value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
multi_assign(const span& location):
|
||||||
|
expr(location, expr_type::ast_multi_assign),
|
||||||
|
tuple(nullptr), value(nullptr) {}
|
||||||
|
~multi_assign();
|
||||||
|
void set_tuple(tuple_expr* node) {tuple = node;}
|
||||||
|
void set_value(expr* node) {value = node;}
|
||||||
|
tuple_expr* get_tuple() {return tuple;}
|
||||||
|
expr* get_value() {return value;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class while_expr:public expr {
|
||||||
|
private:
|
||||||
|
expr* condition;
|
||||||
|
code_block* block;
|
||||||
|
|
||||||
|
public:
|
||||||
|
while_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_while),
|
||||||
|
condition(nullptr), block(nullptr) {}
|
||||||
|
~while_expr();
|
||||||
|
void set_condition(expr* node) {condition = node;}
|
||||||
|
void set_code_block (code_block* node) {block = node;}
|
||||||
|
expr* get_condition() {return condition;}
|
||||||
|
code_block* get_code_block() {return block;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class for_expr:public expr {
|
||||||
|
private:
|
||||||
|
expr* initializing;
|
||||||
|
expr* condition;
|
||||||
|
expr* step;
|
||||||
|
code_block* block;
|
||||||
|
|
||||||
|
public:
|
||||||
|
for_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_for),
|
||||||
|
initializing(nullptr), condition(nullptr),
|
||||||
|
step(nullptr), block(nullptr) {}
|
||||||
|
~for_expr();
|
||||||
|
void set_initial(expr* node) {initializing = node;}
|
||||||
|
void set_condition(expr* node) {condition = node;}
|
||||||
|
void set_step(expr* node) {step = node;}
|
||||||
|
void set_code_block(code_block* node) {block = node;}
|
||||||
|
expr* get_initial() {return initializing;}
|
||||||
|
expr* get_condition() {return condition;}
|
||||||
|
expr* get_step() {return step;}
|
||||||
|
code_block* get_code_block() {return block;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class iter_expr:public expr {
|
||||||
|
private:
|
||||||
|
identifier* name;
|
||||||
|
expr* call;
|
||||||
|
|
||||||
|
public:
|
||||||
|
iter_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_iter),
|
||||||
|
name(nullptr), call(nullptr) {}
|
||||||
|
~iter_expr();
|
||||||
|
void set_name(identifier* node) {name = node;}
|
||||||
|
void set_call(expr* node) {call = node;}
|
||||||
|
identifier* get_name() {return name;}
|
||||||
|
expr* get_call() {return call;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class forei_expr:public expr {
|
||||||
|
public:
|
||||||
|
enum class forei_loop_type {
|
||||||
|
foreach,
|
||||||
|
forindex
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
forei_loop_type type;
|
||||||
|
iter_expr* iterator;
|
||||||
|
expr* vector_node;
|
||||||
|
code_block* block;
|
||||||
|
|
||||||
|
public:
|
||||||
|
forei_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_forei),
|
||||||
|
type(forei_loop_type::foreach), iterator(nullptr),
|
||||||
|
vector_node(nullptr), block(nullptr) {}
|
||||||
|
~forei_expr();
|
||||||
|
void set_loop_type(forei_loop_type ft) {type = ft;}
|
||||||
|
void set_iterator(iter_expr* node) {iterator = node;}
|
||||||
|
void set_value(expr* node) {vector_node = node;}
|
||||||
|
void set_code_block(code_block* node) {block = node;}
|
||||||
|
forei_loop_type get_loop_type() const {return type;}
|
||||||
|
iter_expr* get_iterator() {return iterator;}
|
||||||
|
expr* get_value() {return vector_node;}
|
||||||
|
code_block* get_code_block() {return block;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class condition_expr:public expr {
|
||||||
|
private:
|
||||||
|
if_expr* if_stmt;
|
||||||
|
std::vector<if_expr*> elsif_stmt;
|
||||||
|
if_expr* else_stmt;
|
||||||
|
|
||||||
|
public:
|
||||||
|
condition_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_cond),
|
||||||
|
if_stmt(nullptr), else_stmt(nullptr) {}
|
||||||
|
~condition_expr();
|
||||||
|
void set_if_statement(if_expr* node) {if_stmt = node;}
|
||||||
|
void add_elsif_statement(if_expr* node) {elsif_stmt.push_back(node);}
|
||||||
|
void set_else_statement(if_expr* node) {else_stmt = node;}
|
||||||
|
if_expr* get_if_statement() {return if_stmt;}
|
||||||
|
std::vector<if_expr*>& get_elsif_stataments() {return elsif_stmt;}
|
||||||
|
if_expr* get_else_statement() {return else_stmt;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class if_expr:public expr {
|
||||||
|
private:
|
||||||
|
expr* condition;
|
||||||
|
code_block* block;
|
||||||
|
|
||||||
|
public:
|
||||||
|
if_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_if),
|
||||||
|
condition(nullptr), block(nullptr) {}
|
||||||
|
~if_expr();
|
||||||
|
void set_condition(expr* node) {condition = node;}
|
||||||
|
void set_code_block(code_block* node) {block = node;}
|
||||||
|
expr* get_condition() {return condition;}
|
||||||
|
code_block* get_code_block() {return block;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class continue_expr:public expr {
|
||||||
|
public:
|
||||||
|
continue_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_continue) {}
|
||||||
|
~continue_expr() = default;
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class break_expr:public expr {
|
||||||
|
public:
|
||||||
|
break_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_break) {}
|
||||||
|
~break_expr() = default;
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class return_expr:public expr {
|
||||||
|
private:
|
||||||
|
expr* value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
return_expr(const span& location):
|
||||||
|
expr(location, expr_type::ast_ret),
|
||||||
|
value(nullptr) {}
|
||||||
|
~return_expr();
|
||||||
|
void set_value(expr* node) {value = node;}
|
||||||
|
expr* get_value() {return value;}
|
||||||
|
void accept(ast_visitor*) override;
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,139 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
#include "nasal_gc.h"
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#else
|
||||||
|
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
|
||||||
|
#pragma warning (disable:4244)
|
||||||
|
#pragma warning (disable:4267)
|
||||||
|
#pragma warning (disable:4996)
|
||||||
|
#define _CRT_SECURE_NO_DEPRECATE 1
|
||||||
|
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
||||||
|
#include <io.h>
|
||||||
|
#include <direct.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <cmath>
|
||||||
|
#include <thread>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined __APPLE__
|
||||||
|
#include <crt_externs.h>
|
||||||
|
#define environ (*_NSGetEnviron())
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var builtin_print(var*, gc&);
|
||||||
|
var builtin_println(var*, gc&);
|
||||||
|
var builtin_exit(var*, gc&);
|
||||||
|
var builtin_abort(var*, gc&);
|
||||||
|
var builtin_append(var*, gc&);
|
||||||
|
var builtin_setsize(var*, gc&);
|
||||||
|
var builtin_system(var*, gc&);
|
||||||
|
var builtin_input(var*, gc&);
|
||||||
|
var builtin_readfile(var*, gc&);
|
||||||
|
var builtin_fout(var*, gc&);
|
||||||
|
var builtin_split(var*, gc&);
|
||||||
|
var builtin_rand(var*, gc&);
|
||||||
|
var builtin_id(var*, gc&);
|
||||||
|
var builtin_int(var*, gc&);
|
||||||
|
var builtin_floor(var*, gc&);
|
||||||
|
var builtin_num(var*, gc&);
|
||||||
|
var builtin_pop(var*, gc&);
|
||||||
|
var builtin_str(var*, gc&);
|
||||||
|
var builtin_size(var*, gc&);
|
||||||
|
var builtin_u32xor(var*, gc&);
|
||||||
|
var builtin_u32and(var*, gc&);
|
||||||
|
var builtin_u32or(var*, gc&);
|
||||||
|
var builtin_u32nand(var*, gc&);
|
||||||
|
var builtin_u32not(var*, gc&);
|
||||||
|
var builtin_pow(var*, gc&);
|
||||||
|
var builtin_sin(var*, gc&);
|
||||||
|
var builtin_cos(var*, gc&);
|
||||||
|
var builtin_tan(var*, gc&);
|
||||||
|
var builtin_exp(var*, gc&);
|
||||||
|
var builtin_lg(var*, gc&);
|
||||||
|
var builtin_ln(var*, gc&);
|
||||||
|
var builtin_sqrt(var*, gc&);
|
||||||
|
var builtin_atan2(var*, gc&);
|
||||||
|
var builtin_isnan(var*, gc&);
|
||||||
|
var builtin_time(var*, gc&);
|
||||||
|
var builtin_contains(var*, gc&);
|
||||||
|
var builtin_delete(var*, gc&);
|
||||||
|
var builtin_keys(var*, gc&);
|
||||||
|
var builtin_die(var*, gc&);
|
||||||
|
var builtin_find(var*, gc&);
|
||||||
|
var builtin_type(var*, gc&);
|
||||||
|
var builtin_substr(var*, gc&);
|
||||||
|
var builtin_streq(var*, gc&);
|
||||||
|
var builtin_left(var*, gc&);
|
||||||
|
var builtin_right(var*, gc&);
|
||||||
|
var builtin_cmp(var*, gc&);
|
||||||
|
var builtin_chr(var*, gc&);
|
||||||
|
var builtin_char(var*, gc&);
|
||||||
|
var builtin_values(var*, gc&);
|
||||||
|
var builtin_exists(var*, gc&);
|
||||||
|
var builtin_open(var*, gc&);
|
||||||
|
var builtin_close(var*, gc&);
|
||||||
|
var builtin_read(var*, gc&);
|
||||||
|
var builtin_write(var*, gc&);
|
||||||
|
var builtin_seek(var*, gc&);
|
||||||
|
var builtin_tell(var*, gc&);
|
||||||
|
var builtin_readln(var*, gc&);
|
||||||
|
var builtin_stat(var*, gc&);
|
||||||
|
var builtin_eof(var*, gc&);
|
||||||
|
var builtin_fld(var*, gc&);
|
||||||
|
var builtin_sfld(var*, gc&);
|
||||||
|
var builtin_setfld(var*, gc&);
|
||||||
|
var builtin_buf(var*, gc&);
|
||||||
|
var builtin_sleep(var*, gc&);
|
||||||
|
var builtin_pipe(var*, gc&);
|
||||||
|
var builtin_fork(var*, gc&);
|
||||||
|
var builtin_waitpid(var*, gc&);
|
||||||
|
var builtin_opendir(var*, gc&);
|
||||||
|
var builtin_readdir(var*, gc&);
|
||||||
|
var builtin_closedir(var*, gc&);
|
||||||
|
var builtin_chdir(var*, gc&);
|
||||||
|
var builtin_environ(var*, gc&);
|
||||||
|
var builtin_getcwd(var*, gc&);
|
||||||
|
var builtin_getenv(var*, gc&);
|
||||||
|
var builtin_dlopen(var*, gc&);
|
||||||
|
var builtin_dlclose(var*, gc&);
|
||||||
|
var builtin_dlcallv(var*, gc&);
|
||||||
|
var builtin_dlcall(var*, gc&);
|
||||||
|
var builtin_platform(var*, gc&);
|
||||||
|
var builtin_arch(var*, gc&);
|
||||||
|
// md5 related functions
|
||||||
|
std::string tohex(u32);
|
||||||
|
std::string md5(const std::string&);
|
||||||
|
var builtin_md5(var*, gc&);
|
||||||
|
var builtin_cocreate(var*, gc&);
|
||||||
|
var builtin_coresume(var*, gc&);
|
||||||
|
var builtin_coyield(var*, gc&);
|
||||||
|
var builtin_costatus(var*, gc&);
|
||||||
|
var builtin_corun(var*, gc&);
|
||||||
|
var builtin_millisec(var*, gc&);
|
||||||
|
var builtin_sysargv(var*, gc&);
|
||||||
|
var builtin_gcextend(var*, gc&);
|
||||||
|
var builtin_logtime(var*, gc&);
|
||||||
|
var builtin_ghosttype(var*, gc&);
|
||||||
|
|
||||||
|
// register builtin function's name and it's address here in this table below
|
||||||
|
// this table must end with {nullptr,nullptr}
|
||||||
|
struct nasal_builtin_table {
|
||||||
|
const char* name;
|
||||||
|
var (*func)(var*,gc&);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern nasal_builtin_table builtin[];
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,103 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal_err.h"
|
||||||
|
#include "nasal_builtin.h"
|
||||||
|
#include "nasal_opcode.h"
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
#include "ast_visitor.h"
|
||||||
|
#include "symbol_finder.h"
|
||||||
|
#include "nasal_parse.h"
|
||||||
|
#include "nasal_import.h"
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <list>
|
||||||
|
#include <stack>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (disable:4244)
|
||||||
|
#pragma warning (disable:4267)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class codegen {
|
||||||
|
private:
|
||||||
|
u16 fileindex;
|
||||||
|
error& err;
|
||||||
|
const std::string* file;
|
||||||
|
std::stack<u32> in_iterloop;
|
||||||
|
std::unordered_map<f64,u32> num_table;
|
||||||
|
std::unordered_map<std::string,u32> str_table;
|
||||||
|
std::vector<f64> num_res;
|
||||||
|
std::vector<std::string> str_res;
|
||||||
|
std::vector<opcode> code;
|
||||||
|
std::list<std::vector<i32>> continue_ptr;
|
||||||
|
std::list<std::vector<i32>> break_ptr;
|
||||||
|
|
||||||
|
// symbol table
|
||||||
|
// global : max STACK_DEPTH-1 values
|
||||||
|
std::unordered_map<std::string,i32> global;
|
||||||
|
// local : max 32768 upvalues 65536 values
|
||||||
|
// but in fact local scope also has less than STACK_DEPTH value
|
||||||
|
std::list<std::unordered_map<std::string,i32>> local;
|
||||||
|
|
||||||
|
void check_id_exist(identifier*);
|
||||||
|
|
||||||
|
void die(const std::string& info, const span& loc) {
|
||||||
|
err.err("code", loc, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void regist_num(const f64);
|
||||||
|
void regist_str(const std::string&);
|
||||||
|
void find_symbol(code_block*);
|
||||||
|
void add_sym(const std::string&);
|
||||||
|
i32 local_find(const std::string&);
|
||||||
|
i32 global_find(const std::string&);
|
||||||
|
i32 upvalue_find(const std::string&);
|
||||||
|
|
||||||
|
void gen(u8, u32, u32);
|
||||||
|
|
||||||
|
void num_gen(number_literal*);
|
||||||
|
void str_gen(string_literal*);
|
||||||
|
void bool_gen(bool_literal*);
|
||||||
|
void vec_gen(vector_expr*);
|
||||||
|
void hash_gen(hash_expr*);
|
||||||
|
void func_gen(function*);
|
||||||
|
void call_gen(call_expr*);
|
||||||
|
void call_id(identifier*);
|
||||||
|
void call_hash_gen(call_hash*);
|
||||||
|
void call_vec(call_vector*);
|
||||||
|
void call_func(call_function*);
|
||||||
|
void mcall(expr*);
|
||||||
|
void mcall_id(identifier*);
|
||||||
|
void mcall_vec(call_vector*);
|
||||||
|
void mcall_hash(call_hash*);
|
||||||
|
void multi_def(definition_expr*);
|
||||||
|
void single_def(definition_expr*);
|
||||||
|
void def_gen(definition_expr*);
|
||||||
|
void assignment_expression(assignment_expr*);
|
||||||
|
void assignment_statement(assignment_expr*);
|
||||||
|
void multi_assign_gen(multi_assign*);
|
||||||
|
void cond_gen(condition_expr*);
|
||||||
|
void loop_gen(expr*);
|
||||||
|
void load_continue_break(i32, i32);
|
||||||
|
void while_gen(while_expr*);
|
||||||
|
void for_gen(for_expr*);
|
||||||
|
void forei_gen(forei_expr*);
|
||||||
|
void statement_generation(expr*);
|
||||||
|
void or_gen(binary_operator*);
|
||||||
|
void and_gen(binary_operator*);
|
||||||
|
void unary_gen(unary_operator*);
|
||||||
|
void binary_gen(binary_operator*);
|
||||||
|
void trino_gen(ternary_operator*);
|
||||||
|
void calc_gen(expr*);
|
||||||
|
void block_gen(code_block*);
|
||||||
|
void ret_gen(return_expr*);
|
||||||
|
|
||||||
|
public:
|
||||||
|
codegen(error& e): fileindex(0), err(e), file(nullptr) {}
|
||||||
|
const error& compile(parse&, linker&);
|
||||||
|
void print();
|
||||||
|
const std::vector<std::string>& strs() const {return str_res;}
|
||||||
|
const std::vector<f64>& nums() const {return num_res;}
|
||||||
|
const std::vector<opcode>& codes() const {return code;}
|
||||||
|
};
|
|
@ -1,43 +1,10 @@
|
||||||
#pragma once
|
#include "nasal_dbg.h"
|
||||||
|
|
||||||
#include "nasal_import.h"
|
std::vector<std::string> dbg::parse(const std::string& cmd) {
|
||||||
#include "nasal_err.h"
|
std::vector<std::string> res;
|
||||||
#include "nasal_opcode.h"
|
|
||||||
#include "nasal_vm.h"
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
class dbg:public vm {
|
|
||||||
private:
|
|
||||||
bool next;
|
|
||||||
usize fsize;
|
|
||||||
u16 bk_fidx;
|
|
||||||
u32 bk_line;
|
|
||||||
error& src;
|
|
||||||
|
|
||||||
std::vector<string> parse(const string&);
|
|
||||||
u16 fileindex(const string&);
|
|
||||||
void err();
|
|
||||||
void help();
|
|
||||||
void callsort(const u64*);
|
|
||||||
void stepinfo();
|
|
||||||
void interact();
|
|
||||||
public:
|
|
||||||
dbg(error& err):
|
|
||||||
next(false), fsize(0),
|
|
||||||
bk_fidx(0), bk_line(0),
|
|
||||||
src(err) {}
|
|
||||||
void run(
|
|
||||||
const codegen&,
|
|
||||||
const linker&,
|
|
||||||
const std::vector<string>&
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<string> dbg::parse(const string& cmd) {
|
|
||||||
std::vector<string> res;
|
|
||||||
usize last=0;
|
usize last=0;
|
||||||
usize pos=cmd.find(" ", 0);
|
usize pos=cmd.find(" ", 0);
|
||||||
while(pos!=string::npos) {
|
while(pos!=std::string::npos) {
|
||||||
if (pos>last) {
|
if (pos>last) {
|
||||||
res.push_back(cmd.substr(last, pos-last));
|
res.push_back(cmd.substr(last, pos-last));
|
||||||
}
|
}
|
||||||
|
@ -50,7 +17,7 @@ std::vector<string> dbg::parse(const string& cmd) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 dbg::fileindex(const string& filename) {
|
u16 dbg::file_index(const std::string& filename) const {
|
||||||
for(u16 i=0;i<fsize;++i) {
|
for(u16 i=0;i<fsize;++i) {
|
||||||
if (filename==files[i]) {
|
if (filename==files[i]) {
|
||||||
return i;
|
return i;
|
||||||
|
@ -66,24 +33,30 @@ void dbg::err() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg::help() {
|
void dbg::help() {
|
||||||
std::cout
|
std::clog
|
||||||
<<"<option>\n"
|
<<"<option>\n"
|
||||||
<<"\th, help | get help\n"
|
<<" h, help | get help\n"
|
||||||
<<"\tbt, backtrace | get function call trace\n"
|
<<" bt, backtrace | get function call trace\n"
|
||||||
<<"\tc, continue | run program until break point or exit\n"
|
<<" c, continue | run program until break point or exit\n"
|
||||||
<<"\tf, file | see all the compiled files\n"
|
<<" f, file | see all the compiled files\n"
|
||||||
<<"\tg, global | see global values\n"
|
<<" g, global | see global values\n"
|
||||||
<<"\tl, local | see local values\n"
|
<<" l, local | see local values\n"
|
||||||
<<"\tu, upval | see upvalue\n"
|
<<" u, upval | see upvalue\n"
|
||||||
<<"\tr, register | show vm register detail\n"
|
<<" r, register | show vm register detail\n"
|
||||||
<<"\ta, all | show global,local and upvalue\n"
|
<<" a, all | show global,local and upvalue\n"
|
||||||
<<"\tn, next | execute next bytecode\n"
|
<<" n, next | execute next bytecode\n"
|
||||||
<<"\tq, exit | exit debugger\n"
|
<<" q, exit | exit debugger\n"
|
||||||
<<"<option> <filename> <line>\n"
|
<<"<option> <filename> <line>\n"
|
||||||
<<"\tbk, break | set break point\n";
|
<<" bk, break | set break point\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg::callsort(const u64* arr) {
|
void dbg::list_file() const {
|
||||||
|
for(usize i=0; i<fsize; ++i) {
|
||||||
|
std::clog<<"["<<i<<"] "<<files[i]<<"\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbg::call_sort(const u64* arr) const {
|
||||||
typedef std::pair<u32,u64> op;
|
typedef std::pair<u32,u64> op;
|
||||||
std::vector<op> opcall;
|
std::vector<op> opcall;
|
||||||
u64 total=0;
|
u64 total=0;
|
||||||
|
@ -105,22 +78,22 @@ void dbg::callsort(const u64* arr) {
|
||||||
std::clog<<" total : "<<total<<'\n';
|
std::clog<<" total : "<<total<<'\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg::stepinfo() {
|
void dbg::step_info() {
|
||||||
u32 line=bytecode[ctx.pc].line==0?0:bytecode[ctx.pc].line-1;
|
u32 line=bytecode[ctx.pc].line==0?0:bytecode[ctx.pc].line-1;
|
||||||
u32 begin=(line>>3)==0?0:((line>>3)<<3);
|
u32 begin=(line>>3)==0?0:((line>>3)<<3);
|
||||||
u32 end=(1+(line>>3))<<3;
|
u32 end=(1+(line>>3))<<3;
|
||||||
src.load(files[bytecode[ctx.pc].fidx]);
|
src.load(files[bytecode[ctx.pc].fidx]);
|
||||||
std::cout<<"\nsource code:\n";
|
std::clog<<"\nsource code:\n";
|
||||||
for(u32 i=begin;i<end && i<src.size();++i) {
|
for(u32 i=begin;i<end && i<src.size();++i) {
|
||||||
std::cout<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\n";
|
std::clog<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
begin=(ctx.pc>>3)==0?0:((ctx.pc>>3)<<3);
|
begin=(ctx.pc>>3)==0?0:((ctx.pc>>3)<<3);
|
||||||
end=(1+(ctx.pc>>3))<<3;
|
end=(1+(ctx.pc>>3))<<3;
|
||||||
codestream::set(cnum, cstr, files);
|
codestream::set(cnum, cstr, files);
|
||||||
std::cout<<"next bytecode:\n";
|
std::clog<<"next bytecode:\n";
|
||||||
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i) {
|
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i) {
|
||||||
std::cout
|
std::clog
|
||||||
<<(i==ctx.pc?back_white:reset)
|
<<(i==ctx.pc?back_white:reset)
|
||||||
<<(i==ctx.pc?"--> ":" ")
|
<<(i==ctx.pc?"--> ":" ")
|
||||||
<<codestream(bytecode[i], i)
|
<<codestream(bytecode[i], i)
|
||||||
|
@ -130,65 +103,51 @@ void dbg::stepinfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg::interact() {
|
void dbg::interact() {
|
||||||
// special operand
|
// special operand, end execution
|
||||||
if (bytecode[ctx.pc].op==op_exit) {
|
if (bytecode[ctx.pc].op==op_exit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if ((bytecode[ctx.pc].fidx!=bk_fidx ||
|
||||||
(bytecode[ctx.pc].fidx!=bk_fidx || bytecode[ctx.pc].line!=bk_line) && // break point
|
bytecode[ctx.pc].line!=bk_line) && // break point
|
||||||
!next // next step
|
!next) {// next step
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
next=false;
|
next=false;
|
||||||
string cmd;
|
std::string cmd;
|
||||||
stepinfo();
|
step_info();
|
||||||
while(true) {
|
while(true) {
|
||||||
std::cout<<">> ";
|
std::clog<<">> ";
|
||||||
std::getline(std::cin, cmd);
|
std::getline(std::cin, cmd);
|
||||||
auto res=parse(cmd);
|
auto res=parse(cmd);
|
||||||
if (res.size()==0) {
|
if (res.size()==0) {
|
||||||
stepinfo();
|
step_info();
|
||||||
} else if (res.size()==1) {
|
} else if (res.size()==1) {
|
||||||
if (res[0]=="h" || res[0]=="help") {
|
switch(get_cmd_type(res[0])) {
|
||||||
help();
|
case dbg_cmd::cmd_help: help(); break;
|
||||||
} else if (res[0]=="bt" || res[0]=="backtrace") {
|
case dbg_cmd::cmd_backtrace: traceback(); break;
|
||||||
traceback();
|
case dbg_cmd::cmd_continue: return;
|
||||||
} else if (res[0]=="c" || res[0]=="continue") {
|
case dbg_cmd::cmd_list_file: list_file(); break;
|
||||||
return;
|
case dbg_cmd::cmd_global: gstate(); break;
|
||||||
} else if (res[0]=="f" || res[0]=="file") {
|
case dbg_cmd::cmd_local: lstate(); break;
|
||||||
for(usize i=0;i<fsize;++i) {
|
case dbg_cmd::cmd_upval: ustate(); break;
|
||||||
std::cout<<"["<<i<<"] "<<files[i]<<"\n";
|
case dbg_cmd::cmd_register: reginfo(); break;
|
||||||
}
|
case dbg_cmd::cmd_show_all: detail(); break;
|
||||||
} else if (res[0]=="g" || res[0]=="global") {
|
case dbg_cmd::cmd_next: next=true; return;
|
||||||
gstate();
|
case dbg_cmd::cmd_exit: std::exit(0);
|
||||||
} else if (res[0]=="l" || res[0]=="local") {
|
default: err(); break;
|
||||||
lstate();
|
|
||||||
} else if (res[0]=="u" || res[0]=="upval") {
|
|
||||||
ustate();
|
|
||||||
} else if (res[0]=="r" || res[0]=="register") {
|
|
||||||
reginfo();
|
|
||||||
} else if (res[0]=="a" || res[0]=="all") {
|
|
||||||
detail();
|
|
||||||
} else if (res[0]=="n" || res[0]=="next") {
|
|
||||||
next=true;
|
|
||||||
return;
|
|
||||||
} else if (res[0]=="q" || res[0]=="exit")
|
|
||||||
std::exit(0);
|
|
||||||
else {
|
|
||||||
err();
|
|
||||||
}
|
}
|
||||||
} else if (res.size()==3 && (res[0]=="bk" || res[0]=="break")) {
|
} else if (res.size()==3 &&
|
||||||
bk_fidx=fileindex(res[1]);
|
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
|
||||||
|
bk_fidx=file_index(res[1]);
|
||||||
if (bk_fidx==65535) {
|
if (bk_fidx==65535) {
|
||||||
std::cout<<"cannot find file named `"<<res[1]<<"`\n";
|
std::clog<<"cannot find file named `"<<res[1]<<"`\n";
|
||||||
bk_fidx=0;
|
continue;
|
||||||
}
|
}
|
||||||
i32 tmp=atoi(res[2].c_str());
|
i32 tmp=atoi(res[2].c_str());
|
||||||
if (tmp<=0) {
|
if (tmp<=0) {
|
||||||
std::cout<<"incorrect line number `"<<res[2]<<"`\n";
|
std::clog<<"incorrect line number `"<<res[2]<<"`\n";
|
||||||
} else {
|
} else {
|
||||||
bk_line=tmp;
|
bk_line=tmp;
|
||||||
}
|
}
|
||||||
|
@ -201,8 +160,7 @@ void dbg::interact() {
|
||||||
void dbg::run(
|
void dbg::run(
|
||||||
const codegen& gen,
|
const codegen& gen,
|
||||||
const linker& linker,
|
const linker& linker,
|
||||||
const std::vector<string>& argv
|
const std::vector<std::string>& argv) {
|
||||||
) {
|
|
||||||
verbose=true;
|
verbose=true;
|
||||||
fsize=linker.filelist().size();
|
fsize=linker.filelist().size();
|
||||||
init(gen.strs(), gen.nums(), gen.codes(), linker.filelist(), argv);
|
init(gen.strs(), gen.nums(), gen.codes(), linker.filelist(), argv);
|
||||||
|
@ -269,7 +227,7 @@ void dbg::run(
|
||||||
++ctx.pc;
|
++ctx.pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
callsort(count);
|
call_sort(count);
|
||||||
ngc.info();
|
ngc.info();
|
||||||
ngc.clear();
|
ngc.clear();
|
||||||
imm.clear();
|
imm.clear();
|
|
@ -0,0 +1,88 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal_import.h"
|
||||||
|
#include "nasal_err.h"
|
||||||
|
#include "nasal_opcode.h"
|
||||||
|
#include "nasal_vm.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class dbg:public vm {
|
||||||
|
private:
|
||||||
|
enum class dbg_cmd {
|
||||||
|
cmd_error,
|
||||||
|
cmd_help,
|
||||||
|
cmd_backtrace,
|
||||||
|
cmd_continue,
|
||||||
|
cmd_list_file,
|
||||||
|
cmd_global,
|
||||||
|
cmd_local,
|
||||||
|
cmd_upval,
|
||||||
|
cmd_register,
|
||||||
|
cmd_show_all,
|
||||||
|
cmd_next,
|
||||||
|
cmd_break_point,
|
||||||
|
cmd_exit
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unordered_map<std::string, dbg_cmd> command_table = {
|
||||||
|
{"h", dbg_cmd::cmd_help},
|
||||||
|
{"help", dbg_cmd::cmd_help},
|
||||||
|
{"bt", dbg_cmd::cmd_backtrace},
|
||||||
|
{"backtrace", dbg_cmd::cmd_backtrace},
|
||||||
|
{"c", dbg_cmd::cmd_continue},
|
||||||
|
{"continue", dbg_cmd::cmd_continue},
|
||||||
|
{"f", dbg_cmd::cmd_list_file},
|
||||||
|
{"file", dbg_cmd::cmd_list_file},
|
||||||
|
{"g", dbg_cmd::cmd_global},
|
||||||
|
{"global", dbg_cmd::cmd_global},
|
||||||
|
{"l", dbg_cmd::cmd_local},
|
||||||
|
{"local", dbg_cmd::cmd_local},
|
||||||
|
{"u", dbg_cmd::cmd_upval},
|
||||||
|
{"upval", dbg_cmd::cmd_upval},
|
||||||
|
{"r", dbg_cmd::cmd_register},
|
||||||
|
{"register", dbg_cmd::cmd_register},
|
||||||
|
{"a", dbg_cmd::cmd_show_all},
|
||||||
|
{"all", dbg_cmd::cmd_show_all},
|
||||||
|
{"n", dbg_cmd::cmd_next},
|
||||||
|
{"next", dbg_cmd::cmd_next},
|
||||||
|
{"bk", dbg_cmd::cmd_break_point},
|
||||||
|
{"break", dbg_cmd::cmd_break_point},
|
||||||
|
{"q", dbg_cmd::cmd_exit},
|
||||||
|
{"exit", dbg_cmd::cmd_exit}
|
||||||
|
};
|
||||||
|
dbg_cmd get_cmd_type(const std::string& cmd) const {
|
||||||
|
return command_table.count(cmd)?
|
||||||
|
command_table.at(cmd):dbg_cmd::cmd_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool next;
|
||||||
|
usize fsize;
|
||||||
|
u16 bk_fidx;
|
||||||
|
u32 bk_line;
|
||||||
|
error& src;
|
||||||
|
|
||||||
|
std::vector<std::string> parse(const std::string&);
|
||||||
|
u16 file_index(const std::string&) const;
|
||||||
|
void err();
|
||||||
|
void help();
|
||||||
|
void list_file() const;
|
||||||
|
void call_sort(const u64*) const;
|
||||||
|
void step_info();
|
||||||
|
void interact();
|
||||||
|
|
||||||
|
public:
|
||||||
|
dbg(error& err):
|
||||||
|
next(false), fsize(0),
|
||||||
|
bk_fidx(0), bk_line(0),
|
||||||
|
src(err) {}
|
||||||
|
void run(
|
||||||
|
const codegen&,
|
||||||
|
const linker&,
|
||||||
|
const std::vector<std::string>&
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,228 +1,172 @@
|
||||||
#pragma once
|
#include "nasal_err.h"
|
||||||
|
#ifdef _WIN32
|
||||||
#include <iostream>
|
#include <windows.h> // use SetConsoleTextAttribute
|
||||||
#include <fstream>
|
struct for_reset {
|
||||||
#include <sstream> // MSVC need this to use std::getline
|
CONSOLE_SCREEN_BUFFER_INFO scr;
|
||||||
#include <cstring>
|
for_reset() {
|
||||||
#include <vector>
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
|
||||||
|
}
|
||||||
#include "nasal.h"
|
} reset_ter_color;
|
||||||
|
#endif
|
||||||
struct span {
|
|
||||||
u32 begin_line;
|
std::ostream& back_white(std::ostream& s) {
|
||||||
u32 begin_column;
|
#ifdef _WIN32
|
||||||
u32 end_line;
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
|
||||||
u32 end_column;
|
#else
|
||||||
string file;
|
s<<"\033[7m";
|
||||||
};
|
#endif
|
||||||
|
return s;
|
||||||
#ifdef _WIN32
|
}
|
||||||
#include <windows.h> // use SetConsoleTextAttribute
|
|
||||||
struct for_reset {
|
std::ostream& red(std::ostream& s) {
|
||||||
CONSOLE_SCREEN_BUFFER_INFO scr;
|
#ifdef _WIN32
|
||||||
for_reset() {
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
|
||||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
|
#else
|
||||||
}
|
s<<"\033[91;1m";
|
||||||
} reset_ter_color;
|
#endif
|
||||||
#endif
|
return s;
|
||||||
|
}
|
||||||
std::ostream& back_white(std::ostream& s) {
|
|
||||||
#ifdef _WIN32
|
std::ostream& cyan(std::ostream& s) {
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
|
#ifdef _WIN32
|
||||||
#else
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
|
||||||
s<<"\033[7m";
|
#else
|
||||||
#endif
|
s<<"\033[36;1m";
|
||||||
return s;
|
#endif
|
||||||
}
|
return s;
|
||||||
|
}
|
||||||
std::ostream& red(std::ostream& s) {
|
|
||||||
#ifdef _WIN32
|
std::ostream& orange(std::ostream& s) {
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
|
#ifdef _WIN32
|
||||||
#else
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
|
||||||
s<<"\033[91;1m";
|
#else
|
||||||
#endif
|
s<<"\033[93;1m";
|
||||||
return s;
|
#endif
|
||||||
}
|
return s;
|
||||||
|
}
|
||||||
std::ostream& cyan(std::ostream& s) {
|
|
||||||
#ifdef _WIN32
|
std::ostream& white(std::ostream& s) {
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
|
#ifdef _WIN32
|
||||||
#else
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
|
||||||
s<<"\033[36;1m";
|
#else
|
||||||
#endif
|
s<<"\033[0m\033[1m";
|
||||||
return s;
|
#endif
|
||||||
}
|
return s;
|
||||||
|
}
|
||||||
std::ostream& orange(std::ostream& s) {
|
|
||||||
#ifdef _WIN32
|
std::ostream& reset(std::ostream& s) {
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
|
#ifdef _WIN32
|
||||||
#else
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes);
|
||||||
s<<"\033[93;1m";
|
#else
|
||||||
#endif
|
s<<"\033[0m";
|
||||||
return s;
|
#endif
|
||||||
}
|
return s;
|
||||||
|
}
|
||||||
std::ostream& white(std::ostream& s) {
|
|
||||||
#ifdef _WIN32
|
void flstream::load(const std::string& f) {
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
|
if (file==f) { // don't need to load a loaded file
|
||||||
#else
|
return;
|
||||||
s<<"\033[0m\033[1m";
|
} else {
|
||||||
#endif
|
file=f;
|
||||||
return s;
|
}
|
||||||
}
|
|
||||||
|
res.clear();
|
||||||
std::ostream& reset(std::ostream& s) {
|
std::ifstream in(f, std::ios::binary);
|
||||||
#ifdef _WIN32
|
if (in.fail()) {
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes);
|
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
|
||||||
#else
|
std::exit(1);
|
||||||
s<<"\033[0m";
|
}
|
||||||
#endif
|
|
||||||
return s;
|
while(!in.eof()) {
|
||||||
}
|
std::string line;
|
||||||
|
std::getline(in, line);
|
||||||
class flstream {
|
res.push_back(line);
|
||||||
protected:
|
}
|
||||||
string file;
|
}
|
||||||
std::vector<string> res;
|
|
||||||
public:
|
void error::fatal(const std::string& stage, const std::string& info) {
|
||||||
flstream():file("") {}
|
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
|
||||||
void load(const string&);
|
if (file.length()) {
|
||||||
const string& operator[](usize n) const {return res[n];}
|
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
|
||||||
const string& name() const {return file;}
|
} else {
|
||||||
usize size() const {return res.size();}
|
std::cerr<<reset<<"\n";
|
||||||
};
|
}
|
||||||
|
std::exit(1);
|
||||||
class error:public flstream {
|
}
|
||||||
private:
|
|
||||||
u32 cnt; // counter for errors
|
void error::err(const std::string& stage, const std::string& info) {
|
||||||
|
++cnt;
|
||||||
string identation(usize len) {
|
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
|
||||||
return string(len,' ');
|
if (file.length()) {
|
||||||
}
|
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
|
||||||
string leftpad(u32 num, usize len) {
|
} else {
|
||||||
string tmp=std::to_string(num);
|
std::cerr<<reset<<"\n";
|
||||||
while(tmp.length()<len) {
|
}
|
||||||
tmp=" "+tmp;
|
}
|
||||||
}
|
|
||||||
return tmp;
|
void error::err(
|
||||||
}
|
const std::string& stage, const span& loc, const std::string& info) {
|
||||||
public:
|
// load error occurred file into string lines
|
||||||
error():cnt(0) {}
|
load(loc.file);
|
||||||
void fatal(const string&, const string&);
|
|
||||||
void err(const string&, const string&);
|
++cnt;
|
||||||
void err(const string&, const span&, const string&);
|
|
||||||
|
std::cerr
|
||||||
void chkerr() const {
|
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> "
|
||||||
if (cnt) {
|
<<red<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<reset<<"\n";
|
||||||
std::exit(1);
|
|
||||||
}
|
const usize maxlen = std::to_string(loc.end_line).length();
|
||||||
}
|
const std::string iden = identation(maxlen);
|
||||||
};
|
|
||||||
|
for(u32 line=loc.begin_line; line<=loc.end_line; ++line) {
|
||||||
|
if (!line) {
|
||||||
void flstream::load(const string& f) {
|
continue;
|
||||||
if (file==f) { // don't need to load a loaded file
|
}
|
||||||
return;
|
|
||||||
} else {
|
if (loc.begin_line<line && line<loc.end_line) {
|
||||||
file=f;
|
if (line==loc.begin_line+1) {
|
||||||
}
|
std::cerr<<cyan<<iden<<" | "<<reset<<"...\n"<<cyan<<iden<<" | "<<reset<<"\n";
|
||||||
|
}
|
||||||
res.clear();
|
continue;
|
||||||
std::ifstream in(f, std::ios::binary);
|
}
|
||||||
if (in.fail()) {
|
|
||||||
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
|
// if this line has nothing, skip
|
||||||
std::exit(1);
|
if (!res[line-1].length() && line!=loc.end_line) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
while(!in.eof()) {
|
|
||||||
string line;
|
const std::string& code=res[line-1];
|
||||||
std::getline(in, line);
|
std::cerr<<cyan<<leftpad(line, maxlen)<<" | "<<reset<<code<<"\n";
|
||||||
res.push_back(line);
|
// output underline
|
||||||
}
|
std::cerr<<cyan<<iden<<" | "<<reset;
|
||||||
}
|
if (loc.begin_line==loc.end_line) {
|
||||||
|
for(u32 i=0; i<loc.begin_column; ++i) {
|
||||||
void error::fatal(const string& stage, const string& info) {
|
std::cerr<<char(" \t"[code[i]=='\t']);
|
||||||
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
|
}
|
||||||
if (file.length()) {
|
for(u32 i=loc.begin_column ;i<loc.end_column; ++i) {
|
||||||
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
|
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
|
||||||
} else {
|
}
|
||||||
std::cerr<<reset<<"\n";
|
} else if (line==loc.begin_line) {
|
||||||
}
|
for(u32 i=0; i<loc.begin_column; ++i) {
|
||||||
std::exit(1);
|
std::cerr<<char(" \t"[code[i]=='\t']);
|
||||||
}
|
}
|
||||||
|
for(u32 i=loc.begin_column; i<code.size(); ++i) {
|
||||||
void error::err(const string& stage, const string& info) {
|
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
|
||||||
++cnt;
|
}
|
||||||
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
|
} else if (loc.begin_line<line && line<loc.end_line) {
|
||||||
if (file.length()) {
|
for(u32 i=0; i<code.size(); ++i) {
|
||||||
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
|
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
|
||||||
} else {
|
}
|
||||||
std::cerr<<reset<<"\n";
|
} else {
|
||||||
}
|
for(u32 i=0; i<loc.end_column; ++i) {
|
||||||
}
|
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
|
||||||
|
}
|
||||||
void error::err(const string& stage, const span& loc, const string& info) {
|
}
|
||||||
// load error occurred file into string lines
|
if (line==loc.end_line) {
|
||||||
load(loc.file);
|
std::cerr<<reset;
|
||||||
|
} else {
|
||||||
++cnt;
|
std::cerr<<reset<<"\n";
|
||||||
|
}
|
||||||
std::cerr
|
}
|
||||||
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> "
|
std::cerr<<"\n\n";
|
||||||
<<red<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<reset<<"\n";
|
|
||||||
|
|
||||||
const usize maxlen=std::to_string(loc.end_line).length();
|
|
||||||
const string iden=identation(maxlen);
|
|
||||||
|
|
||||||
for(u32 line=loc.begin_line; line<=loc.end_line; ++line) {
|
|
||||||
if (!line) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loc.begin_line<line && line<loc.end_line) {
|
|
||||||
if (line==loc.begin_line+1) {
|
|
||||||
std::cerr<<cyan<<iden<<" | "<<reset<<"...\n"<<cyan<<iden<<" | "<<reset<<"\n";
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this line has nothing, skip
|
|
||||||
if (!res[line-1].length() && line!=loc.end_line) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const string& code=res[line-1];
|
|
||||||
std::cerr<<cyan<<leftpad(line, maxlen)<<" | "<<reset<<code<<"\n";
|
|
||||||
// output underline
|
|
||||||
std::cerr<<cyan<<iden<<" | "<<reset;
|
|
||||||
if (loc.begin_line==loc.end_line) {
|
|
||||||
for(u32 i=0; i<loc.begin_column; ++i) {
|
|
||||||
std::cerr<<char(" \t"[code[i]=='\t']);
|
|
||||||
}
|
|
||||||
for(u32 i=loc.begin_column ;i<loc.end_column; ++i) {
|
|
||||||
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
|
|
||||||
}
|
|
||||||
} else if (line==loc.begin_line) {
|
|
||||||
for(u32 i=0; i<loc.begin_column; ++i) {
|
|
||||||
std::cerr<<char(" \t"[code[i]=='\t']);
|
|
||||||
}
|
|
||||||
for(u32 i=loc.begin_column; i<code.size(); ++i) {
|
|
||||||
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
|
|
||||||
}
|
|
||||||
} else if (loc.begin_line<line && line<loc.end_line) {
|
|
||||||
for(u32 i=0; i<code.size(); ++i) {
|
|
||||||
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(u32 i=0; i<loc.end_column; ++i) {
|
|
||||||
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (line==loc.end_line) {
|
|
||||||
std::cerr<<reset;
|
|
||||||
} else {
|
|
||||||
std::cerr<<reset<<"\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cerr<<"\n\n";
|
|
||||||
}
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream> // MSVC need this to use std::getline
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
|
||||||
|
struct span {
|
||||||
|
u32 begin_line;
|
||||||
|
u32 begin_column;
|
||||||
|
u32 end_line;
|
||||||
|
u32 end_column;
|
||||||
|
std::string file;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& back_white(std::ostream&);
|
||||||
|
std::ostream& red(std::ostream&);
|
||||||
|
std::ostream& cyan(std::ostream&);
|
||||||
|
std::ostream& orange(std::ostream&);
|
||||||
|
std::ostream& white(std::ostream&);
|
||||||
|
std::ostream& reset(std::ostream&);
|
||||||
|
|
||||||
|
class flstream {
|
||||||
|
protected:
|
||||||
|
std::string file;
|
||||||
|
std::vector<std::string> res;
|
||||||
|
public:
|
||||||
|
flstream():file("") {}
|
||||||
|
void load(const std::string&);
|
||||||
|
const std::string& operator[](usize n) const {return res[n];}
|
||||||
|
const std::string& name() const {return file;}
|
||||||
|
usize size() const {return res.size();}
|
||||||
|
};
|
||||||
|
|
||||||
|
class error:public flstream {
|
||||||
|
private:
|
||||||
|
u32 cnt; // counter for errors
|
||||||
|
|
||||||
|
std::string identation(usize len) {
|
||||||
|
return std::string(len,' ');
|
||||||
|
}
|
||||||
|
std::string leftpad(u32 num, usize len) {
|
||||||
|
auto tmp = std::to_string(num);
|
||||||
|
while(tmp.length()<len) {
|
||||||
|
tmp=" "+tmp;
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
error():cnt(0) {}
|
||||||
|
void fatal(const std::string&, const std::string&);
|
||||||
|
void err(const std::string&, const std::string&);
|
||||||
|
void err(const std::string&, const span&, const std::string&);
|
||||||
|
|
||||||
|
void chkerr() const {
|
||||||
|
if (cnt) {
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,577 @@
|
||||||
|
#include "nasal_gc.h"
|
||||||
|
|
||||||
|
void filehandle_destructor(void* ptr) {
|
||||||
|
if ((FILE*)ptr==stdin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fclose((FILE*)ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dir_entry_destructor(void* ptr) {
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
closedir((DIR*)ptr);
|
||||||
|
#else
|
||||||
|
FindClose(ptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void dylib_destructor(void* ptr) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
FreeLibrary((HMODULE)ptr);
|
||||||
|
#else
|
||||||
|
dlclose(ptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void func_addr_destructor(void* ptr) {}
|
||||||
|
|
||||||
|
var nas_vec::get_val(const i32 n) {
|
||||||
|
i32 size=elems.size();
|
||||||
|
if (n<-size || n>=size) {
|
||||||
|
return var::none();
|
||||||
|
}
|
||||||
|
return elems[n>=0?n:n+size];
|
||||||
|
}
|
||||||
|
|
||||||
|
var* nas_vec::get_mem(const i32 n) {
|
||||||
|
i32 size=elems.size();
|
||||||
|
if (n<-size || n>=size) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &elems[n>=0?n:n+size];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
||||||
|
if (!vec.elems.size() || vec.printed) {
|
||||||
|
out<<(vec.elems.size()?"[..]":"[]");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
vec.printed=true;
|
||||||
|
usize iter=0,size=vec.elems.size();
|
||||||
|
out<<'[';
|
||||||
|
for(auto& i:vec.elems) {
|
||||||
|
out<<i<<",]"[(++iter)==size];
|
||||||
|
}
|
||||||
|
vec.printed=false;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nas_hash::get_val(const std::string& key) {
|
||||||
|
if (elems.count(key)) {
|
||||||
|
return elems.at(key);
|
||||||
|
} else if (!elems.count("parents")) {
|
||||||
|
return var::none();
|
||||||
|
}
|
||||||
|
var ret=var::none();
|
||||||
|
var val=elems.at("parents");
|
||||||
|
if (val.type!=vm_vec) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for(auto& i:val.vec().elems) {
|
||||||
|
if (i.type==vm_hash) {
|
||||||
|
ret=i.hash().get_val(key);
|
||||||
|
}
|
||||||
|
if (ret.type!=vm_none) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var* nas_hash::get_mem(const std::string& key) {
|
||||||
|
if (elems.count(key)) {
|
||||||
|
return &elems.at(key);
|
||||||
|
} else if (!elems.count("parents")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
var* addr=nullptr;
|
||||||
|
var val=elems.at("parents");
|
||||||
|
if (val.type!=vm_vec) {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
for(auto& i:val.vec().elems) {
|
||||||
|
if (i.type==vm_hash) {
|
||||||
|
addr=i.hash().get_mem(key);
|
||||||
|
}
|
||||||
|
if (addr) {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
||||||
|
if (!hash.elems.size() || hash.printed) {
|
||||||
|
out<<(hash.elems.size()?"{..}":"{}");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
hash.printed=true;
|
||||||
|
usize iter=0,size=hash.elems.size();
|
||||||
|
out<<'{';
|
||||||
|
for(auto& i:hash.elems) {
|
||||||
|
out<<i.first<<':'<<i.second<<",}"[(++iter)==size];
|
||||||
|
}
|
||||||
|
hash.printed=false;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_func::clear() {
|
||||||
|
dpara=-1;
|
||||||
|
local.clear();
|
||||||
|
upval.clear();
|
||||||
|
keys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_ghost::set(usize t, void* p, ghost_register_table* table) {
|
||||||
|
type=t;
|
||||||
|
ptr=p;
|
||||||
|
ghost_type_table=table;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_ghost::clear() {
|
||||||
|
if (!ptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ghost_type_table->destructor(type)(ptr);
|
||||||
|
ptr=nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_co::clear() {
|
||||||
|
for(u32 i=0;i<STACK_DEPTH;++i) {
|
||||||
|
stack[i]=var::nil();
|
||||||
|
}
|
||||||
|
ctx.pc=0;
|
||||||
|
ctx.localr=nullptr;
|
||||||
|
ctx.memr=nullptr;
|
||||||
|
ctx.canary=stack+STACK_DEPTH-1;
|
||||||
|
ctx.top=stack;
|
||||||
|
ctx.funcr=var::nil();
|
||||||
|
ctx.upvalr=var::nil();
|
||||||
|
ctx.stack=stack;
|
||||||
|
|
||||||
|
status=coroutine_status::suspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_val::nas_val(u8 val_type) {
|
||||||
|
mark=gc_status::collected;
|
||||||
|
type=val_type;
|
||||||
|
unmut=0;
|
||||||
|
switch(val_type) {
|
||||||
|
case vm_str: ptr.str=new std::string; break;
|
||||||
|
case vm_vec: ptr.vec=new nas_vec; break;
|
||||||
|
case vm_hash: ptr.hash=new nas_hash; break;
|
||||||
|
case vm_func: ptr.func=new nas_func; break;
|
||||||
|
case vm_upval: ptr.upval=new nas_upval; break;
|
||||||
|
case vm_obj: ptr.obj=new nas_ghost; break;
|
||||||
|
case vm_co: ptr.co=new nas_co; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_val::~nas_val() {
|
||||||
|
switch(type) {
|
||||||
|
case vm_str: delete ptr.str; break;
|
||||||
|
case vm_vec: delete ptr.vec; break;
|
||||||
|
case vm_hash: delete ptr.hash; break;
|
||||||
|
case vm_func: delete ptr.func; break;
|
||||||
|
case vm_upval:delete ptr.upval;break;
|
||||||
|
case vm_obj: delete ptr.obj; break;
|
||||||
|
case vm_co: delete ptr.co; break;
|
||||||
|
}
|
||||||
|
type=vm_nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_val::clear() {
|
||||||
|
switch(type) {
|
||||||
|
case vm_str: ptr.str->clear(); break;
|
||||||
|
case vm_vec: ptr.vec->elems.clear(); break;
|
||||||
|
case vm_hash: ptr.hash->elems.clear();break;
|
||||||
|
case vm_func: ptr.func->clear(); break;
|
||||||
|
case vm_upval:ptr.upval->clear(); break;
|
||||||
|
case vm_obj: ptr.obj->clear(); break;
|
||||||
|
case vm_co: ptr.co->clear(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f64 var::tonum() {
|
||||||
|
return type!=vm_str? val.num:str2num(str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string var::tostr() {
|
||||||
|
if (type==vm_str) {
|
||||||
|
return str();
|
||||||
|
} else if (type==vm_num) {
|
||||||
|
std::string tmp=std::to_string(num());
|
||||||
|
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
|
||||||
|
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, var& ref) {
|
||||||
|
switch(ref.type) {
|
||||||
|
case vm_none: out<<"undefined"; break;
|
||||||
|
case vm_nil: out<<"nil"; break;
|
||||||
|
case vm_num: out<<ref.val.num; break;
|
||||||
|
case vm_str: out<<ref.str(); break;
|
||||||
|
case vm_vec: out<<ref.vec(); break;
|
||||||
|
case vm_hash: out<<ref.hash(); break;
|
||||||
|
case vm_func: out<<"func(..) {..}";break;
|
||||||
|
case vm_obj: out<<ref.obj(); break;
|
||||||
|
case vm_co: out<<"<coroutine>"; break;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool var::objchk(usize obj_type) {
|
||||||
|
return type==vm_obj && obj().type==obj_type && obj().ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::none() {
|
||||||
|
return {vm_none, (u32)0};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::nil() {
|
||||||
|
return {vm_nil, (u32)0};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::ret(u32 pc) {
|
||||||
|
return {vm_ret, pc};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::cnt(i64 n) {
|
||||||
|
return {vm_cnt, n};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::num(f64 n) {
|
||||||
|
return {vm_num, n};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::gcobj(nas_val* p) {
|
||||||
|
return {p->type, p};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::addr(var* p) {
|
||||||
|
return {vm_addr, p};
|
||||||
|
}
|
||||||
|
|
||||||
|
var* var::addr() {
|
||||||
|
return val.addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 var::ret() {
|
||||||
|
return val.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64& var::cnt() {
|
||||||
|
return val.cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
f64 var::num() {
|
||||||
|
return val.num;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& var::str() {
|
||||||
|
return *val.gcobj->ptr.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_vec& var::vec() {
|
||||||
|
return *val.gcobj->ptr.vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_hash& var::hash() {
|
||||||
|
return *val.gcobj->ptr.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_func& var::func() {
|
||||||
|
return *val.gcobj->ptr.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_upval& var::upval() {
|
||||||
|
return *val.gcobj->ptr.upval;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_ghost& var::obj() {
|
||||||
|
return *val.gcobj->ptr.obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_co& var::co() {
|
||||||
|
return *val.gcobj->ptr.co;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark() {
|
||||||
|
std::vector<var> bfs;
|
||||||
|
mark_context(bfs);
|
||||||
|
|
||||||
|
while(!bfs.empty()) {
|
||||||
|
var value=bfs.back();
|
||||||
|
bfs.pop_back();
|
||||||
|
if (value.type<=vm_num ||
|
||||||
|
value.val.gcobj->mark!=gc_status::uncollected) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mark_var(bfs, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark_context(std::vector<var>& bfs_queue) {
|
||||||
|
|
||||||
|
// scan now running context, this context maybe related to coroutine or main
|
||||||
|
for(var* i=rctx->stack;i<=rctx->top;++i) {
|
||||||
|
bfs_queue.push_back(*i);
|
||||||
|
}
|
||||||
|
bfs_queue.push_back(rctx->funcr);
|
||||||
|
bfs_queue.push_back(rctx->upvalr);
|
||||||
|
bfs_queue.push_back(temp);
|
||||||
|
|
||||||
|
if (!cort) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// coroutine is running, so scan main process stack from mctx
|
||||||
|
for(var* i=mctx.stack;i<=mctx.top;++i) {
|
||||||
|
bfs_queue.push_back(*i);
|
||||||
|
}
|
||||||
|
bfs_queue.push_back(mctx.funcr);
|
||||||
|
bfs_queue.push_back(mctx.upvalr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
|
||||||
|
value.val.gcobj->mark=gc_status::found;
|
||||||
|
switch(value.type) {
|
||||||
|
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
|
||||||
|
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
|
||||||
|
case vm_func: mark_func(bfs_queue, value.func()); break;
|
||||||
|
case vm_upval: mark_upval(bfs_queue, value.upval()); break;
|
||||||
|
case vm_co: mark_co(bfs_queue, value.co()); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
|
||||||
|
for(auto& i:vec.elems) {
|
||||||
|
bfs_queue.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
|
||||||
|
for(auto& i:hash.elems) {
|
||||||
|
bfs_queue.push_back(i.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
|
||||||
|
for(auto& i:function.local) {
|
||||||
|
bfs_queue.push_back(i);
|
||||||
|
}
|
||||||
|
for(auto& i:function.upval) {
|
||||||
|
bfs_queue.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
|
||||||
|
for(auto& i:upval.elems) {
|
||||||
|
bfs_queue.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
|
||||||
|
bfs_queue.push_back(co.ctx.funcr);
|
||||||
|
bfs_queue.push_back(co.ctx.upvalr);
|
||||||
|
for(var* i=co.stack;i<=co.ctx.top;++i) {
|
||||||
|
bfs_queue.push_back(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::sweep() {
|
||||||
|
for(auto i:memory) {
|
||||||
|
if (i->mark==gc_status::uncollected) {
|
||||||
|
i->clear();
|
||||||
|
unused[i->type-vm_str].push_back(i);
|
||||||
|
i->mark=gc_status::collected;
|
||||||
|
} else if (i->mark==gc_status::found) {
|
||||||
|
i->mark=gc_status::uncollected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::extend(u8 type) {
|
||||||
|
const u8 index=type-vm_str;
|
||||||
|
size[index]+=incr[index];
|
||||||
|
|
||||||
|
for(u32 i=0;i<incr[index];++i) {
|
||||||
|
nas_val* tmp=new(std::nothrow) nas_val(type);
|
||||||
|
|
||||||
|
if (!tmp) {
|
||||||
|
std::cerr<<"nasal_gc.h: gc::extend: ";
|
||||||
|
std::cerr<<"failed to allocate memory\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to heap
|
||||||
|
memory.push_back(tmp);
|
||||||
|
unused[index].push_back(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
incr[index]=incr[index]+incr[index]/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::init(
|
||||||
|
const std::vector<std::string>& s, const std::vector<std::string>& argv) {
|
||||||
|
// initialize function register
|
||||||
|
rctx->funcr=nil;
|
||||||
|
worktime=0;
|
||||||
|
|
||||||
|
// initialize counters
|
||||||
|
for(u8 i=0;i<gc_type_size;++i) {
|
||||||
|
size[i]=gcnt[i]=acnt[i]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// coroutine pointer set to nullptr
|
||||||
|
cort=nullptr;
|
||||||
|
|
||||||
|
// init constant strings
|
||||||
|
strs.resize(s.size());
|
||||||
|
for(u32 i=0;i<strs.size();++i) {
|
||||||
|
strs[i]=var::gcobj(new nas_val(vm_str));
|
||||||
|
strs[i].val.gcobj->unmut=1;
|
||||||
|
strs[i].str()=s[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// record arguments
|
||||||
|
env_argv.resize(argv.size());
|
||||||
|
for(usize i=0;i<argv.size();++i) {
|
||||||
|
env_argv[i]=var::gcobj(new nas_val(vm_str));
|
||||||
|
env_argv[i].val.gcobj->unmut=1;
|
||||||
|
env_argv[i].str()=argv[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::clear() {
|
||||||
|
for(auto i:memory) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
memory.clear();
|
||||||
|
for(u8 i=0;i<gc_type_size;++i) {
|
||||||
|
unused[i].clear();
|
||||||
|
}
|
||||||
|
for(auto& i:strs) {
|
||||||
|
delete i.val.gcobj;
|
||||||
|
}
|
||||||
|
strs.clear();
|
||||||
|
env_argv.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::info() {
|
||||||
|
using std::left;
|
||||||
|
using std::setw;
|
||||||
|
using std::setfill;
|
||||||
|
const char* name[] = {
|
||||||
|
"string ",
|
||||||
|
"vector ",
|
||||||
|
"hashmap ",
|
||||||
|
"function ",
|
||||||
|
"upvalue ",
|
||||||
|
"object ",
|
||||||
|
"coroutine"
|
||||||
|
};
|
||||||
|
|
||||||
|
usize indent=0;
|
||||||
|
for(u8 i=0;i<gc_type_size;++i) {
|
||||||
|
usize len = 0;
|
||||||
|
len = std::to_string(gcnt[i]).length();
|
||||||
|
indent = indent<len? len:indent;
|
||||||
|
len = std::to_string(acnt[i]).length();
|
||||||
|
indent = indent<len? len:indent;
|
||||||
|
len = std::to_string(size[i]).length();
|
||||||
|
indent = indent<len? len:indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
double total=0;
|
||||||
|
std::clog << "\ngc info (gc count|alloc count|memory size)\n";
|
||||||
|
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||||
|
if (!gcnt[i] && !acnt[i] && !size[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
total += gcnt[i];
|
||||||
|
std::clog << " " << name[i];
|
||||||
|
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i];
|
||||||
|
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
|
||||||
|
std::clog << " | " << left << setw(indent) << setfill(' ') << size[i];
|
||||||
|
std::clog << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||||
|
std::clog << " gc time | " << worktime*1.0/den*1000 << " ms\n";
|
||||||
|
if (total) {
|
||||||
|
std::clog << " avg time | " << worktime*1.0/den*1000/total << " ms\n";
|
||||||
|
std::clog << " max gc | " << max_time*1.0/den*1000 << " ms\n";
|
||||||
|
std::clog << " max mark | " << max_mark_time*1.0/den*1000 << " ms\n";
|
||||||
|
std::clog << " max sweep | " << max_sweep_time*1.0/den*1000 << " ms\n";
|
||||||
|
}
|
||||||
|
std::clog<<"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
var gc::alloc(u8 type) {
|
||||||
|
using clk = std::chrono::high_resolution_clock;
|
||||||
|
const u8 index = type-vm_str;
|
||||||
|
++acnt[index];
|
||||||
|
if (unused[index].empty()) {
|
||||||
|
++gcnt[index];
|
||||||
|
auto begin = clk::now();
|
||||||
|
mark();
|
||||||
|
auto mark_end = clk::now();
|
||||||
|
sweep();
|
||||||
|
auto sweep_end = clk::now();
|
||||||
|
|
||||||
|
auto total_time = (sweep_end-begin).count();
|
||||||
|
auto mark_time = (mark_end-begin).count();
|
||||||
|
auto sweep_time = (sweep_end-mark_end).count();
|
||||||
|
worktime += total_time;
|
||||||
|
max_time = max_time<total_time? total_time:max_time;
|
||||||
|
max_mark_time = max_mark_time<mark_time? mark_time:max_mark_time;
|
||||||
|
max_sweep_time = max_sweep_time<sweep_time? sweep_time:max_sweep_time;
|
||||||
|
}
|
||||||
|
if (unused[index].empty()) {
|
||||||
|
extend(type);
|
||||||
|
}
|
||||||
|
var ret=var::gcobj(unused[index].back());
|
||||||
|
ret.val.gcobj->mark=gc_status::uncollected;
|
||||||
|
unused[index].pop_back();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::ctxchg(nas_co& co) {
|
||||||
|
// store running state to main context
|
||||||
|
mctx=*rctx;
|
||||||
|
|
||||||
|
// restore coroutine context state
|
||||||
|
*rctx=co.ctx;
|
||||||
|
|
||||||
|
// set coroutine pointer
|
||||||
|
cort=&co;
|
||||||
|
|
||||||
|
// set coroutine state to running
|
||||||
|
cort->status=coroutine_status::running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc::ctxreserve() {
|
||||||
|
// pc=0 means this coroutine is finished
|
||||||
|
cort->status=rctx->pc?
|
||||||
|
coroutine_status::suspended:
|
||||||
|
coroutine_status::dead;
|
||||||
|
|
||||||
|
// store running state to coroutine
|
||||||
|
cort->ctx=*rctx;
|
||||||
|
|
||||||
|
// restore main context state
|
||||||
|
*rctx=mctx;
|
||||||
|
|
||||||
|
// set coroutine pointer to nullptr
|
||||||
|
cort=nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nas_err(const std::string& error_function_name, const std::string& info) {
|
||||||
|
std::cerr<<"[vm] "<<error_function_name<<": "<<info<<"\n";
|
||||||
|
return var::none();
|
||||||
|
}
|
|
@ -0,0 +1,419 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// avoid MSVC warnings
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (disable:4244)
|
||||||
|
#pragma warning (disable:4267)
|
||||||
|
#pragma warning (disable:4102)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#else
|
||||||
|
#include <io.h>
|
||||||
|
#include <direct.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <thread>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
|
||||||
|
enum vm_type:u8 {
|
||||||
|
/* none-gc object */
|
||||||
|
vm_none=0,
|
||||||
|
vm_cnt,
|
||||||
|
vm_addr,
|
||||||
|
vm_ret,
|
||||||
|
vm_nil,
|
||||||
|
vm_num,
|
||||||
|
/* gc object */
|
||||||
|
vm_str,
|
||||||
|
vm_vec,
|
||||||
|
vm_hash,
|
||||||
|
vm_func,
|
||||||
|
vm_upval,
|
||||||
|
vm_obj,
|
||||||
|
vm_co
|
||||||
|
};
|
||||||
|
|
||||||
|
const u32 gc_type_size=vm_co-vm_str+1;
|
||||||
|
|
||||||
|
enum class coroutine_status:u32 {
|
||||||
|
suspended,
|
||||||
|
running,
|
||||||
|
dead
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class gc_status:u8 {
|
||||||
|
uncollected=0,
|
||||||
|
collected,
|
||||||
|
found
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_vec; // vector
|
||||||
|
struct nas_hash; // hashmap(dict)
|
||||||
|
struct nas_func; // function(lambda)
|
||||||
|
struct nas_upval; // upvalue
|
||||||
|
struct nas_ghost; // objects
|
||||||
|
struct nas_co; // coroutine
|
||||||
|
struct nas_val; // nas_val includes gc-managed types
|
||||||
|
|
||||||
|
struct var {
|
||||||
|
public:
|
||||||
|
u8 type;
|
||||||
|
union {
|
||||||
|
u32 ret;
|
||||||
|
i64 cnt;
|
||||||
|
f64 num;
|
||||||
|
var* addr;
|
||||||
|
nas_val* gcobj;
|
||||||
|
} val;
|
||||||
|
|
||||||
|
private:
|
||||||
|
var(u8 t, u32 pc) {type=t;val.ret=pc;}
|
||||||
|
var(u8 t, i64 ct) {type=t;val.cnt=ct;}
|
||||||
|
var(u8 t, f64 n) {type=t;val.num=n;}
|
||||||
|
var(u8 t, var* p) {type=t;val.addr=p;}
|
||||||
|
var(u8 t, nas_val* p) {type=t;val.gcobj=p;}
|
||||||
|
|
||||||
|
public:
|
||||||
|
var() = default;
|
||||||
|
var(const var&) = default;
|
||||||
|
bool operator==(const var& nr) const {
|
||||||
|
return type==nr.type && val.gcobj==nr.val.gcobj;
|
||||||
|
}
|
||||||
|
bool operator!=(const var& nr) const {
|
||||||
|
return type!=nr.type || val.gcobj!=nr.val.gcobj;
|
||||||
|
}
|
||||||
|
friend std::ostream& operator<<(std::ostream&, var&);
|
||||||
|
|
||||||
|
// number and string can be translated to each other
|
||||||
|
f64 tonum();
|
||||||
|
std::string tostr();
|
||||||
|
bool objchk(usize);
|
||||||
|
|
||||||
|
// create new var object
|
||||||
|
static var none();
|
||||||
|
static var nil();
|
||||||
|
static var ret(u32);
|
||||||
|
static var cnt(i64);
|
||||||
|
static var num(f64);
|
||||||
|
static var gcobj(nas_val*);
|
||||||
|
static var addr(var*);
|
||||||
|
|
||||||
|
// get content
|
||||||
|
var* addr();
|
||||||
|
u32 ret();
|
||||||
|
i64& cnt();
|
||||||
|
f64 num();
|
||||||
|
std::string& str();
|
||||||
|
nas_vec& vec();
|
||||||
|
nas_hash& hash();
|
||||||
|
nas_func& func();
|
||||||
|
nas_upval& upval();
|
||||||
|
nas_ghost& obj();
|
||||||
|
nas_co& co();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_vec {
|
||||||
|
std::vector<var> elems;
|
||||||
|
|
||||||
|
// mark if this is printed, avoid stackoverflow
|
||||||
|
bool printed;
|
||||||
|
|
||||||
|
nas_vec():printed(false) {}
|
||||||
|
usize size() const {return elems.size();}
|
||||||
|
var get_val(const i32);
|
||||||
|
var* get_mem(const i32);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_hash {
|
||||||
|
std::unordered_map<std::string, var> elems;
|
||||||
|
|
||||||
|
// mark if this is printed, avoid stackoverflow
|
||||||
|
bool printed;
|
||||||
|
|
||||||
|
nas_hash():printed(false) {}
|
||||||
|
usize size() const {return elems.size();}
|
||||||
|
var get_val(const std::string&);
|
||||||
|
var* get_mem(const std::string&);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_func {
|
||||||
|
i32 dpara; // dynamic parameter name index in hash.
|
||||||
|
u32 entry; // pc will set to entry-1 to call this function
|
||||||
|
u32 psize; // used to load default parameters to a new function
|
||||||
|
u32 lsize; // used to expand memory space for local values on stack
|
||||||
|
std::vector<var> local; // local scope with default value(var)
|
||||||
|
std::vector<var> upval; // closure
|
||||||
|
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
|
||||||
|
|
||||||
|
nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {}
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_upval {
|
||||||
|
public:
|
||||||
|
/* on stack, use these variables */
|
||||||
|
bool onstk;
|
||||||
|
u32 size;
|
||||||
|
var* stk;
|
||||||
|
|
||||||
|
/* not on stack, use this */
|
||||||
|
std::vector<var> elems;
|
||||||
|
|
||||||
|
public:
|
||||||
|
nas_upval(): onstk(true), size(0), stk(nullptr) {}
|
||||||
|
|
||||||
|
var& operator[](usize n) {
|
||||||
|
return onstk? stk[n]:elems[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
onstk=true;
|
||||||
|
elems.clear();
|
||||||
|
size=0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void filehandle_destructor(void*);
|
||||||
|
void dir_entry_destructor(void*);
|
||||||
|
void dylib_destructor(void*);
|
||||||
|
void func_addr_destructor(void*);
|
||||||
|
|
||||||
|
struct ghost_register_table {
|
||||||
|
private:
|
||||||
|
using dtor=void (*)(void*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, usize> mapper;
|
||||||
|
std::vector<std::string> ghost_name;
|
||||||
|
std::vector<dtor> destructors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// reserved ghost type only for native functions
|
||||||
|
usize ghost_file;
|
||||||
|
usize ghost_dir;
|
||||||
|
usize ghost_dylib;
|
||||||
|
usize ghost_faddr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ghost_register_table() {
|
||||||
|
ghost_file = register_ghost_type("file", filehandle_destructor);
|
||||||
|
ghost_dir = register_ghost_type("dir", dir_entry_destructor);
|
||||||
|
ghost_dylib = register_ghost_type("dylib", dylib_destructor);
|
||||||
|
ghost_faddr = register_ghost_type("faddr", func_addr_destructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exists(const std::string& name) const {
|
||||||
|
return mapper.count(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
usize get_ghost_type_index(const std::string& name) const {
|
||||||
|
return mapper.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& get_ghost_name(usize index) const {
|
||||||
|
return ghost_name.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
usize register_ghost_type(const std::string& name, dtor ptr) {
|
||||||
|
if (mapper.count(name)) {
|
||||||
|
std::cerr<<"nasal_gc.h: ghost_register_table::register_ghost_type: ";
|
||||||
|
std::cerr<<"ghost type \""<<name<<"\" already exists.\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
auto res=destructors.size();
|
||||||
|
mapper[name]=res;
|
||||||
|
ghost_name.push_back(name);
|
||||||
|
destructors.push_back(ptr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
dtor destructor(usize index) {
|
||||||
|
return destructors.at(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_ghost {
|
||||||
|
public:
|
||||||
|
usize type;
|
||||||
|
void* ptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ghost_register_table* ghost_type_table;
|
||||||
|
|
||||||
|
public:
|
||||||
|
nas_ghost(): type(0), ptr(nullptr), ghost_type_table(nullptr) {}
|
||||||
|
~nas_ghost() {clear();}
|
||||||
|
void set(usize, void*, ghost_register_table*);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
public:
|
||||||
|
friend std::ostream& operator<<(std::ostream& out, nas_ghost& ghost) {
|
||||||
|
out<<"<object "<<ghost.ghost_type_table->get_ghost_name(ghost.type);
|
||||||
|
out<<" at 0x"<<std::hex<<(u64)ghost.ptr<<std::dec<<">";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& get_ghost_name() const {
|
||||||
|
return ghost_type_table->get_ghost_name(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct context {
|
||||||
|
u32 pc;
|
||||||
|
var* localr;
|
||||||
|
var* memr;
|
||||||
|
var funcr;
|
||||||
|
var upvalr;
|
||||||
|
var* canary;
|
||||||
|
var* stack;
|
||||||
|
var* top;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_co {
|
||||||
|
var stack[STACK_DEPTH];
|
||||||
|
context ctx;
|
||||||
|
coroutine_status status;
|
||||||
|
|
||||||
|
nas_co() {clear();}
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_val {
|
||||||
|
gc_status mark;
|
||||||
|
u8 type; // value type
|
||||||
|
u8 unmut; // used to mark if a string is unmutable
|
||||||
|
union {
|
||||||
|
std::string* str;
|
||||||
|
nas_vec* vec;
|
||||||
|
nas_hash* hash;
|
||||||
|
nas_func* func;
|
||||||
|
nas_upval* upval;
|
||||||
|
nas_ghost* obj;
|
||||||
|
nas_co* co;
|
||||||
|
} ptr;
|
||||||
|
|
||||||
|
nas_val(u8);
|
||||||
|
~nas_val();
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream&, nas_vec&);
|
||||||
|
std::ostream& operator<<(std::ostream&, nas_hash&);
|
||||||
|
std::ostream& operator<<(std::ostream&, var&);
|
||||||
|
|
||||||
|
const var zero = var::num(0);
|
||||||
|
const var one = var::num(1);
|
||||||
|
const var nil = var::nil();
|
||||||
|
|
||||||
|
struct gc {
|
||||||
|
ghost_register_table global_ghost_type_table;
|
||||||
|
/* main context temporary storage */
|
||||||
|
context mctx;
|
||||||
|
|
||||||
|
/* runtime context */
|
||||||
|
context* rctx = nullptr;
|
||||||
|
nas_co* cort = nullptr; // running coroutine
|
||||||
|
|
||||||
|
/* temporary space used in builtin/module functions */
|
||||||
|
var temp=nil;
|
||||||
|
|
||||||
|
/* constants and memory pool */
|
||||||
|
std::vector<var> strs; // reserved address for const vm_str
|
||||||
|
std::vector<var> env_argv; // command line arguments
|
||||||
|
std::vector<nas_val*> memory; // gc memory
|
||||||
|
std::vector<nas_val*> unused[gc_type_size]; // gc free list
|
||||||
|
|
||||||
|
/* heap increase size */
|
||||||
|
u32 incr[gc_type_size] = {
|
||||||
|
128, // vm_str
|
||||||
|
128, // vm_vec
|
||||||
|
64, // vm_hash
|
||||||
|
128, // vm_func
|
||||||
|
256, // vm_upval
|
||||||
|
16, // vm_obj
|
||||||
|
16 // vm_co
|
||||||
|
};
|
||||||
|
|
||||||
|
/* values for analysis */
|
||||||
|
u64 size[gc_type_size];
|
||||||
|
u64 gcnt[gc_type_size];
|
||||||
|
u64 acnt[gc_type_size];
|
||||||
|
i64 worktime = 0;
|
||||||
|
i64 max_time = 0;
|
||||||
|
i64 max_mark_time = 0;
|
||||||
|
i64 max_sweep_time = 0;
|
||||||
|
|
||||||
|
gc(context* _ctx): rctx(_ctx) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* gc functions */
|
||||||
|
void mark();
|
||||||
|
void mark_context(std::vector<var>&);
|
||||||
|
void mark_var(std::vector<var>&, var&);
|
||||||
|
void mark_vec(std::vector<var>&, nas_vec&);
|
||||||
|
void mark_hash(std::vector<var>&, nas_hash&);
|
||||||
|
void mark_func(std::vector<var>&, nas_func&);
|
||||||
|
void mark_upval(std::vector<var>&, nas_upval&);
|
||||||
|
void mark_co(std::vector<var>&, nas_co&);
|
||||||
|
void sweep();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void extend(u8);
|
||||||
|
void init(const std::vector<std::string>&, const std::vector<std::string>&);
|
||||||
|
void clear();
|
||||||
|
void info();
|
||||||
|
var alloc(const u8);
|
||||||
|
void ctxchg(nas_co&);
|
||||||
|
void ctxreserve();
|
||||||
|
|
||||||
|
public:
|
||||||
|
var newstr(char c) {
|
||||||
|
var s=alloc(vm_str);
|
||||||
|
s.str()=c;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newstr(const char* buff) {
|
||||||
|
var s=alloc(vm_str);
|
||||||
|
s.str()=buff;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newstr(const std::string& buff) {
|
||||||
|
var s=alloc(vm_str);
|
||||||
|
s.str()=buff;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// use to print error log and return error value
|
||||||
|
var nas_err(const std::string&, const std::string&);
|
||||||
|
|
||||||
|
// module function type
|
||||||
|
typedef var (*module_func)(var*, usize, gc*);
|
||||||
|
|
||||||
|
// module function stores in tables with this type, end with {nullptr,nullptr}
|
||||||
|
struct module_func_info {
|
||||||
|
const char* name;
|
||||||
|
module_func fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
// module function "get" type
|
||||||
|
typedef module_func_info* (*get_func_ptr)(ghost_register_table*);
|
|
@ -0,0 +1,229 @@
|
||||||
|
#include "nasal_import.h"
|
||||||
|
|
||||||
|
linker::linker(error& e): show_path(false), lib_loaded(false), err(e) {
|
||||||
|
char sep=is_windows()? ';':':';
|
||||||
|
std::string PATH=getenv("PATH");
|
||||||
|
usize last=0;
|
||||||
|
usize pos=PATH.find(sep, 0);
|
||||||
|
while(pos!=std::string::npos) {
|
||||||
|
std::string dirpath=PATH.substr(last, pos-last);
|
||||||
|
if (dirpath.length()) {
|
||||||
|
envpath.push_back(dirpath);
|
||||||
|
}
|
||||||
|
last=pos+1;
|
||||||
|
pos=PATH.find(sep, last);
|
||||||
|
}
|
||||||
|
if (last!=PATH.length()) {
|
||||||
|
envpath.push_back(PATH.substr(last));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string linker::get_path(call_expr* node) {
|
||||||
|
if (node->get_calls()[0]->get_type()==expr_type::ast_callf) {
|
||||||
|
auto tmp = (call_function*)node->get_calls()[0];
|
||||||
|
return ((string_literal*)tmp->get_argument()[0])->get_content();
|
||||||
|
}
|
||||||
|
auto fpath = std::string(".");
|
||||||
|
for(auto i : node->get_calls()) {
|
||||||
|
fpath += (is_windows()? "\\":"/") + ((call_hash*)i)->get_field();
|
||||||
|
}
|
||||||
|
return fpath + ".nas";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string linker::find_file(
|
||||||
|
const std::string& filename, const span& location) {
|
||||||
|
// first add file name itself into the file path
|
||||||
|
std::vector<std::string> fpath = {filename};
|
||||||
|
|
||||||
|
// generate search path from environ path
|
||||||
|
for(const auto& p : envpath) {
|
||||||
|
fpath.push_back(p + (is_windows()? "\\":"/") + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// search file
|
||||||
|
for(const auto& i : fpath) {
|
||||||
|
if (access(i.c_str(), F_OK)!=-1) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we will find lib.nas in nasal std directory
|
||||||
|
if (filename=="lib.nas") {
|
||||||
|
return is_windows()?
|
||||||
|
find_file("std\\lib.nas", location):
|
||||||
|
find_file("std/lib.nas", location);
|
||||||
|
}
|
||||||
|
if (!show_path) {
|
||||||
|
err.load(location.file);
|
||||||
|
err.err("link", "cannot find file <" + filename + ">");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string paths = "";
|
||||||
|
for(const auto& i : fpath) {
|
||||||
|
paths += " " + i + "\n";
|
||||||
|
}
|
||||||
|
err.load(location.file);
|
||||||
|
err.err("link", "cannot find file <" + filename + "> in these paths:\n" + paths);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool linker::import_check(expr* node) {
|
||||||
|
/*
|
||||||
|
call
|
||||||
|
|_id:import
|
||||||
|
|_callh:std
|
||||||
|
|_callh:file
|
||||||
|
*/
|
||||||
|
if (node->get_type()!=expr_type::ast_call) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto tmp = (call_expr*)node;
|
||||||
|
if (tmp->get_first()->get_type()!=expr_type::ast_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (((identifier*)tmp->get_first())->get_name()!="import") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!tmp->get_calls().size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// import.xxx.xxx;
|
||||||
|
if (tmp->get_calls()[0]->get_type()==expr_type::ast_callh) {
|
||||||
|
for(auto i : tmp->get_calls()) {
|
||||||
|
if (i->get_type()!=expr_type::ast_callh) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// import("xxx");
|
||||||
|
if (tmp->get_calls().size()!=1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
call
|
||||||
|
|_id:import
|
||||||
|
|_call_func
|
||||||
|
|_string:'filename'
|
||||||
|
*/
|
||||||
|
if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto func_call = (call_function*)tmp->get_calls()[0];
|
||||||
|
if (func_call->get_argument().size()!=1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool linker::exist(const std::string& file) {
|
||||||
|
// avoid importing the same file
|
||||||
|
for(const auto& fname : files) {
|
||||||
|
if (file==fname) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files.push_back(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
|
||||||
|
// add children of add_root to the back of root
|
||||||
|
for(auto& i:old_tree_root->get_expressions()) {
|
||||||
|
new_tree_root->add_expression(i);
|
||||||
|
}
|
||||||
|
// clean old root
|
||||||
|
old_tree_root->get_expressions().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
code_block* linker::import_regular_file(call_expr* node) {
|
||||||
|
lexer lex(err);
|
||||||
|
parse par(err);
|
||||||
|
// get filename
|
||||||
|
auto filename = get_path(node);
|
||||||
|
// clear this node
|
||||||
|
for(auto i : node->get_calls()) {
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
node->get_calls().clear();
|
||||||
|
auto location = node->get_first()->get_location();
|
||||||
|
delete node->get_first();
|
||||||
|
node->set_first(new nil_expr(location));
|
||||||
|
|
||||||
|
// avoid infinite loading loop
|
||||||
|
filename = find_file(filename, node->get_location());
|
||||||
|
if (!filename.length() || exist(filename)) {
|
||||||
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
|
}
|
||||||
|
|
||||||
|
// start importing...
|
||||||
|
lex.scan(filename);
|
||||||
|
par.compile(lex);
|
||||||
|
auto tmp = par.swap(nullptr);
|
||||||
|
|
||||||
|
// check if tmp has 'import'
|
||||||
|
return load(tmp, files.size()-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
code_block* linker::import_nasal_lib() {
|
||||||
|
lexer lex(err);
|
||||||
|
parse par(err);
|
||||||
|
auto filename = find_file("lib.nas", {0, 0, 0, 0, files[0]});
|
||||||
|
if (!filename.length()) {
|
||||||
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid infinite loading loop
|
||||||
|
if (exist(filename)) {
|
||||||
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
|
}
|
||||||
|
|
||||||
|
// start importing...
|
||||||
|
lex.scan(filename);
|
||||||
|
par.compile(lex);
|
||||||
|
auto tmp = par.swap(nullptr);
|
||||||
|
|
||||||
|
// check if tmp has 'import'
|
||||||
|
return load(tmp, files.size()-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
code_block* linker::load(code_block* root, u16 fileindex) {
|
||||||
|
auto tree = new code_block({0, 0, 0, 0, files[fileindex]});
|
||||||
|
if (!lib_loaded) {
|
||||||
|
auto tmp = import_nasal_lib();
|
||||||
|
link(tree, tmp);
|
||||||
|
delete tmp;
|
||||||
|
lib_loaded = true;
|
||||||
|
}
|
||||||
|
for(auto i : root->get_expressions()) {
|
||||||
|
if (!import_check(i)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto tmp = import_regular_file((call_expr*)i);
|
||||||
|
link(tree, tmp);
|
||||||
|
delete tmp;
|
||||||
|
}
|
||||||
|
// add root to the back of tree
|
||||||
|
auto file_head = new file_info(
|
||||||
|
{0, 0, 0, 0, files[fileindex]}, fileindex, files[fileindex]);
|
||||||
|
tree->add_expression(file_head);
|
||||||
|
link(tree, root);
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
const error& linker::link(
|
||||||
|
parse& parse, const std::string& self, bool spath = false) {
|
||||||
|
show_path = spath;
|
||||||
|
// initializing
|
||||||
|
files = {self};
|
||||||
|
// scan root and import files
|
||||||
|
// then generate a new ast and return to import_ast
|
||||||
|
// the main file's index is 0
|
||||||
|
auto new_tree_root = load(parse.tree(), 0);
|
||||||
|
auto old_tree_root = parse.swap(new_tree_root);
|
||||||
|
delete old_tree_root;
|
||||||
|
return err;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#else
|
||||||
|
#define _CRT_SECURE_NO_DEPRECATE 1
|
||||||
|
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
||||||
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define F_OK 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
#include "nasal_lexer.h"
|
||||||
|
#include "nasal_parse.h"
|
||||||
|
|
||||||
|
class linker{
|
||||||
|
private:
|
||||||
|
bool show_path;
|
||||||
|
bool lib_loaded;
|
||||||
|
error& err;
|
||||||
|
std::vector<std::string> files;
|
||||||
|
std::vector<std::string> envpath;
|
||||||
|
|
||||||
|
bool import_check(expr*);
|
||||||
|
bool exist(const std::string&);
|
||||||
|
void link(code_block*, code_block*);
|
||||||
|
std::string get_path(call_expr*);
|
||||||
|
std::string find_file(const std::string&, const span&);
|
||||||
|
code_block* import_regular_file(call_expr*);
|
||||||
|
code_block* import_nasal_lib();
|
||||||
|
code_block* load(code_block*, u16);
|
||||||
|
public:
|
||||||
|
linker(error&);
|
||||||
|
const error& link(parse&, const std::string&, bool);
|
||||||
|
const std::vector<std::string>& filelist() const {return files;}
|
||||||
|
};
|
|
@ -1,506 +1,333 @@
|
||||||
#pragma once
|
#ifdef _MSC_VER
|
||||||
#ifdef _MSC_VER
|
#pragma warning (disable:4244)
|
||||||
#pragma warning (disable:4244)
|
#pragma warning (disable:4267)
|
||||||
#pragma warning (disable:4267)
|
#pragma warning (disable:4102)
|
||||||
#pragma warning (disable:4102)
|
#endif
|
||||||
#endif
|
|
||||||
|
#include "nasal_lexer.h"
|
||||||
#include <cstring>
|
|
||||||
#include <sstream>
|
bool lexer::skip(char c) {
|
||||||
#include <vector>
|
return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0;
|
||||||
#include <unordered_map>
|
}
|
||||||
#include <sys/stat.h>
|
|
||||||
|
bool lexer::is_id(char c) {
|
||||||
#include "nasal.h"
|
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
|
||||||
#include "nasal_err.h"
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
bool lexer::is_hex(char c) {
|
||||||
#define S_ISREG(m) (((m)&0xF000)==0x8000)
|
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
|
||||||
#endif
|
}
|
||||||
|
|
||||||
enum class tok:u32 {
|
bool lexer::is_oct(char c) {
|
||||||
null=0, // null token (default token type)
|
return '0'<=c&&c<='7';
|
||||||
num, // number literal
|
}
|
||||||
str, // string literal
|
|
||||||
id, // identifier
|
bool lexer::is_dec(char c) {
|
||||||
tktrue, // keyword true
|
return '0'<=c&&c<='9';
|
||||||
tkfalse, // keyword false
|
}
|
||||||
rfor, // loop keyword for
|
|
||||||
forindex, // loop keyword forindex
|
bool lexer::is_str(char c) {
|
||||||
foreach, // loop keyword foreach
|
return c=='\''||c=='\"'||c=='`';
|
||||||
rwhile, // loop keyword while
|
}
|
||||||
var, // keyword for definition
|
|
||||||
func, // keyword for definition of function
|
bool lexer::is_single_opr(char c) {
|
||||||
brk, // loop keyword break
|
return (
|
||||||
cont, // loop keyword continue
|
c=='('||c==')'||c=='['||c==']'||
|
||||||
ret, // function keyword return
|
c=='{'||c=='}'||c==','||c==';'||
|
||||||
rif, // condition expression keyword if
|
c==':'||c=='?'||c=='`'||c=='@'||
|
||||||
elsif, // condition expression keyword elsif
|
c=='%'||c=='$'||c=='\\'
|
||||||
relse, // condition expression keyword else
|
);
|
||||||
tknil, // nil literal
|
}
|
||||||
lcurve, // (
|
|
||||||
rcurve, // )
|
bool lexer::is_calc_opr(char c) {
|
||||||
lbracket, // [
|
return (
|
||||||
rbracket, // ]
|
c=='='||c=='+'||c=='-'||c=='*'||
|
||||||
lbrace, // {
|
c=='!'||c=='/'||c=='<'||c=='>'||
|
||||||
rbrace, // }
|
c=='~'||c=='|'||c=='&'||c=='^'
|
||||||
semi, // ;
|
);
|
||||||
opand, // operator and
|
}
|
||||||
opor, // operator or
|
|
||||||
comma, // ,
|
void lexer::skip_note() {
|
||||||
dot, // .
|
// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
|
||||||
ellipsis, // ...
|
while(++ptr<res.size() && res[ptr]!='\n') {}
|
||||||
quesmark, // ?
|
}
|
||||||
colon, // :
|
|
||||||
add, // operator +
|
void lexer::err_char() {
|
||||||
sub, // operator -
|
++column;
|
||||||
mult, // operator *
|
char c=res[ptr++];
|
||||||
div, // operator /
|
err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c));
|
||||||
floater, // operator ~ and binary operator ~
|
err.fatal("lexer", "fatal error occurred, stop");
|
||||||
btand, // bitwise operator &
|
}
|
||||||
btor, // bitwise operator |
|
|
||||||
btxor, // bitwise operator ^
|
void lexer::open(const std::string& file) {
|
||||||
opnot, // operator !
|
// check file exsits and it is a regular file
|
||||||
eq, // operator =
|
struct stat buffer;
|
||||||
addeq, // operator +=
|
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
|
||||||
subeq, // operator -=
|
err.err("lexer", "<"+file+"> is not a regular file");
|
||||||
multeq, // operator *=
|
err.chkerr();
|
||||||
diveq, // operator /=
|
}
|
||||||
lnkeq, // operator ~=
|
|
||||||
btandeq, // operator &=
|
// load
|
||||||
btoreq, // operator |=
|
filename = file;
|
||||||
btxoreq, // operator ^=
|
std::ifstream in(file, std::ios::binary);
|
||||||
cmpeq, // operator ==
|
if (in.fail()) {
|
||||||
neq, // operator !=
|
err.err("lexer", "failed to open <" + file + ">");
|
||||||
less, // operator <
|
} else {
|
||||||
leq, // operator <=
|
err.load(file);
|
||||||
grt, // operator >
|
}
|
||||||
geq, // operator >=
|
std::stringstream ss;
|
||||||
eof // <eof> end of token list
|
ss << in.rdbuf();
|
||||||
};
|
res = ss.str();
|
||||||
|
}
|
||||||
struct token {
|
|
||||||
span loc; // location
|
tok lexer::get_type(const std::string& str) {
|
||||||
tok type; // token type
|
return typetbl.count(str)?typetbl.at(str):tok::null;
|
||||||
string str; // content
|
}
|
||||||
token() = default;
|
|
||||||
token(const token&) = default;
|
std::string lexer::utf8_gen() {
|
||||||
};
|
std::string str="";
|
||||||
|
while(ptr<res.size() && res[ptr]<0) {
|
||||||
class lexer {
|
std::string tmp="";
|
||||||
private:
|
u32 nbytes=utf8_hdchk(res[ptr]);
|
||||||
u32 line;
|
if (!nbytes) {
|
||||||
u32 column;
|
++ptr;
|
||||||
usize ptr;
|
++column;
|
||||||
string filename;
|
continue;
|
||||||
string res;
|
}
|
||||||
error& err;
|
|
||||||
std::vector<token> toks;
|
tmp+=res[ptr++];
|
||||||
const std::unordered_map<string,tok> typetbl {
|
for(u32 i=0;i<nbytes;++i,++ptr) {
|
||||||
{"true" ,tok::tktrue },
|
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
|
||||||
{"false" ,tok::tkfalse },
|
tmp+=res[ptr];
|
||||||
{"for" ,tok::rfor },
|
}
|
||||||
{"forindex",tok::forindex},
|
}
|
||||||
{"foreach" ,tok::foreach },
|
|
||||||
{"while" ,tok::rwhile },
|
// utf8 character's total length is 1+nbytes
|
||||||
{"var" ,tok::var },
|
if (tmp.length()!=1+nbytes) {
|
||||||
{"func" ,tok::func },
|
++column;
|
||||||
{"break" ,tok::brk },
|
std::string utf_info="0x"+chrhex(tmp[0]);
|
||||||
{"continue",tok::cont },
|
for(u32 i=1;i<tmp.size();++i) {
|
||||||
{"return" ,tok::ret },
|
utf_info+=" 0x"+chrhex(tmp[i]);
|
||||||
{"if" ,tok::rif },
|
}
|
||||||
{"elsif" ,tok::elsif },
|
err.err("lexer", {line, column-1, line, column, filename}, "invalid utf-8 <"+utf_info+">");
|
||||||
{"else" ,tok::relse },
|
err.fatal("lexer", "fatal error occurred, stop");
|
||||||
{"nil" ,tok::tknil },
|
}
|
||||||
{"(" ,tok::lcurve },
|
str+=tmp;
|
||||||
{")" ,tok::rcurve },
|
column+=2; // may have some problems because not all the unicode takes 2 space
|
||||||
{"[" ,tok::lbracket},
|
}
|
||||||
{"]" ,tok::rbracket},
|
return str;
|
||||||
{"{" ,tok::lbrace },
|
}
|
||||||
{"}" ,tok::rbrace },
|
|
||||||
{";" ,tok::semi },
|
token lexer::id_gen() {
|
||||||
{"and" ,tok::opand },
|
u32 begin_line=line;
|
||||||
{"or" ,tok::opor },
|
u32 begin_column=column;
|
||||||
{"," ,tok::comma },
|
std::string str="";
|
||||||
{"." ,tok::dot },
|
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr]))) {
|
||||||
{"..." ,tok::ellipsis},
|
if (res[ptr]<0) { // utf-8
|
||||||
{"?" ,tok::quesmark},
|
str+=utf8_gen();
|
||||||
{":" ,tok::colon },
|
} else { // ascii
|
||||||
{"+" ,tok::add },
|
str+=res[ptr++];
|
||||||
{"-" ,tok::sub },
|
++column;
|
||||||
{"*" ,tok::mult },
|
}
|
||||||
{"/" ,tok::div },
|
}
|
||||||
{"~" ,tok::floater },
|
tok type=get_type(str);
|
||||||
{"&" ,tok::btand },
|
return {{begin_line, begin_column, line, column, filename}, (type!=tok::null)?type:tok::id, str};
|
||||||
{"|" ,tok::btor },
|
}
|
||||||
{"^" ,tok::btxor },
|
|
||||||
{"!" ,tok::opnot },
|
token lexer::num_gen() {
|
||||||
{"=" ,tok::eq },
|
u32 begin_line=line;
|
||||||
{"+=" ,tok::addeq },
|
u32 begin_column=column;
|
||||||
{"-=" ,tok::subeq },
|
// generate hex number
|
||||||
{"*=" ,tok::multeq },
|
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
|
||||||
{"/=" ,tok::diveq },
|
std::string str="0x";
|
||||||
{"~=" ,tok::lnkeq },
|
ptr+=2;
|
||||||
{"&=" ,tok::btandeq },
|
while(ptr<res.size() && is_hex(res[ptr])) {
|
||||||
{"|=" ,tok::btoreq },
|
str+=res[ptr++];
|
||||||
{"^=" ,tok::btxoreq },
|
}
|
||||||
{"==" ,tok::cmpeq },
|
column+=str.length();
|
||||||
{"!=" ,tok::neq },
|
if (str.length()<3) { // "0x"
|
||||||
{"<" ,tok::less },
|
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
||||||
{"<=" ,tok::leq },
|
}
|
||||||
{">" ,tok::grt },
|
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||||
{">=" ,tok::geq }
|
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
|
||||||
};
|
std::string str="0o";
|
||||||
|
ptr+=2;
|
||||||
tok get_type(const string&);
|
while(ptr<res.size() && is_oct(res[ptr])) {
|
||||||
bool skip(char);
|
str+=res[ptr++];
|
||||||
bool is_id(char);
|
}
|
||||||
bool is_hex(char);
|
bool erfmt=false;
|
||||||
bool is_oct(char);
|
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
|
||||||
bool is_dec(char);
|
erfmt=true;
|
||||||
bool is_str(char);
|
str+=res[ptr++];
|
||||||
bool is_single_opr(char);
|
}
|
||||||
bool is_calc_opr(char);
|
column+=str.length();
|
||||||
|
if (str.length()==2 || erfmt) {
|
||||||
void skip_note();
|
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
||||||
void err_char();
|
}
|
||||||
|
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||||
void open(const string&);
|
}
|
||||||
string utf8_gen();
|
// generate dec number
|
||||||
token id_gen();
|
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
|
||||||
token num_gen();
|
std::string str="";
|
||||||
token str_gen();
|
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||||
token single_opr();
|
str+=res[ptr++];
|
||||||
token dots();
|
}
|
||||||
token calc_opr();
|
if (ptr<res.size() && res[ptr]=='.') {
|
||||||
public:
|
str+=res[ptr++];
|
||||||
lexer(error& e): line(1), column(0), ptr(0), filename(""), res(""), err(e) {}
|
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||||
const error& scan(const string&);
|
str+=res[ptr++];
|
||||||
const std::vector<token>& result() const {return toks;}
|
}
|
||||||
};
|
// "xxxx." is not a correct number
|
||||||
|
if (str.back()=='.') {
|
||||||
bool lexer::skip(char c) {
|
column+=str.length();
|
||||||
return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0;
|
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
||||||
}
|
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
||||||
|
}
|
||||||
bool lexer::is_id(char c) {
|
}
|
||||||
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
|
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
|
||||||
}
|
str+=res[ptr++];
|
||||||
|
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
|
||||||
bool lexer::is_hex(char c) {
|
str+=res[ptr++];
|
||||||
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
|
}
|
||||||
}
|
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||||
|
str+=res[ptr++];
|
||||||
bool lexer::is_oct(char c) {
|
}
|
||||||
return '0'<=c&&c<='7';
|
// "xxxe(-|+)" is not a correct number
|
||||||
}
|
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
|
||||||
|
column+=str.length();
|
||||||
bool lexer::is_dec(char c) {
|
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
||||||
return '0'<=c&&c<='9';
|
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
bool lexer::is_str(char c) {
|
column+=str.length();
|
||||||
return c=='\''||c=='\"'||c=='`';
|
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lexer::is_single_opr(char c) {
|
token lexer::str_gen() {
|
||||||
return (
|
u32 begin_line=line;
|
||||||
c=='('||c==')'||c=='['||c==']'||
|
u32 begin_column=column;
|
||||||
c=='{'||c=='}'||c==','||c==';'||
|
std::string str="";
|
||||||
c==':'||c=='?'||c=='`'||c=='@'||
|
const char begin=res[ptr];
|
||||||
c=='%'||c=='$'||c=='\\'
|
++column;
|
||||||
);
|
while(++ptr<res.size() && res[ptr]!=begin) {
|
||||||
}
|
++column;
|
||||||
|
if (res[ptr]=='\n') {
|
||||||
bool lexer::is_calc_opr(char c) {
|
column=0;
|
||||||
return (
|
++line;
|
||||||
c=='='||c=='+'||c=='-'||c=='*'||
|
}
|
||||||
c=='!'||c=='/'||c=='<'||c=='>'||
|
if (res[ptr]=='\\' && ptr+1<res.size()) {
|
||||||
c=='~'||c=='|'||c=='&'||c=='^'
|
++column;
|
||||||
);
|
++ptr;
|
||||||
}
|
switch(res[ptr]) {
|
||||||
|
case '0': str+='\0'; break;
|
||||||
void lexer::skip_note() {
|
case 'a': str+='\a'; break;
|
||||||
// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
|
case 'b': str+='\b'; break;
|
||||||
while(++ptr<res.size() && res[ptr]!='\n') {}
|
case 'e': str+='\033'; break;
|
||||||
}
|
case 't': str+='\t'; break;
|
||||||
|
case 'n': str+='\n'; break;
|
||||||
void lexer::err_char() {
|
case 'v': str+='\v'; break;
|
||||||
++column;
|
case 'f': str+='\f'; break;
|
||||||
char c=res[ptr++];
|
case 'r': str+='\r'; break;
|
||||||
err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c));
|
case '?': str+='\?'; break;
|
||||||
err.fatal("lexer", "fatal error occurred, stop");
|
case '\\':str+='\\'; break;
|
||||||
}
|
case '\'':str+='\''; break;
|
||||||
|
case '\"':str+='\"'; break;
|
||||||
void lexer::open(const string& file) {
|
default: str+=res[ptr];break;
|
||||||
// check file exsits and it is a regular file
|
}
|
||||||
struct stat buffer;
|
if (res[ptr]=='\n') {
|
||||||
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
|
column=0;
|
||||||
err.err("lexer", "<"+file+"> is not a regular file");
|
++line;
|
||||||
err.chkerr();
|
}
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
// load
|
str+=res[ptr];
|
||||||
filename=file;
|
}
|
||||||
std::ifstream in(file, std::ios::binary);
|
// check if this string ends with a " or '
|
||||||
if (in.fail()) {
|
if (ptr++>=res.size()) {
|
||||||
err.err("lexer", "failed to open <"+file+">");
|
err.err("lexer", {begin_line, begin_column, line, column, filename}, "get EOF when generating string");
|
||||||
} else {
|
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
||||||
err.load(file);
|
}
|
||||||
}
|
++column;
|
||||||
std::stringstream ss;
|
if (begin=='`' && str.length()!=1) {
|
||||||
ss<<in.rdbuf();
|
err.err("lexer", {begin_line, begin_column, line, column, filename}, "\'`\' is used for string including one character");
|
||||||
res=ss.str();
|
}
|
||||||
}
|
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
||||||
|
}
|
||||||
tok lexer::get_type(const string& str) {
|
|
||||||
return typetbl.count(str)?typetbl.at(str):tok::null;
|
token lexer::single_opr() {
|
||||||
}
|
u32 begin_line=line;
|
||||||
|
u32 begin_column=column;
|
||||||
string lexer::utf8_gen() {
|
std::string str(1,res[ptr]);
|
||||||
string str="";
|
++column;
|
||||||
while(ptr<res.size() && res[ptr]<0) {
|
tok type=get_type(str);
|
||||||
string tmp="";
|
if (type==tok::null) {
|
||||||
u32 nbytes=utf8_hdchk(res[ptr]);
|
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid operator `"+str+"`");
|
||||||
if (!nbytes) {
|
}
|
||||||
++ptr;
|
++ptr;
|
||||||
++column;
|
return {{begin_line, begin_column, line, column, filename}, type, str};
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
token lexer::dots() {
|
||||||
tmp+=res[ptr++];
|
u32 begin_line=line;
|
||||||
for(u32 i=0;i<nbytes;++i,++ptr) {
|
u32 begin_column=column;
|
||||||
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
|
std::string str=".";
|
||||||
tmp+=res[ptr];
|
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
|
||||||
}
|
str+="..";
|
||||||
}
|
}
|
||||||
|
ptr+=str.length();
|
||||||
// utf8 character's total length is 1+nbytes
|
column+=str.length();
|
||||||
if (tmp.length()!=1+nbytes) {
|
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
||||||
++column;
|
}
|
||||||
string utf_info="0x"+chrhex(tmp[0]);
|
|
||||||
for(u32 i=1;i<tmp.size();++i) {
|
token lexer::calc_opr() {
|
||||||
utf_info+=" 0x"+chrhex(tmp[i]);
|
u32 begin_line=line;
|
||||||
}
|
u32 begin_column=column;
|
||||||
err.err("lexer", {line, column-1, line, column, filename}, "invalid utf-8 <"+utf_info+">");
|
// get calculation operator
|
||||||
err.fatal("lexer", "fatal error occurred, stop");
|
std::string str(1,res[ptr++]);
|
||||||
}
|
if (ptr<res.size() && res[ptr]=='=') {
|
||||||
str+=tmp;
|
str+=res[ptr++];
|
||||||
column+=2; // may have some problems because not all the unicode takes 2 space
|
}
|
||||||
}
|
column+=str.length();
|
||||||
return str;
|
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
||||||
}
|
}
|
||||||
|
|
||||||
token lexer::id_gen() {
|
const error& lexer::scan(const std::string& file) {
|
||||||
u32 begin_line=line;
|
line=1;
|
||||||
u32 begin_column=column;
|
column=0;
|
||||||
string str="";
|
ptr=0;
|
||||||
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr]))) {
|
open(file);
|
||||||
if (res[ptr]<0) { // utf-8
|
|
||||||
str+=utf8_gen();
|
while(ptr<res.size()) {
|
||||||
} else { // ascii
|
while(ptr<res.size() && skip(res[ptr])) {
|
||||||
str+=res[ptr++];
|
// these characters will be ignored, and '\n' will cause ++line
|
||||||
++column;
|
++column;
|
||||||
}
|
if (res[ptr++]=='\n') {
|
||||||
}
|
++line;
|
||||||
tok type=get_type(str);
|
column=0;
|
||||||
return {{begin_line, begin_column, line, column, filename}, (type!=tok::null)?type:tok::id, str};
|
}
|
||||||
}
|
}
|
||||||
|
if (ptr>=res.size()) {
|
||||||
token lexer::num_gen() {
|
break;
|
||||||
u32 begin_line=line;
|
}
|
||||||
u32 begin_column=column;
|
if (is_id(res[ptr])) {
|
||||||
// generate hex number
|
toks.push_back(id_gen());
|
||||||
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
|
} else if (is_dec(res[ptr])) {
|
||||||
string str="0x";
|
toks.push_back(num_gen());
|
||||||
ptr+=2;
|
} else if (is_str(res[ptr])) {
|
||||||
while(ptr<res.size() && is_hex(res[ptr])) {
|
toks.push_back(str_gen());
|
||||||
str+=res[ptr++];
|
} else if (is_single_opr(res[ptr])) {
|
||||||
}
|
toks.push_back(single_opr());
|
||||||
column+=str.length();
|
} else if (res[ptr]=='.') {
|
||||||
if (str.length()<3) { // "0x"
|
toks.push_back(dots());
|
||||||
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
} else if (is_calc_opr(res[ptr])) {
|
||||||
}
|
toks.push_back(calc_opr());
|
||||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
} else if (res[ptr]=='#') {
|
||||||
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
|
skip_note();
|
||||||
string str="0o";
|
} else {
|
||||||
ptr+=2;
|
err_char();
|
||||||
while(ptr<res.size() && is_oct(res[ptr])) {
|
}
|
||||||
str+=res[ptr++];
|
}
|
||||||
}
|
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
|
||||||
bool erfmt=false;
|
res="";
|
||||||
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
|
return err;
|
||||||
erfmt=true;
|
}
|
||||||
str+=res[ptr++];
|
|
||||||
}
|
|
||||||
column+=str.length();
|
|
||||||
if (str.length()==2 || erfmt) {
|
|
||||||
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
|
||||||
}
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
|
||||||
}
|
|
||||||
// generate dec number
|
|
||||||
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
|
|
||||||
string str="";
|
|
||||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
|
||||||
str+=res[ptr++];
|
|
||||||
}
|
|
||||||
if (ptr<res.size() && res[ptr]=='.') {
|
|
||||||
str+=res[ptr++];
|
|
||||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
|
||||||
str+=res[ptr++];
|
|
||||||
}
|
|
||||||
// "xxxx." is not a correct number
|
|
||||||
if (str.back()=='.') {
|
|
||||||
column+=str.length();
|
|
||||||
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
|
|
||||||
str+=res[ptr++];
|
|
||||||
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
|
|
||||||
str+=res[ptr++];
|
|
||||||
}
|
|
||||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
|
||||||
str+=res[ptr++];
|
|
||||||
}
|
|
||||||
// "xxxe(-|+)" is not a correct number
|
|
||||||
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
|
|
||||||
column+=str.length();
|
|
||||||
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
column+=str.length();
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
|
||||||
}
|
|
||||||
|
|
||||||
token lexer::str_gen() {
|
|
||||||
u32 begin_line=line;
|
|
||||||
u32 begin_column=column;
|
|
||||||
string str="";
|
|
||||||
const char begin=res[ptr];
|
|
||||||
++column;
|
|
||||||
while(++ptr<res.size() && res[ptr]!=begin) {
|
|
||||||
++column;
|
|
||||||
if (res[ptr]=='\n') {
|
|
||||||
column=0;
|
|
||||||
++line;
|
|
||||||
}
|
|
||||||
if (res[ptr]=='\\' && ptr+1<res.size()) {
|
|
||||||
++column;
|
|
||||||
++ptr;
|
|
||||||
switch(res[ptr]) {
|
|
||||||
case '0': str+='\0'; break;
|
|
||||||
case 'a': str+='\a'; break;
|
|
||||||
case 'b': str+='\b'; break;
|
|
||||||
case 'e': str+='\033'; break;
|
|
||||||
case 't': str+='\t'; break;
|
|
||||||
case 'n': str+='\n'; break;
|
|
||||||
case 'v': str+='\v'; break;
|
|
||||||
case 'f': str+='\f'; break;
|
|
||||||
case 'r': str+='\r'; break;
|
|
||||||
case '?': str+='\?'; break;
|
|
||||||
case '\\':str+='\\'; break;
|
|
||||||
case '\'':str+='\''; break;
|
|
||||||
case '\"':str+='\"'; break;
|
|
||||||
default: str+=res[ptr];break;
|
|
||||||
}
|
|
||||||
if (res[ptr]=='\n') {
|
|
||||||
column=0;
|
|
||||||
++line;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
str+=res[ptr];
|
|
||||||
}
|
|
||||||
// check if this string ends with a " or '
|
|
||||||
if (ptr++>=res.size()) {
|
|
||||||
err.err("lexer", {begin_line, begin_column, line, column, filename}, "get EOF when generating string");
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
|
||||||
}
|
|
||||||
++column;
|
|
||||||
if (begin=='`' && str.length()!=1) {
|
|
||||||
err.err("lexer", {begin_line, begin_column, line, column, filename}, "\'`\' is used for string including one character");
|
|
||||||
}
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
|
||||||
}
|
|
||||||
|
|
||||||
token lexer::single_opr() {
|
|
||||||
u32 begin_line=line;
|
|
||||||
u32 begin_column=column;
|
|
||||||
string str(1,res[ptr]);
|
|
||||||
++column;
|
|
||||||
tok type=get_type(str);
|
|
||||||
if (type==tok::null) {
|
|
||||||
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid operator `"+str+"`");
|
|
||||||
}
|
|
||||||
++ptr;
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, type, str};
|
|
||||||
}
|
|
||||||
|
|
||||||
token lexer::dots() {
|
|
||||||
u32 begin_line=line;
|
|
||||||
u32 begin_column=column;
|
|
||||||
string str=".";
|
|
||||||
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
|
|
||||||
str+="..";
|
|
||||||
}
|
|
||||||
ptr+=str.length();
|
|
||||||
column+=str.length();
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
|
||||||
}
|
|
||||||
|
|
||||||
token lexer::calc_opr() {
|
|
||||||
u32 begin_line=line;
|
|
||||||
u32 begin_column=column;
|
|
||||||
// get calculation operator
|
|
||||||
string str(1,res[ptr++]);
|
|
||||||
if (ptr<res.size() && res[ptr]=='=') {
|
|
||||||
str+=res[ptr++];
|
|
||||||
}
|
|
||||||
column+=str.length();
|
|
||||||
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
|
||||||
}
|
|
||||||
|
|
||||||
const error& lexer::scan(const string& file) {
|
|
||||||
line=1;
|
|
||||||
column=0;
|
|
||||||
ptr=0;
|
|
||||||
open(file);
|
|
||||||
|
|
||||||
while(ptr<res.size()) {
|
|
||||||
while(ptr<res.size() && skip(res[ptr])) {
|
|
||||||
// these characters will be ignored, and '\n' will cause ++line
|
|
||||||
++column;
|
|
||||||
if (res[ptr++]=='\n') {
|
|
||||||
++line;
|
|
||||||
column=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ptr>=res.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (is_id(res[ptr])) {
|
|
||||||
toks.push_back(id_gen());
|
|
||||||
} else if (is_dec(res[ptr])) {
|
|
||||||
toks.push_back(num_gen());
|
|
||||||
} else if (is_str(res[ptr])) {
|
|
||||||
toks.push_back(str_gen());
|
|
||||||
} else if (is_single_opr(res[ptr])) {
|
|
||||||
toks.push_back(single_opr());
|
|
||||||
} else if (res[ptr]=='.') {
|
|
||||||
toks.push_back(dots());
|
|
||||||
} else if (is_calc_opr(res[ptr])) {
|
|
||||||
toks.push_back(calc_opr());
|
|
||||||
} else if (res[ptr]=='#') {
|
|
||||||
skip_note();
|
|
||||||
} else {
|
|
||||||
err_char();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
|
|
||||||
res="";
|
|
||||||
return err;
|
|
||||||
}
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (disable:4244)
|
||||||
|
#pragma warning (disable:4267)
|
||||||
|
#pragma warning (disable:4102)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
#include "nasal_err.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define S_ISREG(m) (((m)&0xF000)==0x8000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class tok:u32 {
|
||||||
|
null=0, // null token (default token type)
|
||||||
|
num, // number literal
|
||||||
|
str, // string literal
|
||||||
|
id, // identifier
|
||||||
|
tktrue, // keyword true
|
||||||
|
tkfalse, // keyword false
|
||||||
|
rfor, // loop keyword for
|
||||||
|
forindex, // loop keyword forindex
|
||||||
|
foreach, // loop keyword foreach
|
||||||
|
rwhile, // loop keyword while
|
||||||
|
var, // keyword for definition
|
||||||
|
func, // keyword for definition of function
|
||||||
|
brk, // loop keyword break
|
||||||
|
cont, // loop keyword continue
|
||||||
|
ret, // function keyword return
|
||||||
|
rif, // condition expression keyword if
|
||||||
|
elsif, // condition expression keyword elsif
|
||||||
|
relse, // condition expression keyword else
|
||||||
|
tknil, // nil literal
|
||||||
|
lcurve, // (
|
||||||
|
rcurve, // )
|
||||||
|
lbracket, // [
|
||||||
|
rbracket, // ]
|
||||||
|
lbrace, // {
|
||||||
|
rbrace, // }
|
||||||
|
semi, // ;
|
||||||
|
opand, // operator and
|
||||||
|
opor, // operator or
|
||||||
|
comma, // ,
|
||||||
|
dot, // .
|
||||||
|
ellipsis, // ...
|
||||||
|
quesmark, // ?
|
||||||
|
colon, // :
|
||||||
|
add, // operator +
|
||||||
|
sub, // operator -
|
||||||
|
mult, // operator *
|
||||||
|
div, // operator /
|
||||||
|
floater, // operator ~ and binary operator ~
|
||||||
|
btand, // bitwise operator &
|
||||||
|
btor, // bitwise operator |
|
||||||
|
btxor, // bitwise operator ^
|
||||||
|
opnot, // operator !
|
||||||
|
eq, // operator =
|
||||||
|
addeq, // operator +=
|
||||||
|
subeq, // operator -=
|
||||||
|
multeq, // operator *=
|
||||||
|
diveq, // operator /=
|
||||||
|
lnkeq, // operator ~=
|
||||||
|
btandeq, // operator &=
|
||||||
|
btoreq, // operator |=
|
||||||
|
btxoreq, // operator ^=
|
||||||
|
cmpeq, // operator ==
|
||||||
|
neq, // operator !=
|
||||||
|
less, // operator <
|
||||||
|
leq, // operator <=
|
||||||
|
grt, // operator >
|
||||||
|
geq, // operator >=
|
||||||
|
eof // <eof> end of token list
|
||||||
|
};
|
||||||
|
|
||||||
|
struct token {
|
||||||
|
span loc; // location
|
||||||
|
tok type; // token type
|
||||||
|
std::string str; // content
|
||||||
|
token() = default;
|
||||||
|
token(const token&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class lexer {
|
||||||
|
private:
|
||||||
|
u32 line;
|
||||||
|
u32 column;
|
||||||
|
usize ptr;
|
||||||
|
std::string filename;
|
||||||
|
std::string res;
|
||||||
|
error& err;
|
||||||
|
std::vector<token> toks;
|
||||||
|
const std::unordered_map<std::string, tok> typetbl {
|
||||||
|
{"true" ,tok::tktrue },
|
||||||
|
{"false" ,tok::tkfalse },
|
||||||
|
{"for" ,tok::rfor },
|
||||||
|
{"forindex",tok::forindex},
|
||||||
|
{"foreach" ,tok::foreach },
|
||||||
|
{"while" ,tok::rwhile },
|
||||||
|
{"var" ,tok::var },
|
||||||
|
{"func" ,tok::func },
|
||||||
|
{"break" ,tok::brk },
|
||||||
|
{"continue",tok::cont },
|
||||||
|
{"return" ,tok::ret },
|
||||||
|
{"if" ,tok::rif },
|
||||||
|
{"elsif" ,tok::elsif },
|
||||||
|
{"else" ,tok::relse },
|
||||||
|
{"nil" ,tok::tknil },
|
||||||
|
{"(" ,tok::lcurve },
|
||||||
|
{")" ,tok::rcurve },
|
||||||
|
{"[" ,tok::lbracket},
|
||||||
|
{"]" ,tok::rbracket},
|
||||||
|
{"{" ,tok::lbrace },
|
||||||
|
{"}" ,tok::rbrace },
|
||||||
|
{";" ,tok::semi },
|
||||||
|
{"and" ,tok::opand },
|
||||||
|
{"or" ,tok::opor },
|
||||||
|
{"," ,tok::comma },
|
||||||
|
{"." ,tok::dot },
|
||||||
|
{"..." ,tok::ellipsis},
|
||||||
|
{"?" ,tok::quesmark},
|
||||||
|
{":" ,tok::colon },
|
||||||
|
{"+" ,tok::add },
|
||||||
|
{"-" ,tok::sub },
|
||||||
|
{"*" ,tok::mult },
|
||||||
|
{"/" ,tok::div },
|
||||||
|
{"~" ,tok::floater },
|
||||||
|
{"&" ,tok::btand },
|
||||||
|
{"|" ,tok::btor },
|
||||||
|
{"^" ,tok::btxor },
|
||||||
|
{"!" ,tok::opnot },
|
||||||
|
{"=" ,tok::eq },
|
||||||
|
{"+=" ,tok::addeq },
|
||||||
|
{"-=" ,tok::subeq },
|
||||||
|
{"*=" ,tok::multeq },
|
||||||
|
{"/=" ,tok::diveq },
|
||||||
|
{"~=" ,tok::lnkeq },
|
||||||
|
{"&=" ,tok::btandeq },
|
||||||
|
{"|=" ,tok::btoreq },
|
||||||
|
{"^=" ,tok::btxoreq },
|
||||||
|
{"==" ,tok::cmpeq },
|
||||||
|
{"!=" ,tok::neq },
|
||||||
|
{"<" ,tok::less },
|
||||||
|
{"<=" ,tok::leq },
|
||||||
|
{">" ,tok::grt },
|
||||||
|
{">=" ,tok::geq }
|
||||||
|
};
|
||||||
|
|
||||||
|
tok get_type(const std::string&);
|
||||||
|
bool skip(char);
|
||||||
|
bool is_id(char);
|
||||||
|
bool is_hex(char);
|
||||||
|
bool is_oct(char);
|
||||||
|
bool is_dec(char);
|
||||||
|
bool is_str(char);
|
||||||
|
bool is_single_opr(char);
|
||||||
|
bool is_calc_opr(char);
|
||||||
|
|
||||||
|
void skip_note();
|
||||||
|
void err_char();
|
||||||
|
|
||||||
|
void open(const std::string&);
|
||||||
|
std::string utf8_gen();
|
||||||
|
token id_gen();
|
||||||
|
token num_gen();
|
||||||
|
token str_gen();
|
||||||
|
token single_opr();
|
||||||
|
token dots();
|
||||||
|
token calc_opr();
|
||||||
|
public:
|
||||||
|
lexer(error& e):
|
||||||
|
line(1), column(0),
|
||||||
|
ptr(0), filename(""), res(""), err(e) {}
|
||||||
|
const error& scan(const std::string&);
|
||||||
|
const std::vector<token>& result() const {return toks;}
|
||||||
|
};
|
|
@ -1,255 +1,230 @@
|
||||||
#pragma once
|
#include "nasal.h"
|
||||||
|
|
||||||
#ifndef __nasver
|
bool is_windows() {
|
||||||
#define __nasver "10.1"
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
#endif
|
return true;
|
||||||
|
#else
|
||||||
#include <cstdint>
|
return false;
|
||||||
#include <iostream>
|
#endif
|
||||||
#include <fstream>
|
}
|
||||||
#include <cstring>
|
|
||||||
#include <cmath>
|
bool is_linux() {
|
||||||
#include <vector>
|
#if defined __linux__
|
||||||
|
return true;
|
||||||
bool is_windows() {
|
#else
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
bool is_macos() {
|
||||||
}
|
#if defined __APPLE__
|
||||||
|
return true;
|
||||||
bool is_linux() {
|
#else
|
||||||
#if defined __linux__
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
bool is_x86() {
|
||||||
}
|
#if defined(__i386__) || defined(_M_IX86)
|
||||||
|
return true;
|
||||||
bool is_macos() {
|
#else
|
||||||
#if defined __APPLE__
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
bool is_amd64() {
|
||||||
}
|
#if defined(__amd64__) || defined(_M_X64)
|
||||||
|
return true;
|
||||||
bool is_x86() {
|
#else
|
||||||
#if defined(__i386__) || defined(_M_IX86)
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
bool is_x86_64() {
|
||||||
}
|
return is_amd64();
|
||||||
|
}
|
||||||
bool is_amd64() {
|
|
||||||
#if defined(__amd64__) || defined(_M_X64)
|
bool is_arm() {
|
||||||
return true;
|
#if defined(__arm__) || defined(_M_ARM)
|
||||||
#else
|
return true;
|
||||||
return false;
|
#else
|
||||||
#endif
|
return false;
|
||||||
}
|
#endif
|
||||||
|
}
|
||||||
bool is_x86_64() {
|
|
||||||
return is_amd64();
|
bool is_aarch64() {
|
||||||
}
|
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
return true;
|
||||||
bool is_arm() {
|
#else
|
||||||
#if defined(__arm__) || defined(_M_ARM)
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
bool is_ia64() {
|
||||||
}
|
#if defined(__ia64__)
|
||||||
|
return true;
|
||||||
bool is_aarch64() {
|
#else
|
||||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
bool is_powerpc() {
|
||||||
}
|
#if defined(__powerpc__)
|
||||||
|
return true;
|
||||||
bool is_ia64() {
|
#else
|
||||||
#if defined(__ia64__)
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
bool is_superh() {
|
||||||
}
|
#if defined(__sh__)
|
||||||
|
return true;
|
||||||
bool is_powerpc() {
|
#else
|
||||||
#if defined(__powerpc__)
|
return false;
|
||||||
return true;
|
#endif
|
||||||
#else
|
}
|
||||||
return false;
|
|
||||||
#endif
|
f64 hex2f(const char* str) {
|
||||||
}
|
f64 ret=0;
|
||||||
|
for(; *str; ++str) {
|
||||||
bool is_superh() {
|
if ('0'<=*str && *str<='9') {
|
||||||
#if defined(__sh__)
|
ret=ret*16+(*str-'0');
|
||||||
return true;
|
} else if ('a'<=*str && *str<='f') {
|
||||||
#else
|
ret=ret*16+(*str-'a'+10);
|
||||||
return false;
|
} else if ('A'<=*str && *str<='F') {
|
||||||
#endif
|
ret=ret*16+(*str-'A'+10);
|
||||||
}
|
} else {
|
||||||
|
return nan("");
|
||||||
using i32=std::int32_t;
|
}
|
||||||
using i64=std::int64_t;
|
}
|
||||||
using u8=std::uint8_t;
|
return ret;
|
||||||
using u16=std::uint16_t;
|
}
|
||||||
using u32=std::uint32_t;
|
|
||||||
using u64=std::uint64_t;
|
f64 oct2f(const char* str) {
|
||||||
using usize=std::size_t;
|
f64 ret=0;
|
||||||
using f64=double;
|
while('0'<=*str && *str<'8') {
|
||||||
using std::string;
|
ret=ret*8+(*str++-'0');
|
||||||
|
}
|
||||||
const u32 STACK_DEPTH=1024;
|
if (*str) {
|
||||||
|
return nan("");
|
||||||
f64 hex2f(const char* str) {
|
}
|
||||||
f64 ret=0;
|
return ret;
|
||||||
for(; *str; ++str) {
|
}
|
||||||
if ('0'<=*str && *str<='9') {
|
|
||||||
ret=ret*16+(*str-'0');
|
// we have the same reason not using atof here
|
||||||
} else if ('a'<=*str && *str<='f') {
|
// just as andy's interpreter does.
|
||||||
ret=ret*16+(*str-'a'+10);
|
// it is not platform independent, and may have strange output.
|
||||||
} else if ('A'<=*str && *str<='F') {
|
// so we write a new function here to convert str to number manually.
|
||||||
ret=ret*16+(*str-'A'+10);
|
// but this also makes 0.1+0.2==0.3,
|
||||||
} else {
|
// not another result that you may get in other languages.
|
||||||
return nan("");
|
f64 dec2f(const char* str) {
|
||||||
}
|
f64 ret=0,negative=1,num_pow=0;
|
||||||
}
|
while('0'<=*str && *str<='9') {
|
||||||
return ret;
|
ret=ret*10+(*str++-'0');
|
||||||
}
|
}
|
||||||
|
if (!*str) {
|
||||||
f64 oct2f(const char* str) {
|
return ret;
|
||||||
f64 ret=0;
|
}
|
||||||
while('0'<=*str && *str<'8') {
|
if (*str=='.') {
|
||||||
ret=ret*8+(*str++-'0');
|
if (!*++str) {
|
||||||
}
|
return nan("");
|
||||||
if (*str) {
|
}
|
||||||
return nan("");
|
num_pow=0.1;
|
||||||
}
|
while('0'<=*str && *str<='9') {
|
||||||
return ret;
|
ret+=num_pow*(*str++-'0');
|
||||||
}
|
num_pow*=0.1;
|
||||||
|
}
|
||||||
// we have the same reason not using atof here
|
if (!*str) {
|
||||||
// just as andy's interpreter does.
|
return ret;
|
||||||
// it is not platform independent, and may have strange output.
|
}
|
||||||
// so we write a new function here to convert str to number manually.
|
}
|
||||||
// but this also makes 0.1+0.2==0.3,
|
if (*str!='e' && *str!='E') {
|
||||||
// not another result that you may get in other languages.
|
return nan("");
|
||||||
f64 dec2f(const char* str) {
|
}
|
||||||
f64 ret=0,negative=1,num_pow=0;
|
if (!*++str) {
|
||||||
while('0'<=*str && *str<='9') {
|
return nan("");
|
||||||
ret=ret*10+(*str++-'0');
|
}
|
||||||
}
|
if (*str=='-' || *str=='+') {
|
||||||
if (!*str) {
|
negative=(*str++=='-'? -1:1);
|
||||||
return ret;
|
}
|
||||||
}
|
if (!*str) {
|
||||||
if (*str=='.') {
|
return nan("");
|
||||||
if (!*++str) {
|
}
|
||||||
return nan("");
|
num_pow=0;
|
||||||
}
|
while('0'<=*str && *str<='9') {
|
||||||
num_pow=0.1;
|
num_pow=num_pow*10+(*str++-'0');
|
||||||
while('0'<=*str && *str<='9') {
|
}
|
||||||
ret+=num_pow*(*str++-'0');
|
if (*str) {
|
||||||
num_pow*=0.1;
|
return nan("");
|
||||||
}
|
}
|
||||||
if (!*str) {
|
return ret*std::pow(10,negative*num_pow);
|
||||||
return ret;
|
}
|
||||||
}
|
|
||||||
}
|
f64 str2num(const char* str) {
|
||||||
if (*str!='e' && *str!='E') {
|
bool negative=false;
|
||||||
return nan("");
|
f64 res=0;
|
||||||
}
|
if (*str=='-' || *str=='+') {
|
||||||
if (!*++str) {
|
negative=(*str++=='-');
|
||||||
return nan("");
|
}
|
||||||
}
|
if (!*str) {
|
||||||
if (*str=='-' || *str=='+') {
|
return nan("");
|
||||||
negative=(*str++=='-'? -1:1);
|
}
|
||||||
}
|
if (str[0]=='0' && str[1]=='x') {
|
||||||
if (!*str) {
|
res=hex2f(str+2);
|
||||||
return nan("");
|
} else if (str[0]=='0' && str[1]=='o') {
|
||||||
}
|
res=oct2f(str+2);
|
||||||
num_pow=0;
|
} else {
|
||||||
while('0'<=*str && *str<='9') {
|
res=dec2f(str);
|
||||||
num_pow=num_pow*10+(*str++-'0');
|
}
|
||||||
}
|
return negative?-res:res;
|
||||||
if (*str) {
|
}
|
||||||
return nan("");
|
|
||||||
}
|
i32 utf8_hdchk(const char head) {
|
||||||
return ret*std::pow(10,negative*num_pow);
|
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
|
||||||
}
|
const u8 c=(u8)head;
|
||||||
|
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
|
||||||
f64 str2num(const char* str) {
|
return 1;
|
||||||
bool negative=false;
|
}
|
||||||
f64 res=0;
|
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
|
||||||
if (*str=='-' || *str=='+') {
|
return 2;
|
||||||
negative=(*str++=='-');
|
}
|
||||||
}
|
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
|
||||||
if (!*str) {
|
return 3;
|
||||||
return nan("");
|
}
|
||||||
}
|
return 0;
|
||||||
if (str[0]=='0' && str[1]=='x') {
|
}
|
||||||
res=hex2f(str+2);
|
|
||||||
} else if (str[0]=='0' && str[1]=='o') {
|
std::string chrhex(const char c) {
|
||||||
res=oct2f(str+2);
|
const char hextbl[]="0123456789abcdef";
|
||||||
} else {
|
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
|
||||||
res=dec2f(str);
|
}
|
||||||
}
|
|
||||||
return negative?-res:res;
|
std::string rawstr(const std::string& str, const usize maxlen) {
|
||||||
}
|
std::string ret("");
|
||||||
|
for(auto i:str) {
|
||||||
i32 utf8_hdchk(const char head) {
|
// windows doesn't output unicode normally, so we output the hex
|
||||||
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
|
if (is_windows() && i<=0) {
|
||||||
const u8 c=(u8)head;
|
ret+="\\x"+chrhex(i);
|
||||||
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
|
continue;
|
||||||
return 1;
|
}
|
||||||
}
|
switch(i) {
|
||||||
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
|
case '\0': ret+="\\0"; break;
|
||||||
return 2;
|
case '\a': ret+="\\a"; break;
|
||||||
}
|
case '\b': ret+="\\b"; break;
|
||||||
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
|
case '\t': ret+="\\t"; break;
|
||||||
return 3;
|
case '\n': ret+="\\n"; break;
|
||||||
}
|
case '\v': ret+="\\v"; break;
|
||||||
return 0;
|
case '\f': ret+="\\f"; break;
|
||||||
}
|
case '\r': ret+="\\r"; break;
|
||||||
|
case '\033':ret+="\\e"; break;
|
||||||
string chrhex(const char c) {
|
case '\"': ret+="\\\"";break;
|
||||||
const char hextbl[]="0123456789abcdef";
|
case '\'': ret+="\\\'";break;
|
||||||
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
|
case '\\': ret+="\\\\";break;
|
||||||
}
|
default: ret+=i; break;
|
||||||
|
}
|
||||||
string rawstr(const string& str, const usize maxlen=0) {
|
}
|
||||||
string ret("");
|
if (maxlen && ret.length()>maxlen) {
|
||||||
for(auto i:str) {
|
ret=ret.substr(0,maxlen)+"...";
|
||||||
// windows doesn't output unicode normally, so we output the hex
|
}
|
||||||
if (is_windows() && i<=0) {
|
return ret;
|
||||||
ret+="\\x"+chrhex(i);
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch(i) {
|
|
||||||
case '\0': ret+="\\0"; break;
|
|
||||||
case '\a': ret+="\\a"; break;
|
|
||||||
case '\b': ret+="\\b"; break;
|
|
||||||
case '\t': ret+="\\t"; break;
|
|
||||||
case '\n': ret+="\\n"; break;
|
|
||||||
case '\v': ret+="\\v"; break;
|
|
||||||
case '\f': ret+="\\f"; break;
|
|
||||||
case '\r': ret+="\\r"; break;
|
|
||||||
case '\033':ret+="\\e"; break;
|
|
||||||
case '\"': ret+="\\\"";break;
|
|
||||||
case '\'': ret+="\\\'";break;
|
|
||||||
case '\\': ret+="\\\\";break;
|
|
||||||
default: ret+=i; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (maxlen && ret.length()>maxlen) {
|
|
||||||
ret=ret.substr(0,maxlen)+"...";
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "nasal_gc.h" // declarations of var and nasal_gc
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
#include "nasal_opcode.h"
|
||||||
|
|
||||||
|
const char* opname[]={
|
||||||
|
"exit ","intg ","intl ","loadg ",
|
||||||
|
"loadl ","loadu ","pnum ","pnil ",
|
||||||
|
"pstr ","newv ","newh ","newf ",
|
||||||
|
"happ ","para ","def ","dyn ",
|
||||||
|
"lnot ","usub ","bnot ","btor ",
|
||||||
|
"btxor ","btand ","add ","sub ",
|
||||||
|
"mult ","div ","lnk ","addc ",
|
||||||
|
"subc ","multc ","divc ","lnkc ",
|
||||||
|
"addeq ","subeq ","muleq ","diveq ",
|
||||||
|
"lnkeq ","bandeq","boreq ","bxoreq",
|
||||||
|
"addeqc","subeqc","muleqc","diveqc",
|
||||||
|
"lnkeqc","addecp","subecp","mulecp",
|
||||||
|
"divecp","lnkecp","meq ","eq ",
|
||||||
|
"neq ","less ","leq ","grt ",
|
||||||
|
"geq ","lessc ","leqc ","grtc ",
|
||||||
|
"geqc ","pop ","jmp ","jt ",
|
||||||
|
"jf ","cnt ","findx ","feach ",
|
||||||
|
"callg ","calll ","upval ","callv ",
|
||||||
|
"callvi","callh ","callfv","callfh",
|
||||||
|
"callb ","slcbeg","slcend","slc ",
|
||||||
|
"slc2 ","mcallg","mcalll","mupval",
|
||||||
|
"mcallv","mcallh","ret "
|
||||||
|
};
|
||||||
|
|
||||||
|
void codestream::set(
|
||||||
|
const f64* numbuff,
|
||||||
|
const std::string* strbuff,
|
||||||
|
const std::string* filelist
|
||||||
|
) {
|
||||||
|
nums = numbuff;
|
||||||
|
strs = strbuff;
|
||||||
|
files = filelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void codestream::dump(std::ostream& out) const {
|
||||||
|
using std::setw;
|
||||||
|
using std::setfill;
|
||||||
|
using std::hex;
|
||||||
|
using std::dec;
|
||||||
|
auto op=code.op;
|
||||||
|
auto num=code.num;
|
||||||
|
out<<hex<<"0x"
|
||||||
|
<<setw(6)<<setfill('0')<<index<<" "
|
||||||
|
<<setw(2)<<setfill('0')<<(u32)op<<" "
|
||||||
|
<<setw(2)<<setfill('0')<<((num>>16)&0xff)<<" "
|
||||||
|
<<setw(2)<<setfill('0')<<((num>>8)&0xff)<<" "
|
||||||
|
<<setw(2)<<setfill('0')<<(num&0xff)<<" "
|
||||||
|
<<opname[op]<<" "<<dec;
|
||||||
|
switch(op) {
|
||||||
|
case op_addeq: case op_subeq:
|
||||||
|
case op_muleq: case op_diveq:
|
||||||
|
case op_lnkeq: case op_meq:
|
||||||
|
case op_btandeq: case op_btoreq:
|
||||||
|
case op_btxoreq:
|
||||||
|
out<<hex<<"0x"<<num<<dec<<" sp-"<<num;break;
|
||||||
|
case op_addeqc: case op_subeqc:
|
||||||
|
case op_muleqc:case op_diveqc:
|
||||||
|
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
|
||||||
|
case op_lnkeqc:
|
||||||
|
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
|
||||||
|
case op_addecp: case op_subecp:
|
||||||
|
case op_mulecp: case op_divecp:
|
||||||
|
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<") sp-1";break;
|
||||||
|
case op_lnkecp:
|
||||||
|
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<") sp-1";break;
|
||||||
|
case op_addc: case op_subc:
|
||||||
|
case op_mulc: case op_divc:
|
||||||
|
case op_lessc: case op_leqc:
|
||||||
|
case op_grtc: case op_geqc:
|
||||||
|
case op_pnum:
|
||||||
|
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
|
||||||
|
case op_callvi: case op_newv:
|
||||||
|
case op_callfv: case op_intg:
|
||||||
|
case op_intl: case op_findex:
|
||||||
|
case op_feach: case op_newf:
|
||||||
|
case op_jmp: case op_jt:
|
||||||
|
case op_jf: case op_callg:
|
||||||
|
case op_mcallg: case op_loadg:
|
||||||
|
case op_calll: case op_mcalll:
|
||||||
|
case op_loadl:
|
||||||
|
out<<hex<<"0x"<<num<<dec;break;
|
||||||
|
case op_callb:
|
||||||
|
out<<hex<<"0x"<<num<<" <"<<builtin[num].name
|
||||||
|
<<"@0x"<<(u64)builtin[num].func<<dec<<">";break;
|
||||||
|
case op_upval: case op_mupval:
|
||||||
|
case op_loadu:
|
||||||
|
out<<hex<<"0x"<<((num>>16)&0xffff)
|
||||||
|
<<"[0x"<<(num&0xffff)<<"]"<<dec;break;
|
||||||
|
case op_happ: case op_pstr:
|
||||||
|
case op_lnkc: case op_callh:
|
||||||
|
case op_mcallh: case op_para:
|
||||||
|
case op_deft: case op_dyn:
|
||||||
|
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
|
||||||
|
default:
|
||||||
|
if (files) {
|
||||||
|
out<<hex<<"0x"<<num<<dec;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (files) {
|
||||||
|
out<<"("<<files[code.fidx]<<":"<<code.line<<")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, const codestream& ins) {
|
||||||
|
ins.dump(out);
|
||||||
|
return out;
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
#include "nasal_builtin.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
enum op_code_type:u8 {
|
||||||
|
op_exit, // stop the virtual machine
|
||||||
|
op_intg, // global scope size, set stack top
|
||||||
|
op_intl, // local scope size
|
||||||
|
op_loadg, // load global value
|
||||||
|
op_loadl, // load local value
|
||||||
|
op_loadu, // load upvalue
|
||||||
|
op_pnum, // push constant number to the stack
|
||||||
|
op_pnil, // push constant nil to the stack
|
||||||
|
op_pstr, // push constant std::string to the stack
|
||||||
|
op_newv, // push new vector with initial values from stack
|
||||||
|
op_newh, // push new hash to the stack
|
||||||
|
op_newf, // push new function to the stack
|
||||||
|
op_happ, // hash append
|
||||||
|
op_para, // normal parameter
|
||||||
|
op_deft, // default parameter
|
||||||
|
op_dyn, // dynamic parameter
|
||||||
|
op_lnot, // ! logical negation
|
||||||
|
op_usub, // - negation
|
||||||
|
op_bnot, // ~ bitwise not static_cast<i32>
|
||||||
|
op_btor, // | bitwise or
|
||||||
|
op_btxor, // ^ bitwise xor
|
||||||
|
op_btand, // & bitwise and
|
||||||
|
op_add, // +
|
||||||
|
op_sub, // -
|
||||||
|
op_mul, // *
|
||||||
|
op_div, // /
|
||||||
|
op_lnk, // ~
|
||||||
|
op_addc, // + const
|
||||||
|
op_subc, // - const
|
||||||
|
op_mulc, // * const
|
||||||
|
op_divc, // / const
|
||||||
|
op_lnkc, // ~ const
|
||||||
|
op_addeq, // += maybe pop stack top
|
||||||
|
op_subeq, // -= maybe pop stack top
|
||||||
|
op_muleq, // *= maybe pop stack top
|
||||||
|
op_diveq, // /= maybe pop stack top
|
||||||
|
op_lnkeq, // ~= maybe pop stack top
|
||||||
|
op_btandeq,// &= maybe pop stack top
|
||||||
|
op_btoreq, // |= maybe pop stack top
|
||||||
|
op_btxoreq,// ^= maybe pop stack top
|
||||||
|
op_addeqc, // += const don't pop stack top
|
||||||
|
op_subeqc, // -= const don't pop stack top
|
||||||
|
op_muleqc, // *= const don't pop stack top
|
||||||
|
op_diveqc, // /= const don't pop stack top
|
||||||
|
op_lnkeqc, // ~= const don't pop stack top
|
||||||
|
op_addecp, // += const and pop stack top
|
||||||
|
op_subecp, // -= const and pop stack top
|
||||||
|
op_mulecp, // *= const and pop stack top
|
||||||
|
op_divecp, // /= const and pop stack top
|
||||||
|
op_lnkecp, // ~= concat const std::string and pop stack top
|
||||||
|
op_meq, // = maybe pop stack top
|
||||||
|
op_eq, // ==
|
||||||
|
op_neq, // !=
|
||||||
|
op_less, // <
|
||||||
|
op_leq, // <=
|
||||||
|
op_grt, // >
|
||||||
|
op_geq, // >=
|
||||||
|
op_lessc, // < const
|
||||||
|
op_leqc, // <= const
|
||||||
|
op_grtc, // > const
|
||||||
|
op_geqc, // >= const
|
||||||
|
op_pop, // pop a value out of stack top
|
||||||
|
op_jmp, // jump absolute address with no condition
|
||||||
|
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
|
||||||
|
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
|
||||||
|
op_cnt, // add counter for forindex/foreach
|
||||||
|
op_findex, // index counter on the top of forindex_stack plus 1
|
||||||
|
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
|
||||||
|
op_callg, // get value in global scope
|
||||||
|
op_calll, // get value in local scope
|
||||||
|
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
|
||||||
|
op_callv, // call vec[index]
|
||||||
|
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
|
||||||
|
op_callh, // call hash.label
|
||||||
|
op_callfv, // call function(vector as parameters)
|
||||||
|
op_callfh, // call function(hash as parameters)
|
||||||
|
op_callb, // call builtin-function
|
||||||
|
op_slcbeg, // begin of slice like: vec[1,2,3:6,0,-1]
|
||||||
|
op_slcend, // end of slice
|
||||||
|
op_slc, // slice like vec[1]
|
||||||
|
op_slc2, // slice like vec[nil:10]
|
||||||
|
op_mcallg, // get memory space of value in global scope
|
||||||
|
op_mcalll, // get memory space of value in local scope
|
||||||
|
op_mupval, // get memory space of value in closure
|
||||||
|
op_mcallv, // get memory space of vec[index]
|
||||||
|
op_mcallh, // get memory space of hash.label
|
||||||
|
op_ret // return
|
||||||
|
};
|
||||||
|
|
||||||
|
struct opcode {
|
||||||
|
u8 op; // opcode
|
||||||
|
u16 fidx; // source code file index
|
||||||
|
u32 num; // immediate num
|
||||||
|
u32 line; // location line of source code
|
||||||
|
opcode() = default;
|
||||||
|
opcode(const opcode&) = default;
|
||||||
|
opcode& operator=(const opcode&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class codestream {
|
||||||
|
private:
|
||||||
|
opcode code;
|
||||||
|
const u32 index;
|
||||||
|
inline static const f64* nums = nullptr;
|
||||||
|
inline static const std::string* strs = nullptr;
|
||||||
|
inline static const std::string* files = nullptr;
|
||||||
|
public:
|
||||||
|
codestream(const opcode& c, const u32 i): code(c), index(i) {}
|
||||||
|
static void set(const f64*, const std::string*, const std::string* filelist = nullptr);
|
||||||
|
void dump(std::ostream&) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream&, const codestream&);
|
||||||
|
|
||||||
|
extern const char* opname[];
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,161 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
#include "nasal_lexer.h"
|
||||||
|
#include "nasal_err.h"
|
||||||
|
|
||||||
|
class parse {
|
||||||
|
|
||||||
|
#define thisspan (toks[ptr].loc)
|
||||||
|
#define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc)
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32 ptr;
|
||||||
|
u32 in_func; // count function block
|
||||||
|
u32 in_loop; // count loop block
|
||||||
|
const token* toks;
|
||||||
|
code_block* root;
|
||||||
|
error& err;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unordered_map<tok, std::string> tokname {
|
||||||
|
{tok::rfor ,"for" },
|
||||||
|
{tok::forindex,"forindex"},
|
||||||
|
{tok::foreach ,"foreach" },
|
||||||
|
{tok::rwhile ,"while" },
|
||||||
|
{tok::var ,"var" },
|
||||||
|
{tok::func ,"func" },
|
||||||
|
{tok::brk ,"break" },
|
||||||
|
{tok::cont ,"continue"},
|
||||||
|
{tok::ret ,"return" },
|
||||||
|
{tok::rif ,"if" },
|
||||||
|
{tok::elsif ,"elsif" },
|
||||||
|
{tok::relse ,"else" },
|
||||||
|
{tok::tknil ,"nil" },
|
||||||
|
{tok::lcurve ,"(" },
|
||||||
|
{tok::rcurve ,")" },
|
||||||
|
{tok::lbracket,"[" },
|
||||||
|
{tok::rbracket,"]" },
|
||||||
|
{tok::lbrace ,"{" },
|
||||||
|
{tok::rbrace ,"}" },
|
||||||
|
{tok::semi ,";" },
|
||||||
|
{tok::opand ,"and" },
|
||||||
|
{tok::opor ,"or" },
|
||||||
|
{tok::comma ,"," },
|
||||||
|
{tok::dot ,"." },
|
||||||
|
{tok::ellipsis,"..." },
|
||||||
|
{tok::quesmark,"?" },
|
||||||
|
{tok::colon ,":" },
|
||||||
|
{tok::add ,"+" },
|
||||||
|
{tok::sub ,"-" },
|
||||||
|
{tok::mult ,"*" },
|
||||||
|
{tok::div ,"/" },
|
||||||
|
{tok::floater ,"~" },
|
||||||
|
{tok::btand ,"&" },
|
||||||
|
{tok::btor ,"|" },
|
||||||
|
{tok::btxor ,"^" },
|
||||||
|
{tok::opnot ,"!" },
|
||||||
|
{tok::eq ,"=" },
|
||||||
|
{tok::addeq ,"+=" },
|
||||||
|
{tok::subeq ,"-=" },
|
||||||
|
{tok::multeq ,"*=" },
|
||||||
|
{tok::diveq ,"/=" },
|
||||||
|
{tok::lnkeq ,"~=" },
|
||||||
|
{tok::btandeq ,"&=" },
|
||||||
|
{tok::btoreq ,"|=" },
|
||||||
|
{tok::btxoreq ,"^=" },
|
||||||
|
{tok::cmpeq ,"==" },
|
||||||
|
{tok::neq ,"!=" },
|
||||||
|
{tok::less ,"<" },
|
||||||
|
{tok::leq ,"<=" },
|
||||||
|
{tok::grt ,">" },
|
||||||
|
{tok::geq ,">=" }
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void die(const span&,std::string);
|
||||||
|
void next() {++ptr;}
|
||||||
|
void match(tok type, const char* info=nullptr);
|
||||||
|
bool lookahead(tok);
|
||||||
|
bool is_call(tok);
|
||||||
|
bool check_comma(const tok*);
|
||||||
|
bool check_tuple();
|
||||||
|
bool check_func_end(expr*);
|
||||||
|
bool check_special_call();
|
||||||
|
bool need_semi_check(expr*);
|
||||||
|
void update_location(expr*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
null_expr* null();
|
||||||
|
nil_expr* nil();
|
||||||
|
number_literal* num();
|
||||||
|
string_literal* str();
|
||||||
|
identifier* id();
|
||||||
|
bool_literal* bools();
|
||||||
|
vector_expr* vec();
|
||||||
|
hash_expr* hash();
|
||||||
|
hash_pair* pair();
|
||||||
|
function* func();
|
||||||
|
void params(function*);
|
||||||
|
expr* lcurve_expr();
|
||||||
|
expr* expression();
|
||||||
|
code_block* expression_block();
|
||||||
|
expr* calc();
|
||||||
|
expr* bitwise_or();
|
||||||
|
expr* bitwise_xor();
|
||||||
|
expr* bitwise_and();
|
||||||
|
expr* or_expr();
|
||||||
|
expr* and_expr();
|
||||||
|
expr* cmp_expr();
|
||||||
|
expr* additive_expr();
|
||||||
|
expr* multive_expr();
|
||||||
|
unary_operator* unary();
|
||||||
|
expr* scalar();
|
||||||
|
expr* call_scalar();
|
||||||
|
call_hash* callh();
|
||||||
|
call_vector* callv();
|
||||||
|
call_function* callf();
|
||||||
|
slice_vector* subvec();
|
||||||
|
expr* definition();
|
||||||
|
multi_identifier* incurve_def();
|
||||||
|
multi_identifier* outcurve_def();
|
||||||
|
multi_identifier* multi_id();
|
||||||
|
tuple_expr* multi_scalar();
|
||||||
|
multi_assign* multi_assignment();
|
||||||
|
expr* loop();
|
||||||
|
while_expr* while_loop();
|
||||||
|
for_expr* for_loop();
|
||||||
|
forei_expr* forei_loop();
|
||||||
|
iter_expr* iter_gen();
|
||||||
|
condition_expr* cond();
|
||||||
|
continue_expr* continue_expression();
|
||||||
|
break_expr* break_expression();
|
||||||
|
return_expr* return_expression();
|
||||||
|
|
||||||
|
public:
|
||||||
|
code_block* tree() {return root;}
|
||||||
|
|
||||||
|
// swap root pointer with another pointer(maybe nullptr)
|
||||||
|
code_block* swap(code_block* another) {
|
||||||
|
auto res = root;
|
||||||
|
root = another;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
parse(error& e):
|
||||||
|
ptr(0), in_func(0), in_loop(0),
|
||||||
|
toks(nullptr),
|
||||||
|
root(nullptr),
|
||||||
|
err(e) {}
|
||||||
|
~parse() {
|
||||||
|
if (root) {
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const error& compile(const lexer&);
|
||||||
|
static void easter_egg();
|
||||||
|
};
|
|
@ -0,0 +1,412 @@
|
||||||
|
#include "nasal_vm.h"
|
||||||
|
|
||||||
|
void vm::init(
|
||||||
|
const std::vector<std::string>& strs,
|
||||||
|
const std::vector<f64>& nums,
|
||||||
|
const std::vector<opcode>& code,
|
||||||
|
const std::vector<std::string>& filenames,
|
||||||
|
const std::vector<std::string>& argv
|
||||||
|
) {
|
||||||
|
cnum=nums.data();
|
||||||
|
cstr=strs.data();
|
||||||
|
bytecode=code.data();
|
||||||
|
files=filenames.data();
|
||||||
|
|
||||||
|
/* set canary and program counter */
|
||||||
|
ctx.pc=0;
|
||||||
|
ctx.localr=ctx.memr=nullptr;
|
||||||
|
ctx.funcr=ctx.upvalr=nil;
|
||||||
|
ctx.canary=stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1]
|
||||||
|
ctx.top=stack;
|
||||||
|
ctx.stack=stack;
|
||||||
|
|
||||||
|
/* clear main stack */
|
||||||
|
for(u32 i=0;i<STACK_DEPTH;++i) {
|
||||||
|
stack[i]=nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init gc */
|
||||||
|
ngc.init(strs,argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::valinfo(var& val) {
|
||||||
|
const nas_val* p=val.val.gcobj;
|
||||||
|
switch(val.type) {
|
||||||
|
case vm_none: std::clog<<"| null |";break;
|
||||||
|
case vm_ret: std::clog<<"| pc | 0x"<<std::hex
|
||||||
|
<<val.ret()<<std::dec;break;
|
||||||
|
case vm_addr: std::clog<<"| addr | 0x"<<std::hex
|
||||||
|
<<(u64)val.addr()<<std::dec;break;
|
||||||
|
case vm_cnt: std::clog<<"| cnt | "<<val.cnt();break;
|
||||||
|
case vm_nil: std::clog<<"| nil |";break;
|
||||||
|
case vm_num: std::clog<<"| num | "<<val.num();break;
|
||||||
|
case vm_str: std::clog<<"| str | <0x"<<std::hex<<(u64)p
|
||||||
|
<<"> "<<rawstr(val.str(),16)<<std::dec;break;
|
||||||
|
case vm_func: std::clog<<"| func | <0x"<<std::hex<<(u64)p
|
||||||
|
<<"> entry:0x"<<val.func().entry
|
||||||
|
<<std::dec;break;
|
||||||
|
case vm_upval:std::clog<<"| upval| <0x"<<std::hex<<(u64)p
|
||||||
|
<<std::dec<<"> ["<<val.upval().size
|
||||||
|
<<" val]";break;
|
||||||
|
case vm_vec: std::clog<<"| vec | <0x"<<std::hex<<(u64)p
|
||||||
|
<<std::dec<<"> ["<<val.vec().size()
|
||||||
|
<<" val]";break;
|
||||||
|
case vm_hash: std::clog<<"| hash | <0x"<<std::hex<<(u64)p
|
||||||
|
<<std::dec<<"> {"<<val.hash().size()
|
||||||
|
<<" val}";break;
|
||||||
|
case vm_obj: std::clog<<"| obj | <0x"<<std::hex<<(u64)p
|
||||||
|
<<"> obj:0x"<<(u64)val.obj().ptr
|
||||||
|
<<std::dec;break;
|
||||||
|
case vm_co: std::clog<<"| co | <0x"<<std::hex<<(u64)p
|
||||||
|
<<std::dec<<"> coroutine";break;
|
||||||
|
default: std::clog<<"| err | <0x"<<std::hex<<(u64)p
|
||||||
|
<<std::dec<<"> unknown object";break;
|
||||||
|
}
|
||||||
|
std::clog<<"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::traceback() {
|
||||||
|
/* bytecode[0].num is the global size */
|
||||||
|
var* bottom=ngc.rctx->stack==stack?stack+bytecode[0].num:ngc.rctx->stack;
|
||||||
|
var* ctx_top=ngc.rctx->stack==stack?ctx.top:ngc.rctx->top;
|
||||||
|
std::stack<u32> ret;
|
||||||
|
for(var* i=bottom;i<=ctx_top;++i) {
|
||||||
|
if (i->type==vm_ret && i->ret()!=0) {
|
||||||
|
ret.push(i->ret());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.push(ctx.pc); // store the position program crashed
|
||||||
|
std::clog<<"trace back ("<<(ngc.rctx->stack==stack?"main":"coroutine")<<")\n";
|
||||||
|
codestream::set(cnum, cstr, files);
|
||||||
|
for(u32 p=0,same=0,prev=0xffffffff;!ret.empty();prev=p,ret.pop()) {
|
||||||
|
if ((p=ret.top())==prev) {
|
||||||
|
++same;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (same) {
|
||||||
|
std::clog
|
||||||
|
<<" 0x"<<std::hex<<std::setw(6)<<std::setfill('0')
|
||||||
|
<<prev<<std::dec<<" "<<same<<" same call(s)\n";
|
||||||
|
}
|
||||||
|
same=0;
|
||||||
|
std::clog<<" "<<codestream(bytecode[p],p)<<"\n";
|
||||||
|
}
|
||||||
|
// the first called place has no same calls
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::stackinfo(const u32 limit=10) {
|
||||||
|
/* bytecode[0].num is the global size */
|
||||||
|
const u32 gsize=ngc.rctx->stack==stack?bytecode[0].num:0;
|
||||||
|
var* t=ctx.top;
|
||||||
|
var* bottom=ngc.rctx->stack+gsize;
|
||||||
|
std::clog<<"stack (0x"<<std::hex<<(u64)bottom<<std::dec
|
||||||
|
<<" <+"<<gsize<<">, limit "<<limit<<", total "
|
||||||
|
<<(t<bottom? 0:(i64)(t-bottom+1))<<")\n";
|
||||||
|
for(u32 i=0;i<limit && t>=bottom;++i,--t) {
|
||||||
|
std::clog<<" 0x"<<std::hex
|
||||||
|
<<std::setw(6)<<std::setfill('0')
|
||||||
|
<<(u64)(t-ngc.rctx->stack)<<std::dec
|
||||||
|
<<" ";
|
||||||
|
valinfo(t[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::reginfo() {
|
||||||
|
std::clog<<"registers ("<<(ngc.cort?"coroutine":"main")<<")\n"<<std::hex
|
||||||
|
<<" [pc ] | pc | 0x"<<ctx.pc<<"\n"
|
||||||
|
<<" [global] | addr | 0x"<<(u64)stack<<"\n"
|
||||||
|
<<" [local ] | addr | 0x"<<(u64)ctx.localr<<"\n"
|
||||||
|
<<" [memr ] | addr | 0x"<<(u64)ctx.memr<<"\n"
|
||||||
|
<<" [canary] | addr | 0x"<<(u64)ctx.canary<<"\n"
|
||||||
|
<<" [top ] | addr | 0x"<<(u64)ctx.top<<"\n"
|
||||||
|
<<std::dec;
|
||||||
|
std::clog<<" [funcr ] ";valinfo(ctx.funcr);
|
||||||
|
std::clog<<" [upval ] ";valinfo(ctx.upvalr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::gstate() {
|
||||||
|
if (!bytecode[0].num || stack[0].type==vm_none) { // bytecode[0].op is op_intg
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::clog<<"global (0x"<<std::hex<<(u64)stack<<" <+0>)\n"<<std::dec;
|
||||||
|
for(u32 i=0;i<bytecode[0].num;++i) {
|
||||||
|
std::clog<<" 0x"<<std::hex<<std::setw(6)
|
||||||
|
<<std::setfill('0')<<i<<std::dec
|
||||||
|
<<" ";
|
||||||
|
valinfo(stack[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::lstate() {
|
||||||
|
if (!ctx.localr || !ctx.funcr.func().lsize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const u32 lsize=ctx.funcr.func().lsize;
|
||||||
|
std::clog<<"local (0x"<<std::hex<<(u64)ctx.localr
|
||||||
|
<<" <+"<<(u64)(ctx.localr-ngc.rctx->stack)<<">)\n"<<std::dec;
|
||||||
|
for(u32 i=0;i<lsize;++i) {
|
||||||
|
std::clog<<" 0x"<<std::hex<<std::setw(6)
|
||||||
|
<<std::setfill('0')<<i<<std::dec
|
||||||
|
<<" ";
|
||||||
|
valinfo(ctx.localr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::ustate() {
|
||||||
|
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::clog<<"upvalue\n";
|
||||||
|
auto& upval=ctx.funcr.func().upval;
|
||||||
|
for(u32 i=0;i<upval.size();++i) {
|
||||||
|
std::clog<<" -> upval["<<i<<"]:\n";
|
||||||
|
auto& uv=upval[i].upval();
|
||||||
|
for(u32 j=0;j<uv.size;++j) {
|
||||||
|
std::clog<<" 0x"<<std::hex<<std::setw(6)
|
||||||
|
<<std::setfill('0')<<j<<std::dec
|
||||||
|
<<" ";
|
||||||
|
valinfo(uv[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::detail() {
|
||||||
|
reginfo();
|
||||||
|
gstate();
|
||||||
|
lstate();
|
||||||
|
ustate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::die(const std::string& str) {
|
||||||
|
std::cerr<<"[vm] error: "<<str<<"\n";
|
||||||
|
traceback();
|
||||||
|
stackinfo();
|
||||||
|
|
||||||
|
// show verbose crash info
|
||||||
|
if (verbose) {
|
||||||
|
detail();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngc.rctx->stack==stack) { // in main context, exit directly
|
||||||
|
std::exit(1);
|
||||||
|
} else {
|
||||||
|
// in coroutine, shut down the coroutine and return to main context
|
||||||
|
ctx.pc=0; // mark coroutine 'dead'
|
||||||
|
ngc.ctxreserve();
|
||||||
|
ctx.top[0]=nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm::run(
|
||||||
|
const codegen& gen,
|
||||||
|
const linker& linker,
|
||||||
|
const std::vector<std::string>& argv,
|
||||||
|
const bool detail
|
||||||
|
) {
|
||||||
|
verbose=detail;
|
||||||
|
init(gen.strs(), gen.nums(), gen.codes(), linker.filelist(), argv);
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
const void* oprs[]={
|
||||||
|
&&vmexit, &&intg, &&intl, &&loadg,
|
||||||
|
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||||
|
&&pstr, &&newv, &&newh, &&newf,
|
||||||
|
&&happ, &¶, &&deft, &&dyn,
|
||||||
|
&&lnot, &&usub, &&bnot, &&btor,
|
||||||
|
&&btxor, &&btand, &&add, &&sub,
|
||||||
|
&&mul, &&div, &&lnk, &&addc,
|
||||||
|
&&subc, &&mulc, &&divc, &&lnkc,
|
||||||
|
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||||
|
&&lnkeq, &&bandeq, &&boreq, &&bxoreq,
|
||||||
|
&&addeqc, &&subeqc, &&muleqc, &&diveqc,
|
||||||
|
&&lnkeqc, &&addecp, &&subecp, &&mulecp,
|
||||||
|
&&divecp, &&lnkecp, &&meq, &&eq,
|
||||||
|
&&neq, &&less, &&leq, &&grt,
|
||||||
|
&&geq, &&lessc, &&leqc, &&grtc,
|
||||||
|
&&geqc, &&pop, &&jmp, &&jt,
|
||||||
|
&&jf, &&cnt, &&findex, &&feach,
|
||||||
|
&&callg, &&calll, &&upval, &&callv,
|
||||||
|
&&callvi, &&callh, &&callfv, &&callfh,
|
||||||
|
&&callb, &&slcbeg, &&slcend, &&slc,
|
||||||
|
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||||
|
&&mcallv, &&mcallh, &&ret
|
||||||
|
};
|
||||||
|
std::vector<const void*> code;
|
||||||
|
for(auto& i:gen.codes()) {
|
||||||
|
code.push_back(oprs[i.op]);
|
||||||
|
imm.push_back(i.num);
|
||||||
|
}
|
||||||
|
// goto the first operand
|
||||||
|
goto *code[ctx.pc];
|
||||||
|
#else
|
||||||
|
typedef void (vm::*nafunc)();
|
||||||
|
const nafunc oprs[]={
|
||||||
|
nullptr, &vm::o_intg,
|
||||||
|
&vm::o_intl, &vm::o_loadg,
|
||||||
|
&vm::o_loadl, &vm::o_loadu,
|
||||||
|
&vm::o_pnum, &vm::o_pnil,
|
||||||
|
&vm::o_pstr, &vm::o_newv,
|
||||||
|
&vm::o_newh, &vm::o_newf,
|
||||||
|
&vm::o_happ, &vm::o_para,
|
||||||
|
&vm::o_deft, &vm::o_dyn,
|
||||||
|
&vm::o_lnot, &vm::o_usub,
|
||||||
|
&vm::o_bnot, &vm::o_btor,
|
||||||
|
&vm::o_btxor, &vm::o_btand,
|
||||||
|
&vm::o_add, &vm::o_sub,
|
||||||
|
&vm::o_mul, &vm::o_div,
|
||||||
|
&vm::o_lnk, &vm::o_addc,
|
||||||
|
&vm::o_subc, &vm::o_mulc,
|
||||||
|
&vm::o_divc, &vm::o_lnkc,
|
||||||
|
&vm::o_addeq, &vm::o_subeq,
|
||||||
|
&vm::o_muleq, &vm::o_diveq,
|
||||||
|
&vm::o_lnkeq, &vm::o_bandeq,
|
||||||
|
&vm::o_boreq, &vm::o_bxoreq,
|
||||||
|
&vm::o_addeqc, &vm::o_subeqc,
|
||||||
|
&vm::o_muleqc, &vm::o_diveqc,
|
||||||
|
&vm::o_lnkeqc, &vm::o_addecp,
|
||||||
|
&vm::o_subecp, &vm::o_mulecp,
|
||||||
|
&vm::o_divecp, &vm::o_lnkecp,
|
||||||
|
&vm::o_meq, &vm::o_eq,
|
||||||
|
&vm::o_neq, &vm::o_less,
|
||||||
|
&vm::o_leq, &vm::o_grt,
|
||||||
|
&vm::o_geq, &vm::o_lessc,
|
||||||
|
&vm::o_leqc, &vm::o_grtc,
|
||||||
|
&vm::o_geqc, &vm::o_pop,
|
||||||
|
&vm::o_jmp, &vm::o_jt,
|
||||||
|
&vm::o_jf, &vm::o_cnt,
|
||||||
|
&vm::o_findex, &vm::o_feach,
|
||||||
|
&vm::o_callg, &vm::o_calll,
|
||||||
|
&vm::o_upval, &vm::o_callv,
|
||||||
|
&vm::o_callvi, &vm::o_callh,
|
||||||
|
&vm::o_callfv, &vm::o_callfh,
|
||||||
|
&vm::o_callb, &vm::o_slcbeg,
|
||||||
|
&vm::o_slcend, &vm::o_slc,
|
||||||
|
&vm::o_slc2, &vm::o_mcallg,
|
||||||
|
&vm::o_mcalll, &vm::o_mupval,
|
||||||
|
&vm::o_mcallv, &vm::o_mcallh,
|
||||||
|
&vm::o_ret
|
||||||
|
};
|
||||||
|
std::vector<nafunc> code;
|
||||||
|
for(auto& i:gen.codes()) {
|
||||||
|
code.push_back(oprs[i.op]);
|
||||||
|
imm.push_back(i.num);
|
||||||
|
}
|
||||||
|
while(code[ctx.pc]) {
|
||||||
|
(this->*code[ctx.pc])();
|
||||||
|
if (ctx.top>=ctx.canary) {
|
||||||
|
die("stack overflow");
|
||||||
|
}
|
||||||
|
++ctx.pc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vmexit:
|
||||||
|
if (detail) {
|
||||||
|
ngc.info();
|
||||||
|
}
|
||||||
|
ngc.clear();
|
||||||
|
imm.clear();
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
// may cause stackoverflow
|
||||||
|
#define exec_check(op) {\
|
||||||
|
op();\
|
||||||
|
if (ctx.top<ctx.canary)\
|
||||||
|
goto *code[++ctx.pc];\
|
||||||
|
die("stack overflow");\
|
||||||
|
goto *code[++ctx.pc];\
|
||||||
|
}
|
||||||
|
// do not cause stackoverflow
|
||||||
|
#define exec_nodie(op) {\
|
||||||
|
op();\
|
||||||
|
goto *code[++ctx.pc];\
|
||||||
|
}
|
||||||
|
|
||||||
|
intg: exec_nodie(o_intg ); // +imm[pc] (detected at codegen)
|
||||||
|
intl: exec_nodie(o_intl ); // -0
|
||||||
|
loadg: exec_nodie(o_loadg ); // -1
|
||||||
|
loadl: exec_nodie(o_loadl ); // -1
|
||||||
|
loadu: exec_nodie(o_loadu ); // -1
|
||||||
|
pnum: exec_check(o_pnum ); // +1
|
||||||
|
pnil: exec_check(o_pnil ); // +1
|
||||||
|
pstr: exec_check(o_pstr ); // +1
|
||||||
|
newv: exec_check(o_newv ); // +1-imm[pc]
|
||||||
|
newh: exec_check(o_newh ); // +1
|
||||||
|
newf: exec_check(o_newf ); // +1
|
||||||
|
happ: exec_nodie(o_happ ); // -1
|
||||||
|
para: exec_nodie(o_para ); // -0
|
||||||
|
deft: exec_nodie(o_deft ); // -1
|
||||||
|
dyn: exec_nodie(o_dyn ); // -0
|
||||||
|
lnot: exec_nodie(o_lnot ); // -0
|
||||||
|
usub: exec_nodie(o_usub ); // -0
|
||||||
|
bnot: exec_nodie(o_bnot ); // -0
|
||||||
|
btor: exec_nodie(o_btor ); // -1
|
||||||
|
btxor: exec_nodie(o_btxor ); // -1
|
||||||
|
btand: exec_nodie(o_btand ); // -1
|
||||||
|
add: exec_nodie(o_add ); // -1
|
||||||
|
sub: exec_nodie(o_sub ); // -1
|
||||||
|
mul: exec_nodie(o_mul ); // -1
|
||||||
|
div: exec_nodie(o_div ); // -1
|
||||||
|
lnk: exec_nodie(o_lnk ); // -1
|
||||||
|
addc: exec_nodie(o_addc ); // -0
|
||||||
|
subc: exec_nodie(o_subc ); // -0
|
||||||
|
mulc: exec_nodie(o_mulc ); // -0
|
||||||
|
divc: exec_nodie(o_divc ); // -0
|
||||||
|
lnkc: exec_nodie(o_lnkc ); // -0
|
||||||
|
addeq: exec_nodie(o_addeq ); // -1
|
||||||
|
subeq: exec_nodie(o_subeq ); // -1
|
||||||
|
muleq: exec_nodie(o_muleq ); // -1
|
||||||
|
diveq: exec_nodie(o_diveq ); // -1
|
||||||
|
lnkeq: exec_nodie(o_lnkeq ); // -1
|
||||||
|
bandeq: exec_nodie(o_bandeq); // -1
|
||||||
|
boreq: exec_nodie(o_boreq ); // -1
|
||||||
|
bxoreq: exec_nodie(o_bxoreq); // -1
|
||||||
|
addeqc: exec_nodie(o_addeqc); // -0
|
||||||
|
subeqc: exec_nodie(o_subeqc); // -0
|
||||||
|
muleqc: exec_nodie(o_muleqc); // -0
|
||||||
|
diveqc: exec_nodie(o_diveqc); // -0
|
||||||
|
lnkeqc: exec_nodie(o_lnkeqc); // -0
|
||||||
|
addecp: exec_nodie(o_addecp); // -1
|
||||||
|
subecp: exec_nodie(o_subecp); // -1
|
||||||
|
mulecp: exec_nodie(o_mulecp); // -1
|
||||||
|
divecp: exec_nodie(o_divecp); // -1
|
||||||
|
lnkecp: exec_nodie(o_lnkecp); // -1
|
||||||
|
meq: exec_nodie(o_meq ); // -1
|
||||||
|
eq: exec_nodie(o_eq ); // -1
|
||||||
|
neq: exec_nodie(o_neq ); // -1
|
||||||
|
less: exec_nodie(o_less ); // -1
|
||||||
|
leq: exec_nodie(o_leq ); // -1
|
||||||
|
grt: exec_nodie(o_grt ); // -1
|
||||||
|
geq: exec_nodie(o_geq ); // -1
|
||||||
|
lessc: exec_nodie(o_lessc ); // -0
|
||||||
|
leqc: exec_nodie(o_leqc ); // -0
|
||||||
|
grtc: exec_nodie(o_grtc ); // -0
|
||||||
|
geqc: exec_nodie(o_geqc ); // -0
|
||||||
|
pop: exec_nodie(o_pop ); // -1
|
||||||
|
jmp: exec_nodie(o_jmp ); // -0
|
||||||
|
jt: exec_nodie(o_jt ); // -0
|
||||||
|
jf: exec_nodie(o_jf ); // -1
|
||||||
|
cnt: exec_nodie(o_cnt ); // -0
|
||||||
|
findex: exec_check(o_findex); // +1
|
||||||
|
feach: exec_check(o_feach ); // +1
|
||||||
|
callg: exec_check(o_callg ); // +1
|
||||||
|
calll: exec_check(o_calll ); // +1
|
||||||
|
upval: exec_check(o_upval ); // +1
|
||||||
|
callv: exec_nodie(o_callv ); // -0
|
||||||
|
callvi: exec_nodie(o_callvi); // -0
|
||||||
|
callh: exec_nodie(o_callh ); // -0
|
||||||
|
callfv: exec_nodie(o_callfv); // check in the function
|
||||||
|
callfh: exec_nodie(o_callfh); // check in the function
|
||||||
|
callb: exec_nodie(o_callb ); // -0
|
||||||
|
slcbeg: exec_check(o_slcbeg); // +1
|
||||||
|
slcend: exec_nodie(o_slcend); // -1
|
||||||
|
slc: exec_nodie(o_slc ); // -1
|
||||||
|
slc2: exec_nodie(o_slc2 ); // -2
|
||||||
|
mcallg: exec_check(o_mcallg); // +1
|
||||||
|
mcalll: exec_check(o_mcalll); // +1
|
||||||
|
mupval: exec_check(o_mupval); // +1
|
||||||
|
mcallv: exec_nodie(o_mcallv); // -0
|
||||||
|
mcallh: exec_nodie(o_mcallh); // -0
|
||||||
|
ret: exec_nodie(o_ret ); // -2
|
||||||
|
#endif
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,132 @@
|
||||||
|
#include "optimizer.h"
|
||||||
|
|
||||||
|
void optimizer::const_string(
|
||||||
|
binary_operator* node,
|
||||||
|
string_literal* left_node,
|
||||||
|
string_literal* right_node) {
|
||||||
|
if (node->get_operator_type()!=binary_operator::binary_type::concat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& left = left_node->get_content();
|
||||||
|
const auto& right = right_node->get_content();
|
||||||
|
node->set_optimized_string(
|
||||||
|
new string_literal(node->get_location(), left+right));
|
||||||
|
}
|
||||||
|
|
||||||
|
void optimizer::const_number(
|
||||||
|
binary_operator* node,
|
||||||
|
number_literal* left_node,
|
||||||
|
number_literal* right_node) {
|
||||||
|
const auto left = left_node->get_number();
|
||||||
|
const auto right = right_node->get_number();
|
||||||
|
f64 res;
|
||||||
|
switch(node->get_operator_type()) {
|
||||||
|
case binary_operator::binary_type::add: res = left+right; break;
|
||||||
|
case binary_operator::binary_type::sub: res = left-right; break;
|
||||||
|
case binary_operator::binary_type::mult: res = left*right; break;
|
||||||
|
case binary_operator::binary_type::div: res = left/right; break;
|
||||||
|
case binary_operator::binary_type::less: res = left<right; break;
|
||||||
|
case binary_operator::binary_type::leq: res = left<=right; break;
|
||||||
|
case binary_operator::binary_type::grt: res = left>right; break;
|
||||||
|
case binary_operator::binary_type::geq: res = left>=right; break;
|
||||||
|
case binary_operator::binary_type::bitwise_or:
|
||||||
|
res = i32(left)|i32(right); break;
|
||||||
|
case binary_operator::binary_type::bitwise_xor:
|
||||||
|
res = i32(left)^i32(right); break;
|
||||||
|
case binary_operator::binary_type::bitwise_and:
|
||||||
|
res = i32(left)&i32(right); break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
if (std::isinf(res) || std::isnan(res)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node->set_optimized_number(
|
||||||
|
new number_literal(node->get_location(), res));
|
||||||
|
}
|
||||||
|
|
||||||
|
void optimizer::const_number(
|
||||||
|
unary_operator* node,
|
||||||
|
number_literal* value_node) {
|
||||||
|
auto res = value_node->get_number();
|
||||||
|
switch(node->get_operator_type()) {
|
||||||
|
case unary_operator::unary_type::negative: res = -res; break;
|
||||||
|
case unary_operator::unary_type::bitwise_not: res = ~i32(res); break;
|
||||||
|
case unary_operator::unary_type::logical_not: res = !res; break;
|
||||||
|
}
|
||||||
|
if (std::isinf(res) || std::isnan(res)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node->set_optimized_number(
|
||||||
|
new number_literal(node->get_location(), res));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool optimizer::visit_binary_operator(binary_operator* node) {
|
||||||
|
node->get_left()->accept(this);
|
||||||
|
node->get_right()->accept(this);
|
||||||
|
number_literal* left_num_node = nullptr;
|
||||||
|
number_literal* right_num_node = nullptr;
|
||||||
|
string_literal* left_str_node = nullptr;
|
||||||
|
string_literal* right_str_node = nullptr;
|
||||||
|
if (node->get_left()->get_type()==expr_type::ast_num) {
|
||||||
|
left_num_node = (number_literal*)node->get_left();
|
||||||
|
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
|
||||||
|
((binary_operator*)node->get_left())->get_optimized_number()) {
|
||||||
|
left_num_node = ((binary_operator*)node->get_left())->get_optimized_number();
|
||||||
|
} else if (node->get_left()->get_type()==expr_type::ast_unary &&
|
||||||
|
((unary_operator*)node->get_left())->get_optimized_number()) {
|
||||||
|
left_num_node = ((unary_operator*)node->get_left())->get_optimized_number();
|
||||||
|
}
|
||||||
|
if (node->get_right()->get_type()==expr_type::ast_num) {
|
||||||
|
right_num_node = (number_literal*)node->get_right();
|
||||||
|
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
|
||||||
|
((binary_operator*)node->get_right())->get_optimized_number()) {
|
||||||
|
right_num_node = ((binary_operator*)node->get_right())->get_optimized_number();
|
||||||
|
} else if (node->get_right()->get_type()==expr_type::ast_unary &&
|
||||||
|
((unary_operator*)node->get_right())->get_optimized_number()) {
|
||||||
|
right_num_node = ((unary_operator*)node->get_right())->get_optimized_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->get_left()->get_type()==expr_type::ast_str) {
|
||||||
|
left_str_node = (string_literal*)node->get_left();
|
||||||
|
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
|
||||||
|
((binary_operator*)node->get_left())->get_optimized_string()) {
|
||||||
|
left_str_node = ((binary_operator*)node->get_left())->get_optimized_string();
|
||||||
|
}
|
||||||
|
if (node->get_right()->get_type()==expr_type::ast_str) {
|
||||||
|
right_str_node = (string_literal*)node->get_right();
|
||||||
|
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
|
||||||
|
((binary_operator*)node->get_right())->get_optimized_string()) {
|
||||||
|
right_str_node = ((binary_operator*)node->get_right())->get_optimized_string();
|
||||||
|
}
|
||||||
|
if (left_num_node && right_num_node) {
|
||||||
|
const_number(node, left_num_node, right_num_node);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (left_str_node && right_str_node) {
|
||||||
|
const_string(node, left_str_node, right_str_node);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool optimizer::visit_unary_operator(unary_operator* node) {
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
number_literal* value_node = nullptr;
|
||||||
|
if (node->get_value()->get_type()==expr_type::ast_num) {
|
||||||
|
value_node = (number_literal*)node->get_value();
|
||||||
|
} else if (node->get_value()->get_type()==expr_type::ast_binary &&
|
||||||
|
((binary_operator*)node->get_value())->get_optimized_number()) {
|
||||||
|
value_node = ((binary_operator*)node->get_value())->get_optimized_number();
|
||||||
|
} else if (node->get_value()->get_type()==expr_type::ast_unary &&
|
||||||
|
((unary_operator*)node->get_value())->get_optimized_number()) {
|
||||||
|
value_node = ((unary_operator*)node->get_value())->get_optimized_number();
|
||||||
|
}
|
||||||
|
if (value_node) {
|
||||||
|
const_number(node, value_node);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void optimizer::do_optimization(code_block* root) {
|
||||||
|
root->accept(this);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
#include "ast_visitor.h"
|
||||||
|
|
||||||
|
class optimizer:public ast_visitor {
|
||||||
|
private:
|
||||||
|
void const_string(binary_operator*, string_literal*, string_literal*);
|
||||||
|
void const_number(binary_operator*, number_literal*, number_literal*);
|
||||||
|
void const_number(unary_operator*, number_literal*);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool visit_binary_operator(binary_operator*) override;
|
||||||
|
bool visit_unary_operator(unary_operator*) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void do_optimization(code_block*);
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include "symbol_finder.h"
|
||||||
|
|
||||||
|
bool symbol_finder::visit_definition_expr(definition_expr* node) {
|
||||||
|
if (node->get_variable_name()) {
|
||||||
|
symbols.push_back(node->get_variable_name()->get_name());
|
||||||
|
} else {
|
||||||
|
for(auto i : node->get_variables()->get_variables()) {
|
||||||
|
symbols.push_back(i->get_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node->get_value()->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool symbol_finder::visit_function(function* node) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool symbol_finder::visit_iter_expr(iter_expr* node) {
|
||||||
|
if (node->get_name()) {
|
||||||
|
symbols.push_back(node->get_name()->get_name());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string>& symbol_finder::do_find(code_block* root) {
|
||||||
|
symbols.clear();
|
||||||
|
root->accept(this);
|
||||||
|
return symbols;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal_ast.h"
|
||||||
|
#include "ast_visitor.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class symbol_finder:public ast_visitor {
|
||||||
|
private:
|
||||||
|
std::vector<std::string> symbols;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool visit_definition_expr(definition_expr*) override;
|
||||||
|
bool visit_function(function*) override;
|
||||||
|
bool visit_iter_expr(iter_expr*) override;
|
||||||
|
const std::vector<std::string>& do_find(code_block*);
|
||||||
|
};
|
|
@ -222,8 +222,7 @@ var isfunc=func(f){
|
||||||
}
|
}
|
||||||
|
|
||||||
var isghost=func(g){
|
var isghost=func(g){
|
||||||
die("this runtime has no ghost object");
|
return typeof(g)=="obj";
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ishash=func(h){
|
var ishash=func(h){
|
||||||
|
@ -251,6 +250,9 @@ var isvec=func(v){
|
||||||
return typeof(v)=="vec";
|
return typeof(v)=="vec";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ghosttype=func(ghost_object) {
|
||||||
|
return __ghosttype(ghost_object);
|
||||||
|
}
|
||||||
|
|
||||||
# get the index of val in the vec
|
# get the index of val in the vec
|
||||||
var vecindex=func(vec,val){
|
var vecindex=func(vec,val){
|
|
@ -1,5 +1,5 @@
|
||||||
import.stl.padding;
|
import.std.padding;
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
|
|
||||||
var char_ttf=[
|
var char_ttf=[
|
||||||
[" "," "," "," "," "," "],
|
[" "," "," "," "," "," "],
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Road check and auto pilot by ValKmjolnir
|
# Road check and auto pilot by ValKmjolnir
|
||||||
import.stl.fg_env;
|
import.std.fg_env;
|
||||||
|
|
||||||
var dt=0.01;
|
var dt=0.01;
|
||||||
var intergral=0;
|
var intergral=0;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import.stl.queue;
|
import.std.queue;
|
||||||
|
|
||||||
rand(time(0));
|
rand(time(0));
|
||||||
var pixel=[' ','#','.','*'];
|
var pixel=[' ','#','.','*'];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import.stl.mat;
|
import.std.mat;
|
||||||
|
|
||||||
rand(time(0));
|
rand(time(0));
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import.stl.padding;
|
import.std.padding;
|
||||||
import.stl.file;
|
import.std.file;
|
||||||
import.stl.sort;
|
import.std.sort;
|
||||||
|
|
||||||
var source=find_all_files_with_extension(".","cpp","h");
|
var source=find_all_files_with_extension("./src","cpp","h");
|
||||||
sort(source,func(a,b){return cmp(a,b)<0});
|
sort(source,func(a,b){return cmp(a,b)<0});
|
||||||
|
|
||||||
var lib=find_all_files_with_extension("./stl","nas");
|
var lib=find_all_files_with_extension("./std","nas");
|
||||||
sort(lib,func(a,b){return cmp(a,b)<0});
|
sort(lib,func(a,b){return cmp(a,b)<0});
|
||||||
|
|
||||||
var testfile=find_all_files_with_extension("./test","nas");
|
var testfile=find_all_files_with_extension("./test","nas");
|
||||||
|
@ -78,8 +78,8 @@ var calc=func(codetype,files,path=""){
|
||||||
return int(bytes/1024);
|
return int(bytes/1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
var all=calc("source code:",source)
|
var all=calc("source code:",source,"src/")
|
||||||
+calc("lib:",lib,"stl/")
|
+calc("lib:",lib,"std/")
|
||||||
+calc("test file:",testfile,"test/")
|
+calc("test file:",testfile,"test/")
|
||||||
+calc("module:",module,"module/");
|
+calc("module:",module,"module/");
|
||||||
println(rightpad("total:",padding_length),'|',leftpad(str(all),6),' kb |');
|
println(rightpad("total:",padding_length),'|',leftpad(str(all),6),' kb |');
|
|
@ -1,7 +1,7 @@
|
||||||
# coroutine.nas by ValKmjolnir
|
# coroutine.nas by ValKmjolnir
|
||||||
# 2022/5/19
|
# 2022/5/19
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
import.stl.padding;
|
import.std.padding;
|
||||||
|
|
||||||
if(os.platform()=="windows"){
|
if(os.platform()=="windows"){
|
||||||
system("chcp 65001");
|
system("chcp 65001");
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import.stl.sort;
|
import.std.sort;
|
||||||
import.stl.padding;
|
import.std.padding;
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
|
|
||||||
var mess=func(vec) {
|
var mess=func(vec) {
|
||||||
srand();
|
srand();
|
||||||
|
|
|
@ -5,9 +5,10 @@ var http=func(){
|
||||||
return {
|
return {
|
||||||
establish:func(ip,port){
|
establish:func(ip,port){
|
||||||
sd=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP);
|
sd=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP);
|
||||||
if(socket.bind(sd,ip,port)<0){
|
while(socket.bind(sd,ip,port)<0){
|
||||||
println("failed to bind socket "~sd~" at IP: "~ip~" port: "~port~".");
|
println("[",os.time(),"] failed to bind socket "~sd~" at IP: "~ip~" port: "~port~".");
|
||||||
return;
|
unix.sleep(5);
|
||||||
|
println("[",os.time(),"] retrying...");
|
||||||
}
|
}
|
||||||
socket.listen(sd,1);
|
socket.listen(sd,1);
|
||||||
println("[",os.time(),"] start server at [",ip,":",port,"]");
|
println("[",os.time(),"] start server at [",ip,":",port,"]");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import.stl.json;
|
import.std.json;
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
|
|
||||||
var ss=JSON.stringify({
|
var ss=JSON.stringify({
|
||||||
vec:[0,1,2],
|
vec:[0,1,2],
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import.module.libsock;
|
import.module.libsock;
|
||||||
import.stl.json;
|
import.std.json;
|
||||||
|
|
||||||
var gettime=func(){
|
var gettime=func(){
|
||||||
return split(" ",os.time())[1];
|
return split(" ",os.time())[1];
|
||||||
|
|
|
@ -172,7 +172,7 @@ var lexer=func(file)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var lex=lexer("stl/fg_env.nas");
|
var lex=lexer("std/fg_env.nas");
|
||||||
lex.compile();
|
lex.compile();
|
||||||
foreach(var tok;lex.get_token())
|
foreach(var tok;lex.get_token())
|
||||||
print('(',tok.line,' | ',tok.token,')\n');
|
print('(',tok.line,' | ',tok.token,')\n');
|
|
@ -1,4 +1,4 @@
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
|
|
||||||
var new_map=func(width,height){
|
var new_map=func(width,height){
|
||||||
var tmp=[];
|
var tmp=[];
|
||||||
|
@ -11,7 +11,7 @@ var new_map=func(width,height){
|
||||||
}
|
}
|
||||||
|
|
||||||
var prt=func(map){
|
var prt=func(map){
|
||||||
var s='\ec\e[1;1H';
|
var s='\e[H';
|
||||||
foreach(var line;map){
|
foreach(var line;map){
|
||||||
foreach(var elem;line)
|
foreach(var elem;line)
|
||||||
s~=elem~' ';
|
s~=elem~' ';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import.test.md5;
|
import.test.md5;
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
import.stl.file;
|
import.std.file;
|
||||||
srand();
|
srand();
|
||||||
|
|
||||||
var compare=func() {
|
var compare=func() {
|
||||||
|
@ -48,15 +48,18 @@ var filechecksum=func(){
|
||||||
foreach(var p;find_all_files_with_extension("./test","nas")) {
|
foreach(var p;find_all_files_with_extension("./test","nas")) {
|
||||||
append(files,"./test/"~p);
|
append(files,"./test/"~p);
|
||||||
}
|
}
|
||||||
foreach(var p;find_all_files_with_extension("./stl","nas")) {
|
foreach(var p;find_all_files_with_extension("./std","nas")) {
|
||||||
append(files,"./stl/"~p);
|
append(files,"./std/"~p);
|
||||||
}
|
}
|
||||||
foreach(var p;find_all_files_with_extension("./module","nas","cpp")) {
|
foreach(var p;find_all_files_with_extension("./module","nas","cpp")) {
|
||||||
append(files,"./module/"~p);
|
append(files,"./module/"~p);
|
||||||
}
|
}
|
||||||
foreach(var p;find_all_files_with_extension(".","cpp","h","md")) {
|
foreach(var p;find_all_files_with_extension(".","md")) {
|
||||||
append(files,"./"~p);
|
append(files,"./"~p);
|
||||||
}
|
}
|
||||||
|
foreach(var p;find_all_files_with_extension("./src","cpp","h")) {
|
||||||
|
append(files,"./src/"~p);
|
||||||
|
}
|
||||||
foreach(var p;find_all_files_with_extension("./doc","md")) {
|
foreach(var p;find_all_files_with_extension("./doc","md")) {
|
||||||
append(files,"./doc/"~p);
|
append(files,"./doc/"~p);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
import.module.libkey;
|
import.module.libkey;
|
||||||
|
|
||||||
var is_windows_platform=os.platform()=="windows";
|
var is_windows_platform=os.platform()=="windows";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import.stl.process_bar;
|
import.std.process_bar;
|
||||||
|
|
||||||
var ppm=func(filename,width,height,RGB){
|
var ppm=func(filename,width,height,RGB){
|
||||||
# P3 use ASCII number
|
# P3 use ASCII number
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import.stl.sort;
|
import.std.sort;
|
||||||
|
|
||||||
var vec=[];
|
var vec=[];
|
||||||
rand(time(0));
|
rand(time(0));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import.stl.sort;
|
import.std.sort;
|
||||||
|
|
||||||
var to_lower=func(s){
|
var to_lower=func(s){
|
||||||
var tmp="";
|
var tmp="";
|
||||||
|
|
Loading…
Reference in New Issue