Merge pull request #20 from ValKmjolnir/develop

🎇 refactor ast structure & build process
This commit is contained in:
Li Haokun 2023-07-02 22:16:09 +08:00 committed by GitHub
commit 8ca7ee1d90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 10067 additions and 7406 deletions

View File

@ -4,7 +4,7 @@ on:
schedule:
- cron: "0 16 * * *"
push:
branches: [ master ]
branches: [ master,develop ]
pull_request:
branches: [ master ]
workflow_dispatch:
@ -16,9 +16,9 @@ jobs:
- uses: actions/checkout@v2
- name: make
run: |
make
make -j4
cd module
make all
make all -j4
cd ..
make test
tar -czf nasal-mac-nightly.tgz .
@ -42,9 +42,9 @@ jobs:
- uses: actions/checkout@v2
- name: make
run: |
make
make -j4
cd module
make all
make all -j4
cd ..
make test
touch nasal-linux-x86_64-nightly.tgz

View File

@ -4,37 +4,44 @@ project(nasal VERSION 10.1)
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
# -std=c++14 -Wshadow -Wall
set(CMAKE_CXX_STANDARD 14)
# -std=c++17 -Wshadow -Wall
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
add_compile_options(-fPIC)
# generate release executables
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR})
# build nasal used object
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)
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR})
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
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()
# build nasal
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/main.cpp)
target_link_libraries(nasal nasal-object)
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(nasal dl)
endif()
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR})
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
add_custom_command(
TARGET nasal POST_BUILD
@ -42,4 +49,28 @@ if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
${CMAKE_SOURCE_DIR}/build/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
View File

@ -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
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:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The licenses for most software are designed to take away your
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
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to 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.

View File

@ -4,8 +4,8 @@
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v10.1-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-MIT-green?style=flat-square&logo=github)](./LICENSE)
![in dev](https://img.shields.io/badge/dev-v11.0-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE)
> 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/).
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).
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?__
@ -59,7 +59,6 @@ the interpreter a useful tool in your own projects.
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU)
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)
![vs](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio)
@ -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.
Use g++(`MinGW-w64`) or MSVC(`Visual Studio`) on `Windows` .
Use g++/clang++ on `Linux/macOS/Unix` platform (we suggest `clang`).
On `Windows (MinGW-w64)`:
### __`Windows (MinGW-w64)`__
Make sure your MinGW thread model is `posix thread model`, otherwise it may not have the thread library.
> 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

View File

@ -4,8 +4,8 @@
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v10.1-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-MIT-green?style=flat-square&logo=github)](../LICENSE)
![in dev](https://img.shields.io/badge/dev-v11.0-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE)
> 这篇文档包含多语言版本: [__中文__](../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为我们带来了这样一个神奇且简洁的编程语言。
该项目使用 __MIT__ 协议开源 (2021/5/4)。
该项目使用 __MIT__ 协议开源 (2019/7~2021/5/4~2023/5),从 2023/6 开始使用 __GPL v2__ 协议
### __我们为什么想要重新写一个nasal解释器?__
@ -49,29 +49,26 @@ __如果有好的意见或建议欢迎联系我们!__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU)
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)
![vs](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio)
我们推荐你下载最新更新的代码包来直接编译,这个项目非常小巧,没有使用任何第三方库,因此编译起来非常轻松,
我们推荐你下载最新代码包编译,这个项目非常小巧,没有使用任何第三方库,因此编译起来非常轻松,
你只需要这两样东西: C++ 编译器以及make程序。
__注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构建这个解释器在下载之前请阅读[__发行日志__](../doc/dev_zh.md#发行日志)以保证这个发行版的文件中不包含非常严重的bug。
`Windows` 用户通过 g++(`MinGW-w64`) 或者使用 MSVC(`Visual Studio`) 来进行编译。
`Linux/macOS/Unix` 用户可以使用 g++ 或者 clang++ 来进行编译 (建议您使用 `clang`)。
`Windows` 平台(`MinGW-w64`):
### __`Windows` 平台(`MinGW-w64`)__
一定要确保您的 MinGW thread model 是 `posix thread model`, 否则可能存在没有 thread 库的问题。
> 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

View File

@ -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/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/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>
<h2>&nbsp;Introduction | 介绍</h2>
<text>
@ -51,12 +51,11 @@
<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.
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>
这个解释器是由<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>
</text>
<h2>&nbsp;Benchmark | 执行效率</h2>

View File

@ -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
View File

@ -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
View File

@ -1,37 +1,89 @@
.PHONY:test clean
STD=c++17
SRC=\
main.cpp\
nasal_ast.h\
nasal_err.h\
nasal_builtin.h\
nasal_opcode.h\
nasal_opt.h\
nasal_codegen.h\
nasal_gc.h\
nasal_import.h\
nasal_lexer.h\
nasal_parse.h\
nasal_vm.h\
nasal_dbg.h\
nasal.h
NASAL_OBJECT=\
nasal_err.o\
nasal_ast.o\
ast_visitor.o\
ast_dumper.o\
nasal_lexer.o\
nasal_parse.o\
nasal_import.o\
optimizer.o\
nasal_opcode.o\
symbol_finder.o\
nasal_codegen.o\
nasal_misc.o\
nasal_gc.o\
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)
$(CXX) -std=$(STD) -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
nasal.exe:$(SRC)
$(CXX) -std=$(STD) -O3 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
nasal.exe: $(NASAL_OBJECT)
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
stable-release:$(SRC)
$(CXX) -std=$(STD) -O2 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
stable-release-mingw:$(SRC)
$(CXX) -std=$(STD) -O2 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
main.o: src/main.cpp
$(CXX) -std=$(STD) -c -O3 src/main.cpp -fno-exceptions -fPIC -o main.o -I .
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:
@ echo "[clean] nasal" && if [ -e nasal ]; then rm nasal; fi
@ echo "[clean] nasal.exe" && if [ -e nasal.exe ]; then rm nasal.exe; fi
@ rm $(NASAL_OBJECT)
.PHONY: test
test:nasal
@ ./nasal -e test/ascii-art.nas
@ ./nasal -t -d test/bfs.nas

View File

@ -1,7 +1,7 @@
// module for test
#include <iostream>
#include "../nasal.h"
#include "../src/nasal.h"
namespace nasal_fib_module {

View File

@ -1,4 +1,4 @@
#include "../nasal.h"
#include "../src/nasal.h"
#include <iostream>
#ifndef _MSC_VER

View File

@ -1,59 +1,60 @@
.PHONY=clean all winall
dynamic_libs_so=libfib.so libkey.so libnasock.so libmat.so
dynamic_libs_dll=libfib.dll libkey.dll libnasock.dll libmat.dll
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
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)
@ echo "[Compiling] done"
winall: $(dynamic_libs_dll)
@ echo [Compiling] done
libfib.so: fib.cpp $(used_header)
libfib.so: fib.cpp $(used_header) $(used_object)
@ echo "[Compiling] libfib.so"
@ $(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
libfib.dll: fib.cpp $(used_header)
libfib.dll: fib.cpp $(used_header) $(used_object)
@ echo [Compiling] libfib.dll
@ $(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
libkey.so: keyboard.cpp $(used_header)
libkey.so: keyboard.cpp $(used_header) $(used_object)
@ echo "[Compiling] libkey.so"
@ $(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
libkey.dll: keyboard.cpp $(used_header)
libkey.dll: keyboard.cpp $(used_header) $(used_object)
@ echo [Compiling] libkey.dll
@ $(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
libnasock.so: nasocket.cpp $(used_header)
libnasock.so: nasocket.cpp $(used_header) $(used_object)
@ echo "[Compiling] libnasock.so"
@ $(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
libnasock.dll: nasocket.cpp $(used_header)
libnasock.dll: nasocket.cpp $(used_header) $(used_object)
@ echo [Compiling] libnasock.dll
@ $(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
libmat.so: matrix.cpp $(used_header)
libmat.so: matrix.cpp $(used_header) $(used_object)
@ echo "[Compiling] libmat.so"
@ $(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
libmat.dll: matrix.cpp $(used_header)
libmat.dll: matrix.cpp $(used_header) $(used_object)
@ echo [Compiling] libmat.dll
@ $(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
clean:

View File

@ -1,4 +1,4 @@
#include "../nasal.h"
#include "../src/nasal.h"
#include <cmath>
var nas_vec2(var* args, usize size, gc* ngc) {

View File

@ -1,4 +1,4 @@
#include "../nasal.h"
#include "../src/nasal.h"
#ifndef _MSC_VER
#include <unistd.h>

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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*);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

File diff suppressed because it is too large Load Diff

473
src/ast_dumper.cpp Normal file
View File

@ -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;
}

82
src/ast_dumper.h Normal file
View File

@ -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);
}
};

229
src/ast_visitor.cpp Normal file
View File

@ -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;
}

43
src/ast_visitor.h Normal file
View File

@ -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*);
};

209
src/main.cpp Normal file
View File

@ -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;
}

53
src/nasal.h Normal file
View File

@ -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"

365
src/nasal_ast.cpp Normal file
View File

@ -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);
}

674
src/nasal_ast.h Normal file
View File

@ -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

139
src/nasal_builtin.h Normal file
View File

@ -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[];

1159
src/nasal_codegen.cpp Normal file

File diff suppressed because it is too large Load Diff

103
src/nasal_codegen.h Normal file
View File

@ -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;}
};

View File

@ -1,43 +1,10 @@
#pragma once
#include "nasal_dbg.h"
#include "nasal_import.h"
#include "nasal_err.h"
#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;
std::vector<std::string> dbg::parse(const std::string& cmd) {
std::vector<std::string> res;
usize last=0;
usize pos=cmd.find(" ", 0);
while(pos!=string::npos) {
while(pos!=std::string::npos) {
if (pos>last) {
res.push_back(cmd.substr(last, pos-last));
}
@ -50,7 +17,7 @@ std::vector<string> dbg::parse(const string& cmd) {
return res;
}
u16 dbg::fileindex(const string& filename) {
u16 dbg::file_index(const std::string& filename) const {
for(u16 i=0;i<fsize;++i) {
if (filename==files[i]) {
return i;
@ -66,24 +33,30 @@ void dbg::err() {
}
void dbg::help() {
std::cout
std::clog
<<"<option>\n"
<<"\th, help | get help\n"
<<"\tbt, backtrace | get function call trace\n"
<<"\tc, continue | run program until break point or exit\n"
<<"\tf, file | see all the compiled files\n"
<<"\tg, global | see global values\n"
<<"\tl, local | see local values\n"
<<"\tu, upval | see upvalue\n"
<<"\tr, register | show vm register detail\n"
<<"\ta, all | show global,local and upvalue\n"
<<"\tn, next | execute next bytecode\n"
<<"\tq, exit | exit debugger\n"
<<" h, help | get help\n"
<<" bt, backtrace | get function call trace\n"
<<" c, continue | run program until break point or exit\n"
<<" f, file | see all the compiled files\n"
<<" g, global | see global values\n"
<<" l, local | see local values\n"
<<" u, upval | see upvalue\n"
<<" r, register | show vm register detail\n"
<<" a, all | show global,local and upvalue\n"
<<" n, next | execute next bytecode\n"
<<" q, exit | exit debugger\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;
std::vector<op> opcall;
u64 total=0;
@ -105,22 +78,22 @@ void dbg::callsort(const u64* arr) {
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 begin=(line>>3)==0?0:((line>>3)<<3);
u32 end=(1+(line>>3))<<3;
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) {
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);
end=(1+(ctx.pc>>3))<<3;
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) {
std::cout
std::clog
<<(i==ctx.pc?back_white:reset)
<<(i==ctx.pc?"--> ":" ")
<<codestream(bytecode[i], i)
@ -130,65 +103,51 @@ void dbg::stepinfo() {
}
void dbg::interact() {
// special operand
// special operand, end execution
if (bytecode[ctx.pc].op==op_exit) {
return;
}
if (
(bytecode[ctx.pc].fidx!=bk_fidx || bytecode[ctx.pc].line!=bk_line) && // break point
!next // next step
) {
if ((bytecode[ctx.pc].fidx!=bk_fidx ||
bytecode[ctx.pc].line!=bk_line) && // break point
!next) {// next step
return;
}
next=false;
string cmd;
stepinfo();
std::string cmd;
step_info();
while(true) {
std::cout<<">> ";
std::clog<<">> ";
std::getline(std::cin, cmd);
auto res=parse(cmd);
if (res.size()==0) {
stepinfo();
step_info();
} else if (res.size()==1) {
if (res[0]=="h" || res[0]=="help") {
help();
} else if (res[0]=="bt" || res[0]=="backtrace") {
traceback();
} else if (res[0]=="c" || res[0]=="continue") {
return;
} else if (res[0]=="f" || res[0]=="file") {
for(usize i=0;i<fsize;++i) {
std::cout<<"["<<i<<"] "<<files[i]<<"\n";
}
} else if (res[0]=="g" || res[0]=="global") {
gstate();
} else if (res[0]=="l" || res[0]=="local") {
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();
switch(get_cmd_type(res[0])) {
case dbg_cmd::cmd_help: help(); break;
case dbg_cmd::cmd_backtrace: traceback(); break;
case dbg_cmd::cmd_continue: return;
case dbg_cmd::cmd_list_file: list_file(); break;
case dbg_cmd::cmd_global: gstate(); break;
case dbg_cmd::cmd_local: lstate(); break;
case dbg_cmd::cmd_upval: ustate(); break;
case dbg_cmd::cmd_register: reginfo(); break;
case dbg_cmd::cmd_show_all: detail(); break;
case dbg_cmd::cmd_next: next=true; return;
case dbg_cmd::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 && (res[0]=="bk" || res[0]=="break")) {
bk_fidx=fileindex(res[1]);
} else if (res.size()==3 &&
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
bk_fidx=file_index(res[1]);
if (bk_fidx==65535) {
std::cout<<"cannot find file named `"<<res[1]<<"`\n";
bk_fidx=0;
std::clog<<"cannot find file named `"<<res[1]<<"`\n";
continue;
}
i32 tmp=atoi(res[2].c_str());
if (tmp<=0) {
std::cout<<"incorrect line number `"<<res[2]<<"`\n";
std::clog<<"incorrect line number `"<<res[2]<<"`\n";
} else {
bk_line=tmp;
}
@ -201,8 +160,7 @@ void dbg::interact() {
void dbg::run(
const codegen& gen,
const linker& linker,
const std::vector<string>& argv
) {
const std::vector<std::string>& argv) {
verbose=true;
fsize=linker.filelist().size();
init(gen.strs(), gen.nums(), gen.codes(), linker.filelist(), argv);
@ -269,7 +227,7 @@ void dbg::run(
++ctx.pc;
}
callsort(count);
call_sort(count);
ngc.info();
ngc.clear();
imm.clear();

88
src/nasal_dbg.h Normal file
View File

@ -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>&
);
};

View File

@ -1,228 +1,172 @@
#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;
string file;
};
#ifdef _WIN32
#include <windows.h> // use SetConsoleTextAttribute
struct for_reset {
CONSOLE_SCREEN_BUFFER_INFO scr;
for_reset() {
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
}
} reset_ter_color;
#endif
std::ostream& back_white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
#else
s<<"\033[7m";
#endif
return s;
}
std::ostream& red(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
#else
s<<"\033[91;1m";
#endif
return s;
}
std::ostream& cyan(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
#else
s<<"\033[36;1m";
#endif
return s;
}
std::ostream& orange(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
#else
s<<"\033[93;1m";
#endif
return s;
}
std::ostream& white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
#else
s<<"\033[0m\033[1m";
#endif
return s;
}
std::ostream& reset(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes);
#else
s<<"\033[0m";
#endif
return s;
}
class flstream {
protected:
string file;
std::vector<string> res;
public:
flstream():file("") {}
void load(const string&);
const string& operator[](usize n) const {return res[n];}
const string& name() const {return file;}
usize size() const {return res.size();}
};
class error:public flstream {
private:
u32 cnt; // counter for errors
string identation(usize len) {
return string(len,' ');
}
string leftpad(u32 num, usize len) {
string tmp=std::to_string(num);
while(tmp.length()<len) {
tmp=" "+tmp;
}
return tmp;
}
public:
error():cnt(0) {}
void fatal(const string&, const string&);
void err(const string&, const string&);
void err(const string&, const span&, const string&);
void chkerr() const {
if (cnt) {
std::exit(1);
}
}
};
void flstream::load(const string& f) {
if (file==f) { // don't need to load a loaded file
return;
} else {
file=f;
}
res.clear();
std::ifstream in(f, std::ios::binary);
if (in.fail()) {
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
std::exit(1);
}
while(!in.eof()) {
string line;
std::getline(in, line);
res.push_back(line);
}
}
void error::fatal(const string& stage, const string& info) {
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
} else {
std::cerr<<reset<<"\n";
}
std::exit(1);
}
void error::err(const string& stage, const string& info) {
++cnt;
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
} else {
std::cerr<<reset<<"\n";
}
}
void error::err(const string& stage, const span& loc, const string& info) {
// load error occurred file into string lines
load(loc.file);
++cnt;
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> "
<<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";
#include "nasal_err.h"
#ifdef _WIN32
#include <windows.h> // use SetConsoleTextAttribute
struct for_reset {
CONSOLE_SCREEN_BUFFER_INFO scr;
for_reset() {
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
}
} reset_ter_color;
#endif
std::ostream& back_white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
#else
s<<"\033[7m";
#endif
return s;
}
std::ostream& red(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
#else
s<<"\033[91;1m";
#endif
return s;
}
std::ostream& cyan(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
#else
s<<"\033[36;1m";
#endif
return s;
}
std::ostream& orange(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
#else
s<<"\033[93;1m";
#endif
return s;
}
std::ostream& white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
#else
s<<"\033[0m\033[1m";
#endif
return s;
}
std::ostream& reset(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes);
#else
s<<"\033[0m";
#endif
return s;
}
void flstream::load(const std::string& f) {
if (file==f) { // don't need to load a loaded file
return;
} else {
file=f;
}
res.clear();
std::ifstream in(f, std::ios::binary);
if (in.fail()) {
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
std::exit(1);
}
while(!in.eof()) {
std::string line;
std::getline(in, line);
res.push_back(line);
}
}
void error::fatal(const std::string& stage, const std::string& info) {
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
} else {
std::cerr<<reset<<"\n";
}
std::exit(1);
}
void error::err(const std::string& stage, const std::string& info) {
++cnt;
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
} else {
std::cerr<<reset<<"\n";
}
}
void error::err(
const std::string& stage, const span& loc, const std::string& info) {
// load error occurred file into string lines
load(loc.file);
++cnt;
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> "
<<red<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<reset<<"\n";
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) {
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 std::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";
}

63
src/nasal_err.h Normal file
View File

@ -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);
}
}
};

577
src/nasal_gc.cpp Normal file
View File

@ -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();
}

419
src/nasal_gc.h Normal file
View File

@ -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*);

229
src/nasal_import.cpp Normal file
View File

@ -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;
}

40
src/nasal_import.h Normal file
View File

@ -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;}
};

View File

@ -1,506 +1,333 @@
#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
string str; // content
token() = default;
token(const token&) = default;
};
class lexer {
private:
u32 line;
u32 column;
usize ptr;
string filename;
string res;
error& err;
std::vector<token> toks;
const std::unordered_map<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 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 string&);
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 string&);
const std::vector<token>& result() const {return toks;}
};
bool lexer::skip(char c) {
return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0;
}
bool lexer::is_id(char c) {
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
}
bool lexer::is_hex(char c) {
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
}
bool lexer::is_oct(char c) {
return '0'<=c&&c<='7';
}
bool lexer::is_dec(char c) {
return '0'<=c&&c<='9';
}
bool lexer::is_str(char c) {
return c=='\''||c=='\"'||c=='`';
}
bool lexer::is_single_opr(char c) {
return (
c=='('||c==')'||c=='['||c==']'||
c=='{'||c=='}'||c==','||c==';'||
c==':'||c=='?'||c=='`'||c=='@'||
c=='%'||c=='$'||c=='\\'
);
}
bool lexer::is_calc_opr(char c) {
return (
c=='='||c=='+'||c=='-'||c=='*'||
c=='!'||c=='/'||c=='<'||c=='>'||
c=='~'||c=='|'||c=='&'||c=='^'
);
}
void lexer::skip_note() {
// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n') {}
}
void lexer::err_char() {
++column;
char c=res[ptr++];
err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c));
err.fatal("lexer", "fatal error occurred, stop");
}
void lexer::open(const string& file) {
// check file exsits and it is a regular file
struct stat buffer;
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
err.err("lexer", "<"+file+"> is not a regular file");
err.chkerr();
}
// load
filename=file;
std::ifstream in(file, std::ios::binary);
if (in.fail()) {
err.err("lexer", "failed to open <"+file+">");
} else {
err.load(file);
}
std::stringstream ss;
ss<<in.rdbuf();
res=ss.str();
}
tok lexer::get_type(const string& str) {
return typetbl.count(str)?typetbl.at(str):tok::null;
}
string lexer::utf8_gen() {
string str="";
while(ptr<res.size() && res[ptr]<0) {
string tmp="";
u32 nbytes=utf8_hdchk(res[ptr]);
if (!nbytes) {
++ptr;
++column;
continue;
}
tmp+=res[ptr++];
for(u32 i=0;i<nbytes;++i,++ptr) {
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp+=res[ptr];
}
}
// utf8 character's total length is 1+nbytes
if (tmp.length()!=1+nbytes) {
++column;
string utf_info="0x"+chrhex(tmp[0]);
for(u32 i=1;i<tmp.size();++i) {
utf_info+=" 0x"+chrhex(tmp[i]);
}
err.err("lexer", {line, column-1, line, column, filename}, "invalid utf-8 <"+utf_info+">");
err.fatal("lexer", "fatal error occurred, stop");
}
str+=tmp;
column+=2; // may have some problems because not all the unicode takes 2 space
}
return str;
}
token lexer::id_gen() {
u32 begin_line=line;
u32 begin_column=column;
string str="";
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8
str+=utf8_gen();
} else { // ascii
str+=res[ptr++];
++column;
}
}
tok type=get_type(str);
return {{begin_line, begin_column, line, column, filename}, (type!=tok::null)?type:tok::id, str};
}
token lexer::num_gen() {
u32 begin_line=line;
u32 begin_column=column;
// generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
string str="0x";
ptr+=2;
while(ptr<res.size() && is_hex(res[ptr])) {
str+=res[ptr++];
}
column+=str.length();
if (str.length()<3) { // "0x"
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
string str="0o";
ptr+=2;
while(ptr<res.size() && is_oct(res[ptr])) {
str+=res[ptr++];
}
bool erfmt=false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
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;
}
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include "nasal_lexer.h"
bool lexer::skip(char c) {
return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0;
}
bool lexer::is_id(char c) {
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
}
bool lexer::is_hex(char c) {
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
}
bool lexer::is_oct(char c) {
return '0'<=c&&c<='7';
}
bool lexer::is_dec(char c) {
return '0'<=c&&c<='9';
}
bool lexer::is_str(char c) {
return c=='\''||c=='\"'||c=='`';
}
bool lexer::is_single_opr(char c) {
return (
c=='('||c==')'||c=='['||c==']'||
c=='{'||c=='}'||c==','||c==';'||
c==':'||c=='?'||c=='`'||c=='@'||
c=='%'||c=='$'||c=='\\'
);
}
bool lexer::is_calc_opr(char c) {
return (
c=='='||c=='+'||c=='-'||c=='*'||
c=='!'||c=='/'||c=='<'||c=='>'||
c=='~'||c=='|'||c=='&'||c=='^'
);
}
void lexer::skip_note() {
// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n') {}
}
void lexer::err_char() {
++column;
char c=res[ptr++];
err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c));
err.fatal("lexer", "fatal error occurred, stop");
}
void lexer::open(const std::string& file) {
// check file exsits and it is a regular file
struct stat buffer;
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
err.err("lexer", "<"+file+"> is not a regular file");
err.chkerr();
}
// load
filename = file;
std::ifstream in(file, std::ios::binary);
if (in.fail()) {
err.err("lexer", "failed to open <" + file + ">");
} else {
err.load(file);
}
std::stringstream ss;
ss << in.rdbuf();
res = ss.str();
}
tok lexer::get_type(const std::string& str) {
return typetbl.count(str)?typetbl.at(str):tok::null;
}
std::string lexer::utf8_gen() {
std::string str="";
while(ptr<res.size() && res[ptr]<0) {
std::string tmp="";
u32 nbytes=utf8_hdchk(res[ptr]);
if (!nbytes) {
++ptr;
++column;
continue;
}
tmp+=res[ptr++];
for(u32 i=0;i<nbytes;++i,++ptr) {
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp+=res[ptr];
}
}
// utf8 character's total length is 1+nbytes
if (tmp.length()!=1+nbytes) {
++column;
std::string utf_info="0x"+chrhex(tmp[0]);
for(u32 i=1;i<tmp.size();++i) {
utf_info+=" 0x"+chrhex(tmp[i]);
}
err.err("lexer", {line, column-1, line, column, filename}, "invalid utf-8 <"+utf_info+">");
err.fatal("lexer", "fatal error occurred, stop");
}
str+=tmp;
column+=2; // may have some problems because not all the unicode takes 2 space
}
return str;
}
token lexer::id_gen() {
u32 begin_line=line;
u32 begin_column=column;
std::string str="";
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8
str+=utf8_gen();
} else { // ascii
str+=res[ptr++];
++column;
}
}
tok type=get_type(str);
return {{begin_line, begin_column, line, column, filename}, (type!=tok::null)?type:tok::id, str};
}
token lexer::num_gen() {
u32 begin_line=line;
u32 begin_column=column;
// generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
std::string str="0x";
ptr+=2;
while(ptr<res.size() && is_hex(res[ptr])) {
str+=res[ptr++];
}
column+=str.length();
if (str.length()<3) { // "0x"
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
std::string str="0o";
ptr+=2;
while(ptr<res.size() && is_oct(res[ptr])) {
str+=res[ptr++];
}
bool erfmt=false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
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]*)
std::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;
std::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;
std::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;
std::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
std::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 std::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;
}

183
src/nasal_lexer.h Normal file
View File

@ -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;}
};

View File

@ -1,255 +1,230 @@
#pragma once
#ifndef __nasver
#define __nasver "10.1"
#endif
#include <cstdint>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cmath>
#include <vector>
bool is_windows() {
#if defined(_WIN32) || defined(_WIN64)
return true;
#else
return false;
#endif
}
bool is_linux() {
#if defined __linux__
return true;
#else
return false;
#endif
}
bool is_macos() {
#if defined __APPLE__
return true;
#else
return false;
#endif
}
bool is_x86() {
#if defined(__i386__) || defined(_M_IX86)
return true;
#else
return false;
#endif
}
bool is_amd64() {
#if defined(__amd64__) || defined(_M_X64)
return true;
#else
return false;
#endif
}
bool is_x86_64() {
return is_amd64();
}
bool is_arm() {
#if defined(__arm__) || defined(_M_ARM)
return true;
#else
return false;
#endif
}
bool is_aarch64() {
#if defined(__aarch64__) || defined(_M_ARM64)
return true;
#else
return false;
#endif
}
bool is_ia64() {
#if defined(__ia64__)
return true;
#else
return false;
#endif
}
bool is_powerpc() {
#if defined(__powerpc__)
return true;
#else
return false;
#endif
}
bool is_superh() {
#if defined(__sh__)
return true;
#else
return false;
#endif
}
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;
using std::string;
const u32 STACK_DEPTH=1024;
f64 hex2f(const char* str) {
f64 ret=0;
for(; *str; ++str) {
if ('0'<=*str && *str<='9') {
ret=ret*16+(*str-'0');
} else if ('a'<=*str && *str<='f') {
ret=ret*16+(*str-'a'+10);
} else if ('A'<=*str && *str<='F') {
ret=ret*16+(*str-'A'+10);
} else {
return nan("");
}
}
return ret;
}
f64 oct2f(const char* str) {
f64 ret=0;
while('0'<=*str && *str<'8') {
ret=ret*8+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret;
}
// 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* str) {
f64 ret=0,negative=1,num_pow=0;
while('0'<=*str && *str<='9') {
ret=ret*10+(*str++-'0');
}
if (!*str) {
return ret;
}
if (*str=='.') {
if (!*++str) {
return nan("");
}
num_pow=0.1;
while('0'<=*str && *str<='9') {
ret+=num_pow*(*str++-'0');
num_pow*=0.1;
}
if (!*str) {
return ret;
}
}
if (*str!='e' && *str!='E') {
return nan("");
}
if (!*++str) {
return nan("");
}
if (*str=='-' || *str=='+') {
negative=(*str++=='-'? -1:1);
}
if (!*str) {
return nan("");
}
num_pow=0;
while('0'<=*str && *str<='9') {
num_pow=num_pow*10+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret*std::pow(10,negative*num_pow);
}
f64 str2num(const char* str) {
bool negative=false;
f64 res=0;
if (*str=='-' || *str=='+') {
negative=(*str++=='-');
}
if (!*str) {
return nan("");
}
if (str[0]=='0' && str[1]=='x') {
res=hex2f(str+2);
} else if (str[0]=='0' && str[1]=='o') {
res=oct2f(str+2);
} else {
res=dec2f(str);
}
return negative?-res:res;
}
i32 utf8_hdchk(const char head) {
// 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
return 1;
}
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
return 2;
}
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
return 3;
}
return 0;
}
string chrhex(const char c) {
const char hextbl[]="0123456789abcdef";
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
}
string rawstr(const string& str, const usize maxlen=0) {
string ret("");
for(auto i:str) {
// windows doesn't output unicode normally, so we output the hex
if (is_windows() && i<=0) {
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
#include "nasal.h"
bool is_windows() {
#if defined(_WIN32) || defined(_WIN64)
return true;
#else
return false;
#endif
}
bool is_linux() {
#if defined __linux__
return true;
#else
return false;
#endif
}
bool is_macos() {
#if defined __APPLE__
return true;
#else
return false;
#endif
}
bool is_x86() {
#if defined(__i386__) || defined(_M_IX86)
return true;
#else
return false;
#endif
}
bool is_amd64() {
#if defined(__amd64__) || defined(_M_X64)
return true;
#else
return false;
#endif
}
bool is_x86_64() {
return is_amd64();
}
bool is_arm() {
#if defined(__arm__) || defined(_M_ARM)
return true;
#else
return false;
#endif
}
bool is_aarch64() {
#if defined(__aarch64__) || defined(_M_ARM64)
return true;
#else
return false;
#endif
}
bool is_ia64() {
#if defined(__ia64__)
return true;
#else
return false;
#endif
}
bool is_powerpc() {
#if defined(__powerpc__)
return true;
#else
return false;
#endif
}
bool is_superh() {
#if defined(__sh__)
return true;
#else
return false;
#endif
}
f64 hex2f(const char* str) {
f64 ret=0;
for(; *str; ++str) {
if ('0'<=*str && *str<='9') {
ret=ret*16+(*str-'0');
} else if ('a'<=*str && *str<='f') {
ret=ret*16+(*str-'a'+10);
} else if ('A'<=*str && *str<='F') {
ret=ret*16+(*str-'A'+10);
} else {
return nan("");
}
}
return ret;
}
f64 oct2f(const char* str) {
f64 ret=0;
while('0'<=*str && *str<'8') {
ret=ret*8+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret;
}
// 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* str) {
f64 ret=0,negative=1,num_pow=0;
while('0'<=*str && *str<='9') {
ret=ret*10+(*str++-'0');
}
if (!*str) {
return ret;
}
if (*str=='.') {
if (!*++str) {
return nan("");
}
num_pow=0.1;
while('0'<=*str && *str<='9') {
ret+=num_pow*(*str++-'0');
num_pow*=0.1;
}
if (!*str) {
return ret;
}
}
if (*str!='e' && *str!='E') {
return nan("");
}
if (!*++str) {
return nan("");
}
if (*str=='-' || *str=='+') {
negative=(*str++=='-'? -1:1);
}
if (!*str) {
return nan("");
}
num_pow=0;
while('0'<=*str && *str<='9') {
num_pow=num_pow*10+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret*std::pow(10,negative*num_pow);
}
f64 str2num(const char* str) {
bool negative=false;
f64 res=0;
if (*str=='-' || *str=='+') {
negative=(*str++=='-');
}
if (!*str) {
return nan("");
}
if (str[0]=='0' && str[1]=='x') {
res=hex2f(str+2);
} else if (str[0]=='0' && str[1]=='o') {
res=oct2f(str+2);
} else {
res=dec2f(str);
}
return negative?-res:res;
}
i32 utf8_hdchk(const char head) {
// 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
return 1;
}
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
return 2;
}
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
return 3;
}
return 0;
}
std::string chrhex(const char c) {
const char hextbl[]="0123456789abcdef";
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
}
std::string rawstr(const std::string& str, const usize maxlen) {
std::string ret("");
for(auto i:str) {
// windows doesn't output unicode normally, so we output the hex
if (is_windows() && i<=0) {
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;
}

111
src/nasal_opcode.cpp Normal file
View File

@ -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;
}

123
src/nasal_opcode.h Normal file
View File

@ -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[];

1026
src/nasal_parse.cpp Normal file

File diff suppressed because it is too large Load Diff

161
src/nasal_parse.h Normal file
View File

@ -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();
};

412
src/nasal_vm.cpp Normal file
View File

@ -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, &&para, &&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

132
src/optimizer.cpp Normal file
View File

@ -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);
}

20
src/optimizer.h Normal file
View File

@ -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*);
};

30
src/symbol_finder.cpp Normal file
View File

@ -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;
}

19
src/symbol_finder.h Normal file
View File

@ -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*);
};

View File

@ -222,8 +222,7 @@ var isfunc=func(f){
}
var isghost=func(g){
die("this runtime has no ghost object");
return 0;
return typeof(g)=="obj";
}
var ishash=func(h){
@ -251,6 +250,9 @@ var isvec=func(v){
return typeof(v)=="vec";
}
var ghosttype=func(ghost_object) {
return __ghosttype(ghost_object);
}
# get the index of val in the vec
var vecindex=func(vec,val){

View File

@ -1,5 +1,5 @@
import.stl.padding;
import.stl.process_bar;
import.std.padding;
import.std.process_bar;
var char_ttf=[
[" "," "," "," "," "," "],

View File

@ -1,5 +1,5 @@
# Road check and auto pilot by ValKmjolnir
import.stl.fg_env;
import.std.fg_env;
var dt=0.01;
var intergral=0;

View File

@ -1,4 +1,4 @@
import.stl.queue;
import.std.queue;
rand(time(0));
var pixel=[' ','#','.','*'];

View File

@ -1,4 +1,4 @@
import.stl.mat;
import.std.mat;
rand(time(0));

View File

@ -1,11 +1,11 @@
import.stl.padding;
import.stl.file;
import.stl.sort;
import.std.padding;
import.std.file;
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});
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});
var testfile=find_all_files_with_extension("./test","nas");
@ -78,8 +78,8 @@ var calc=func(codetype,files,path=""){
return int(bytes/1024);
}
var all=calc("source code:",source)
+calc("lib:",lib,"stl/")
var all=calc("source code:",source,"src/")
+calc("lib:",lib,"std/")
+calc("test file:",testfile,"test/")
+calc("module:",module,"module/");
println(rightpad("total:",padding_length),'|',leftpad(str(all),6),' kb |');

View File

@ -1,7 +1,7 @@
# coroutine.nas by ValKmjolnir
# 2022/5/19
import.stl.process_bar;
import.stl.padding;
import.std.process_bar;
import.std.padding;
if(os.platform()=="windows"){
system("chcp 65001");

View File

@ -1,6 +1,6 @@
import.stl.sort;
import.stl.padding;
import.stl.process_bar;
import.std.sort;
import.std.padding;
import.std.process_bar;
var mess=func(vec) {
srand();

View File

@ -5,9 +5,10 @@ var http=func(){
return {
establish:func(ip,port){
sd=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP);
if(socket.bind(sd,ip,port)<0){
println("failed to bind socket "~sd~" at IP: "~ip~" port: "~port~".");
return;
while(socket.bind(sd,ip,port)<0){
println("[",os.time(),"] failed to bind socket "~sd~" at IP: "~ip~" port: "~port~".");
unix.sleep(5);
println("[",os.time(),"] retrying...");
}
socket.listen(sd,1);
println("[",os.time(),"] start server at [",ip,":",port,"]");

View File

@ -1,5 +1,5 @@
import.stl.json;
import.stl.process_bar;
import.std.json;
import.std.process_bar;
var ss=JSON.stringify({
vec:[0,1,2],

View File

@ -1,5 +1,5 @@
import.module.libsock;
import.stl.json;
import.std.json;
var gettime=func(){
return split(" ",os.time())[1];

View File

@ -172,7 +172,7 @@ var lexer=func(file)
};
}
var lex=lexer("stl/fg_env.nas");
var lex=lexer("std/fg_env.nas");
lex.compile();
foreach(var tok;lex.get_token())
print('(',tok.line,' | ',tok.token,')\n');

View File

@ -1,4 +1,4 @@
import.stl.process_bar;
import.std.process_bar;
var new_map=func(width,height){
var tmp=[];
@ -11,7 +11,7 @@ var new_map=func(width,height){
}
var prt=func(map){
var s='\ec\e[1;1H';
var s='\e[H';
foreach(var line;map){
foreach(var elem;line)
s~=elem~' ';

View File

@ -1,6 +1,6 @@
import.test.md5;
import.stl.process_bar;
import.stl.file;
import.std.process_bar;
import.std.file;
srand();
var compare=func() {
@ -48,15 +48,18 @@ var filechecksum=func(){
foreach(var p;find_all_files_with_extension("./test","nas")) {
append(files,"./test/"~p);
}
foreach(var p;find_all_files_with_extension("./stl","nas")) {
append(files,"./stl/"~p);
foreach(var p;find_all_files_with_extension("./std","nas")) {
append(files,"./std/"~p);
}
foreach(var p;find_all_files_with_extension("./module","nas","cpp")) {
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);
}
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")) {
append(files,"./doc/"~p);
}

View File

@ -1,4 +1,4 @@
import.stl.process_bar;
import.std.process_bar;
import.module.libkey;
var is_windows_platform=os.platform()=="windows";

View File

@ -1,4 +1,4 @@
import.stl.process_bar;
import.std.process_bar;
var ppm=func(filename,width,height,RGB){
# P3 use ASCII number

View File

@ -1,4 +1,4 @@
import.stl.sort;
import.std.sort;
var vec=[];
rand(time(0));

View File

@ -1,4 +1,4 @@
import.stl.sort;
import.std.sort;
var to_lower=func(s){
var tmp="";