From 38089606c12979816dcb53427914144c94978595 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Tue, 13 Apr 2021 13:47:13 +0100 Subject: [PATCH] Remove the goimpl and move it to a separate branch --- CMakeLists.txt | 2 +- Jenkinsfile | 23 - Makefile | 14 - bootstrap/.golangci.yaml | 101 --- bootstrap/Dockerfile | 13 - bootstrap/LICENSE | 339 --------- bootstrap/Makefile | 11 - bootstrap/README.org | 50 -- bootstrap/cmd/repl.go | 39 - bootstrap/cmd/root.go | 80 -- bootstrap/cmd/run.go | 38 - bootstrap/examples/ffi/foo.srn | 16 - bootstrap/examples/ffi/foo/Makefile | 5 - bootstrap/examples/ffi/foo/foo.c | 7 - bootstrap/examples/ffi/foo/foo.h | 6 - bootstrap/examples/ffi/foo/foo.o | Bin 1544 -> 0 bytes bootstrap/examples/ffi/foo/libfoo.so | Bin 15464 -> 0 bytes bootstrap/examples/hello-world.srn | 25 - bootstrap/go.mod | 12 - bootstrap/go.sum | 637 ---------------- bootstrap/pkg/ast/ast.go | 44 -- bootstrap/pkg/ast/location.go | 116 --- bootstrap/pkg/ast/source.go | 97 --- bootstrap/pkg/core/block.go | 94 --- bootstrap/pkg/core/bool.go | 66 -- bootstrap/pkg/core/builtins.go | 87 --- bootstrap/pkg/core/call_stack.go | 191 ----- bootstrap/pkg/core/coll.go | 37 - bootstrap/pkg/core/constants.go | 24 - bootstrap/pkg/core/core.go | 171 ----- bootstrap/pkg/core/errors.go | 186 ----- bootstrap/pkg/core/eval.go | 689 ------------------ bootstrap/pkg/core/function.go | 276 ------- bootstrap/pkg/core/instructions.go | 91 --- bootstrap/pkg/core/keyword.go | 220 ------ bootstrap/pkg/core/list.go | 152 ---- bootstrap/pkg/core/macro.go | 106 --- bootstrap/pkg/core/namespace.go | 264 ------- bootstrap/pkg/core/nil.go | 46 -- bootstrap/pkg/core/nothing.go | 55 -- bootstrap/pkg/core/numbers.go | 204 ------ bootstrap/pkg/core/parser.go | 589 --------------- bootstrap/pkg/core/printer.go | 195 ----- bootstrap/pkg/core/quasiquote.go | 173 ----- bootstrap/pkg/core/reader.go | 23 - bootstrap/pkg/core/runtime.go | 217 ------ bootstrap/pkg/core/scope.go | 107 --- bootstrap/pkg/core/sforms.go | 156 ---- bootstrap/pkg/core/string.go | 72 -- bootstrap/pkg/core/symbol.go | 96 --- bootstrap/pkg/core/types.go | 173 ----- bootstrap/pkg/dl/dl.go | 84 --- bootstrap/pkg/errors/errors.go | 71 -- bootstrap/pkg/hash/hash.go | 48 -- bootstrap/serene.go | 25 - examples/hello_world/hello_world.srn | 4 - .../site}/archetypes/default.md | 0 {site => resources/site}/config.toml | 0 .../site}/content/posts/my-first-post.md | 0 {site => resources/site}/scripts/deploy.sh | 0 .../site}/themes/anybodyhome/LICENSE.md | 0 .../site}/themes/anybodyhome/README.md | 0 .../themes/anybodyhome/archetypes/default.md | 0 .../site}/themes/anybodyhome/layouts/404.html | 0 .../layouts/_default/paginator.html | 0 .../anybodyhome/layouts/_default/single.html | 0 .../anybodyhome/layouts/_default/summary.html | 0 .../themes/anybodyhome/layouts/index.html | 0 .../anybodyhome/layouts/partials/footer.html | 0 .../anybodyhome/layouts/partials/head.html | 0 .../anybodyhome/layouts/partials/header.html | 0 .../layouts/partials/paginator.html | 0 .../themes/anybodyhome/layouts/summary.html | 0 .../themes/anybodyhome/static/css/styles.css | 0 .../site}/themes/anybodyhome/theme.toml | 0 {cmake => scripts/cmake}/cotire.cmake | 0 76 files changed, 1 insertion(+), 6666 deletions(-) delete mode 100644 Jenkinsfile delete mode 100644 Makefile delete mode 100644 bootstrap/.golangci.yaml delete mode 100644 bootstrap/Dockerfile delete mode 100755 bootstrap/LICENSE delete mode 100644 bootstrap/Makefile delete mode 100644 bootstrap/README.org delete mode 100644 bootstrap/cmd/repl.go delete mode 100644 bootstrap/cmd/root.go delete mode 100644 bootstrap/cmd/run.go delete mode 100644 bootstrap/examples/ffi/foo.srn delete mode 100644 bootstrap/examples/ffi/foo/Makefile delete mode 100644 bootstrap/examples/ffi/foo/foo.c delete mode 100644 bootstrap/examples/ffi/foo/foo.h delete mode 100644 bootstrap/examples/ffi/foo/foo.o delete mode 100755 bootstrap/examples/ffi/foo/libfoo.so delete mode 100644 bootstrap/examples/hello-world.srn delete mode 100644 bootstrap/go.mod delete mode 100644 bootstrap/go.sum delete mode 100644 bootstrap/pkg/ast/ast.go delete mode 100644 bootstrap/pkg/ast/location.go delete mode 100644 bootstrap/pkg/ast/source.go delete mode 100644 bootstrap/pkg/core/block.go delete mode 100644 bootstrap/pkg/core/bool.go delete mode 100644 bootstrap/pkg/core/builtins.go delete mode 100644 bootstrap/pkg/core/call_stack.go delete mode 100644 bootstrap/pkg/core/coll.go delete mode 100644 bootstrap/pkg/core/constants.go delete mode 100644 bootstrap/pkg/core/core.go delete mode 100644 bootstrap/pkg/core/errors.go delete mode 100644 bootstrap/pkg/core/eval.go delete mode 100644 bootstrap/pkg/core/function.go delete mode 100644 bootstrap/pkg/core/instructions.go delete mode 100644 bootstrap/pkg/core/keyword.go delete mode 100644 bootstrap/pkg/core/list.go delete mode 100644 bootstrap/pkg/core/macro.go delete mode 100644 bootstrap/pkg/core/namespace.go delete mode 100644 bootstrap/pkg/core/nil.go delete mode 100644 bootstrap/pkg/core/nothing.go delete mode 100644 bootstrap/pkg/core/numbers.go delete mode 100644 bootstrap/pkg/core/parser.go delete mode 100644 bootstrap/pkg/core/printer.go delete mode 100644 bootstrap/pkg/core/quasiquote.go delete mode 100644 bootstrap/pkg/core/reader.go delete mode 100644 bootstrap/pkg/core/runtime.go delete mode 100644 bootstrap/pkg/core/scope.go delete mode 100644 bootstrap/pkg/core/sforms.go delete mode 100644 bootstrap/pkg/core/string.go delete mode 100644 bootstrap/pkg/core/symbol.go delete mode 100644 bootstrap/pkg/core/types.go delete mode 100644 bootstrap/pkg/dl/dl.go delete mode 100644 bootstrap/pkg/errors/errors.go delete mode 100644 bootstrap/pkg/hash/hash.go delete mode 100644 bootstrap/serene.go delete mode 100644 examples/hello_world/hello_world.srn rename {site => resources/site}/archetypes/default.md (100%) rename {site => resources/site}/config.toml (100%) rename {site => resources/site}/content/posts/my-first-post.md (100%) rename {site => resources/site}/scripts/deploy.sh (100%) rename {site => resources/site}/themes/anybodyhome/LICENSE.md (100%) rename {site => resources/site}/themes/anybodyhome/README.md (100%) rename {site => resources/site}/themes/anybodyhome/archetypes/default.md (100%) rename {site => resources/site}/themes/anybodyhome/layouts/404.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/_default/paginator.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/_default/single.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/_default/summary.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/index.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/partials/footer.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/partials/head.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/partials/header.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/partials/paginator.html (100%) rename {site => resources/site}/themes/anybodyhome/layouts/summary.html (100%) rename {site => resources/site}/themes/anybodyhome/static/css/styles.css (100%) rename {site => resources/site}/themes/anybodyhome/theme.toml (100%) rename {cmake => scripts/cmake}/cotire.cmake (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c03e699..166e986 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") - set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/scripts/cmake") set(MemoryCheckCommand "valgrind") add_compile_options(-fno-rtti) diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 7f61bd4..0000000 --- a/Jenkinsfile +++ /dev/null @@ -1,23 +0,0 @@ -pipeline { - agent any - - stages { - stage('Build') { - steps { - sh("make build") - echo 'Building..' - sh("make clean") - } - } - stage('Test') { - steps { - echo 'Testing..' - } - } - stage('Deploy') { - steps { - echo 'Deploying....' - } - } - } -} \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index e11672b..0000000 --- a/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -ROOT_DIR=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) - -include $(ROOT_DIR)/bootstrap/Makefile - -.PHONY: lint -lint: lint-bootstrap - -.PHONY: compile -compile: compile-bootstrap - -.PHONY: build -build: compile - -clean: clean-bootstrap diff --git a/bootstrap/.golangci.yaml b/bootstrap/.golangci.yaml deleted file mode 100644 index e4610bf..0000000 --- a/bootstrap/.golangci.yaml +++ /dev/null @@ -1,101 +0,0 @@ -linters-settings: - dupl: - threshold: 100 - funlen: - lines: 100 - statements: 50 - gci: - local-prefixes: github.com/udemy/schema-vault - goconst: - min-len: 2 - min-occurrences: 2 - gocritic: - enabled-tags: - - diagnostic - - experimental - - opinionated - - performance - - style - disabled-checks: - - dupImport - - ifElseChain - - octalLiteral - - whyNoLint - - wrapperFunc - settings: - rangeValCopy: - sizeThreshold: 512 - gocyclo: - min-complexity: 15 - goimports: - local-prefixes: github.com/udemy/schema-vault - golint: - min-confidence: 0 - gomnd: - settings: - mnd: - checks: argument,return - govet: - check-shadowing: true - maligned: - suggest-new: true - misspell: - locale: US - nolintlint: - allow-leading-space: true - allow-unused: false - require-explanation: false - require-specific: false -linters: - disable-all: true - enable: - - bodyclose - - deadcode - - depguard - - dogsled - - dupl - - errcheck - - funlen - - gochecknoinits - - goconst - - gocritic - - gocyclo - - goimports - - gofmt - - golint - - gomnd - - goprintffuncname - - gosec - - gosimple - - govet - - ineffassign - - interfacer - - misspell - - nakedret - - noctx - - nolintlint - - rowserrcheck - - scopelint - - staticcheck - - structcheck - - stylecheck - - typecheck - - unconvert - - unparam - - unused - - varcheck - - whitespace -issues: - exclude-rules: - - path: _test\.go - linters: - - gomnd - - linters: - - gocritic - text: "unnecessaryDefer:" -run: - timeout: 1m - issues-exit-code: 1 - tests: true - skip-dirs-use-default: true - allow-parallel-runners: true diff --git a/bootstrap/Dockerfile b/bootstrap/Dockerfile deleted file mode 100644 index 2db5a60..0000000 --- a/bootstrap/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM golang:1-alpine -LABEL maintainer="Sameer Rahmani " - -RUN mkdir -p /usr/src/serene -WORKDIR /usr/src/serene - -COPY go.mod go.sum ./ -RUN go mod download -x -COPY . . -# Build the Go app -RUN go build -v ./serene.go - -CMD ["./serene"] diff --git a/bootstrap/LICENSE b/bootstrap/LICENSE deleted file mode 100755 index d159169..0000000 --- a/bootstrap/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - 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. - - Preamble - - 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. - - 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. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 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. - - , 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. diff --git a/bootstrap/Makefile b/bootstrap/Makefile deleted file mode 100644 index 0258408..0000000 --- a/bootstrap/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -BOOTSTRAP_TAG ?= latest -BOOTSTRAP_DIR = $(ROOT_DIR)/bootstrap - -compile-bootstrap: - docker build -t serene-lang/serene-bootstrap:$(BOOTSTRAP_TAG) $(BOOTSTRAP_DIR) - -clean-bootstrap: - docker rmi serene-lang/serene-bootstrap:$(BOOTSTRAP_TAG) - -lint-bootstrap: - docker run --rm -v $(BOOTSTRAP_DIR):/app -w /app golangci/golangci-lint:v1.35.2 golangci-lint run pkg/core diff --git a/bootstrap/README.org b/bootstrap/README.org deleted file mode 100644 index 8a2b2ca..0000000 --- a/bootstrap/README.org +++ /dev/null @@ -1,50 +0,0 @@ -#+OPTIONS: num:t toc:nil -* Serene lang (Bootstrap interpreter) -The bootstrap version of Serene is used to bootstrap the compiler which is written in Serene -itself. It's an interpreter with minimal set of features and rough edges. - -#+TOC: headlines 2 - -** Heads up -Since the interpreter is there to bootstrap the compiler the goal is to keep it simple and good enough -too run the compiler. So we don't want to over engineer things because the interpreter will go away -evantually. Here is the list of things that we implemented ( or will implement ) differently: - -- *Normal Macros* (Syntax macro): Since bootstrap version is an interpreter we don't have clear -distinction between compile time and runtime (We have only runtime) - unless we build bytecode support -into the interpreter to be able to cache the compiled ast - So we can't really implemeent normal macros -as they meant to be implemented. - -- List implementation: We don't follow the actual list implementation as a single or doubly linked -list. So far we kept it really stupid by creating a wrapper around Go slices. But we need to fix this -one in the future. - -- Since we didn't write the reader in Serene itself, there is no reader macroes. - -** New contributors -If you're a new contributor and you want to start working on the source code and don't know where to start, -just look through the source code for *TODO:* sections and the ~dev.org~ on the root for some higher level -TODOs. - -In order to start reading the code start from the ~pkg/core/parser.go~ file which is the parser implementation -and then take a look at ~pkg/core/core.go~ and ~pkg/core/eval.go~. If you have any question feel free to ask them -either on the mailing list or the gitter/IRC channel. - -** Development hint - -*** Use Make... functions -In order to create a new value in any type use the designated Make function, for example: =MakeList= - - -** Setup development environment -*** Emacs -All you have to do is to install =LSP= and =gopls= that's it. - -** Running the REPL -In order to run the REPL by compiling the code just do: - -#+BEGIN_SRC bash -go run serene.go repl -#+END_SRC - -There is a =--debug= parameter which enables more debug output (For example: read -> AST -> out data). diff --git a/bootstrap/cmd/repl.go b/bootstrap/cmd/repl.go deleted file mode 100644 index b81690b..0000000 --- a/bootstrap/cmd/repl.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package cmd - -import ( - "github.com/spf13/cobra" - "serene-lang.org/bootstrap/pkg/core" -) - -// replCmd represents the base command when called without any subcommands -var replCmd = &cobra.Command{ - Use: "repl", - Short: "Runs the local Serene's REPL", - Long: `Runs the local Serene's REPL to interact with Serene`, - Run: func(cmd *cobra.Command, args []string) { - // TODO: Get the debug value from a CLI flag - core.REPL(makeFlags()) - }, -} - -func init() { // nolint:gochecknoinits - rootCmd.AddCommand(replCmd) -} diff --git a/bootstrap/cmd/root.go b/bootstrap/cmd/root.go deleted file mode 100644 index b4ad35a..0000000 --- a/bootstrap/cmd/root.go +++ /dev/null @@ -1,80 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -// Package cmd provides the sub commands for the Serene binary. -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" -) - -var debugMode bool -var stackDebugMode bool - -func makeFlags() map[string]bool { - return map[string]bool{ - "debugMode": debugMode, - "stackDebugMode": stackDebugMode, - } -} - -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "Serene", - Short: "The bootstrap version", - Long: `Serene's bootstrap interpreter V0.1.0 - -Serene's bootstrap interpreter is used to -bootstrap the Serene's compiler.' - -It comes with ABSOLUTELY NO WARRANTY; -This is free software, and you are welcome -to redistribute it under certain conditions; -for details take a look at the LICENSE file. -`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Fix me!!!! I don't do anything !!!") - }, -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -func init() { // nolint:gochecknoinits - cobra.OnInitialize() - rootCmd.PersistentFlags().BoolVar( - &debugMode, - "debug", - false, - "Turns on the debug mode.") - - rootCmd.PersistentFlags().BoolVar( - &stackDebugMode, - "debug-stack", - false, - "Turns on the call stack debug mode.") -} diff --git a/bootstrap/cmd/run.go b/bootstrap/cmd/run.go deleted file mode 100644 index d2430d9..0000000 --- a/bootstrap/cmd/run.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package cmd - -import ( - "github.com/spf13/cobra" - "serene-lang.org/bootstrap/pkg/core" -) - -// replCmd represents the base command when called without any subcommands -var runCmd = &cobra.Command{ - Use: "run NS", - Short: "Evaluates the given NS and runs the main function of it", - Long: `Evaluates the given NS and runs the main function`, - Run: func(cmd *cobra.Command, args []string) { - core.Run(makeFlags(), args) - }, -} - -func init() { // nolint:gochecknoinits - rootCmd.AddCommand(runCmd) -} diff --git a/bootstrap/examples/ffi/foo.srn b/bootstrap/examples/ffi/foo.srn deleted file mode 100644 index ca1d4fc..0000000 --- a/bootstrap/examples/ffi/foo.srn +++ /dev/null @@ -1,16 +0,0 @@ -(ns examples.ffi.foo - :require '[adssa - [serene.ffi.libllvm]]) - -(defmacro deffi.....) - -(ffi/deffi some-function - :fn-name "some_function" - :args {:x :i32 - :y :char_ptr} - - :return :void) - - - -(some-function 12 "asdasd") diff --git a/bootstrap/examples/ffi/foo/Makefile b/bootstrap/examples/ffi/foo/Makefile deleted file mode 100644 index 7a4e56f..0000000 --- a/bootstrap/examples/ffi/foo/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -foo: - gcc -c -Wall -Werror -fpic foo.c - gcc -shared -o libfoo.so foo.o - -all: foo diff --git a/bootstrap/examples/ffi/foo/foo.c b/bootstrap/examples/ffi/foo/foo.c deleted file mode 100644 index fdb4e9d..0000000 --- a/bootstrap/examples/ffi/foo/foo.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - - -void bar(void) -{ - puts("Hello, I am a shared library"); -} diff --git a/bootstrap/examples/ffi/foo/foo.h b/bootstrap/examples/ffi/foo/foo.h deleted file mode 100644 index a50f948..0000000 --- a/bootstrap/examples/ffi/foo/foo.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef foo_h__ -#define foo_h__ - -extern void baz(void); - -#endif // foo_h__ diff --git a/bootstrap/examples/ffi/foo/foo.o b/bootstrap/examples/ffi/foo/foo.o deleted file mode 100644 index 327ce0edcf0a8b3f1740ff512d0e8620247734b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1544 zcmb_bOKTKC5U$yYNsJp=6AytXbS|QhGzqzwfU*zvAuI{xfhQSzHxpLe$t*oBBwlh0 zco5>v|Kr7@e?su6cOh2obdk0*dlA2-r>efLue$oN=gn@b6a>Hs;5l4J%mREZUHjFs zTZMTj!(r#c_s+*>#QdPo<*P59csNW~#S5Vlp+q)NIzAG^H$AQN89=+y*cOl5@rg-O zu_ZU<`kLMlqs=FIKs5;Wa|6NqJqS)K!E$+K_7WR=fIJPtB7JIiD;K54{PYecP&@&& zrz!Y#RWuzI?ie7%UlBsARD=+2713B_W&&T_aR}2l@~Ogt)8}~a$lbwFxseU9GJ%KV zG?jhmDGgD(yIZeyquo|(zj+WH)au=41fzE*LoIhq*G{|oeU+n(K7mev)@ELyPoR9~ zsQ}3AEHSEw*67?0*wgV)NfVzMkVncWki9Gesna~@r%8gZ1msC-V%gp~T+58=zr_TS z#e?Wrt0W#T`X4?5YtS37la6h5!7g+fbP{%8ee(1i)fz0I5Np6{G< zf6mO@JJY%MgYLebs#J=Q(zJ_2I?$wj|^Gzkd3&|#5EoH?oRSjHPh1V;Vlv98I{22HyvvAIgi${_qi6hCGBl<@;z zHSGk}>SE}?-C$VSsgx$-xW?E4#a7Zm_LKUaG$DNbroKn@Z^pC}XhcH)M3#C@JF3x! zZ;zd;dt6(jewAQwgt;XNywsx-`1OzAdzfFHV1J0YB~(W>i?wnoY&`Gycz(hw27V#% zJo5Go?DdATh3xUsVvsEi?Cl)OPh5{0 zzXbRiFYN4K;oebh;k_ImZq{sPIdv5E0`I@a%0ukK4Vh_Rj~13M9lajLr6Lm2 zYMX2XYy@lsYy@lsYy@lsYy@lsYy@lsYy@lsp3Vrk*|D+wdbLOSk1G%Zn`{JZ1Z)Is1Z)Is1Z)Is1Z)Is1Z)Is1pZqD%sN!8JH?t(S<5P^UPF0d z>mnmJ8^7m_jPTM%qU2?0D7ly_}fk7-* zZZ>jx?Q2F8#*LuId#kK(mGrCzQq?*G{*);Q?YwO4cu{YdP~O9mZvSCp&tH?n{veM0 z8XL-8M*p{w%k4Df{a=7}?D6`x#?~G8c6Pp|T6?n-K|ZgxJ8jOU4TV?KiMDlGh`U$F zX<3I9xf@_A>7WN*VLdERg_@8$f7Vc8AQb!1=r-aVbn_U=36b-P|q zfBW8U&+Y1GiZ@Pp-TS&^M@4Vn{*LxOZ+}nE8{GrmKzm1DH?v$`@YvlJvVkI#iSC<_ zZ52t|HJ)HgMrA?d6i<%_{vcye(3nH+_{5}>^NTrhhEGqhnZ}?%j%ZG13&qj=M1|n7 zu8s6!5<$T;_CB(l+29_%NZ;d$r;LzkMnq&bQ1qPC-d-MJl9mg6&Zu_e_BbPG0$n1 zXf0=CZiOD>HW2fn%wuvd!yohF2IfQue~jSZiNZ@ICijB z=4SAiFMvwBhi#0u-_UfBGxA6Pf6OaD^dI_o|KBwJ(hf;@z5pUl;?JYYocLv#lXl_x zr##M>@g_c^n#ULR(CHXM)F1O%8I2tP!x-?Ln19A7faov$Ap_<7M(fA^Lg4psG>CdZ z%Xk^}$2=^P8f!Cxt@1>*kEY - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -// Package ast provides the functionality and data structures around the -// Serene's AST. -package ast - -type NodeType int - -const ( - Nil NodeType = iota - Nothing - Bool - Instruction - Symbol - Keyword - Number - List - Fn - NativeFn - Namespace - String - Block // Dont' mistake it with block from other programming languages - -) - -type ITypable interface { - GetType() NodeType -} diff --git a/bootstrap/pkg/ast/location.go b/bootstrap/pkg/ast/location.go deleted file mode 100644 index 0c0f2c9..0000000 --- a/bootstrap/pkg/ast/location.go +++ /dev/null @@ -1,116 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package ast - -// ILocatable describes something that can be located in a source -type ILocatable interface { - GetLocation() *Location -} - -// Location is used to point to a specific location in the source -// code (before parse). It can point to a single point or a range. -type Location struct { - start int - end int - // Where this location is pointing too ? - source Source - - // Is it a known location or not ? For example builtins doesn't - // have a knowen location - knownLocation bool -} - -var UnknownLocation *Location = &Location{knownLocation: false} - -func (l *Location) GetStart() int { - return l.start -} - -func (l *Location) GetEnd() int { - return l.end -} - -// GetSource returns the source of the current location or the "builtin" -// source to indicate that the source of this location is not known in -// the context of source code -func (l *Location) GetSource() *Source { - if l.IsKnownLocaiton() { - return &l.source - } - return GetBuiltinSource() -} - -// IncStart increases the start pointer of the location by `x` with respect -// to the boundaries of the source -func (l *Location) IncStart(x int) { - if x+l.start < len(*l.source.Buffer) { - l.start += x - } else { - l.start = len(*l.source.Buffer) - 1 - } -} - -// DecStart decreases the start pointer of the location by `x` with respect -// to the boundaries of the source -func (l *Location) DecStart(x int) { - if l.start-x >= 0 { - l.start -= x - } else { - l.start = 0 - } -} - -// IncEnd increases the end pointer of the location by `x` with respect -// to the boundaries of the source -func (l *Location) IncEnd(x int) { - if x+l.end < len(*l.source.Buffer) { - l.end += x - } else { - l.end = len(*l.source.Buffer) - 1 - } -} - -// DecEnd decreases the end pointer of the location by `x` with respect -// to the boundaries of the source -func (l *Location) DecEnd(x int) { - if l.end-x >= 0 { - l.end -= x - } else { - l.end = 0 - } -} - -func (l *Location) IsKnownLocaiton() bool { - return l.knownLocation -} - -// MakeLocation return a pointer to a `Location` in the given source `input` -// specified by the `start` and `end` boundaries -func MakeLocation(input *Source, start, end int) *Location { - return &Location{ - source: *input, - start: start, - end: end, - knownLocation: true, - } -} - -func MakeUnknownLocation() *Location { - return UnknownLocation -} diff --git a/bootstrap/pkg/ast/source.go b/bootstrap/pkg/ast/source.go deleted file mode 100644 index cef2b98..0000000 --- a/bootstrap/pkg/ast/source.go +++ /dev/null @@ -1,97 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package ast - -// The Source data structure is used to track expression back to the source -// code. For example to find which source (file) they belongs to. - -import ( - "sort" - "strings" -) - -var builtinSource *Source -var OutOfRangeLine string = "----" - -type Source struct { - // A Pointer to the buffer where the parser used for parsing the source - Buffer *[]string - - // The namespace name which this source is describing - NS string - - // This array contains the boundaries of each line in the buffer. For example - // [24 50 106] means that the buffer contains 3 lines and the first line can - // be found from the index 0 to index 24 of the buffer and the second line is - // from index 25 till 50 and so on - LineIndex *[]int -} - -// GetSubstr returns the a pointer to the string from the buffer specified by the `start` and `end` -func (s *Source) GetSubstr(start, end int) *string { - if start < len(*s.Buffer) && start >= 0 && end < len(*s.Buffer) && end > 0 && start <= end { - result := strings.Join((*s.Buffer)[start:end], "") - return &result - } - - return nil -} - -// GetLine returns the line specified by the `linenum` from the buffer. It will return "----" if the -// given line number exceeds the boundaries of the buffer -func (s *Source) GetLine(linenum int) string { - lines := strings.Split(strings.Join(*s.Buffer, ""), "\n") - if linenum > 0 && linenum <= len(lines) { - return lines[linenum-1] - } - return OutOfRangeLine -} - -// LineNumberFor returns the line number associated with the given position `pos` in -// the buffer -func (s *Source) LineNumberFor(pos int) int { - if pos < 0 { - return -1 - } - - result := sort.SearchInts(*s.LineIndex, pos) - - // We've found something - if result > -1 { - // Since line numbers start from 1 unlike arrays :)) - result++ - } - - return result -} - -// GetBuiltinSource returns a pointer to a source that represents builtin -// expressions -func GetBuiltinSource() *Source { - if builtinSource == nil { - buf := strings.Split("builtin", "") - lineindex := []int{len(buf) - 1} - builtinSource = &Source{ - Buffer: &buf, - NS: "Serene.builtins", - LineIndex: &lineindex, - } - } - return builtinSource -} diff --git a/bootstrap/pkg/core/block.go b/bootstrap/pkg/core/block.go deleted file mode 100644 index e22fdbe..0000000 --- a/bootstrap/pkg/core/block.go +++ /dev/null @@ -1,94 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - "strings" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -// Block struct represents a group of forms. Don't confuse it with -// code blocks from other languages that specify a block using curly -// brackets and indentation. -// Blocks in serene are just a group of forms and nothing more. -type Block struct { - ExecutionScope - body []IExpr -} - -func (b *Block) GetType() ast.NodeType { - return ast.Block -} - -func (b *Block) String() string { - var strs []string - for _, e := range b.body { - strs = append(strs, e.String()) - } - return strings.Join(strs, " ") -} - -func (b *Block) ToDebugStr() string { - return fmt.Sprintf("%#v", b) -} - -func (b *Block) GetLocation() *ast.Location { - if len(b.body) > 0 { - return b.body[0].GetLocation() - } - return ast.MakeUnknownLocation() -} - -func (b *Block) Hash() uint32 { - bytes := []byte("TODO") - return hash.Of(append([]byte{byte(ast.Block)}, bytes...)) -} - -func (b *Block) ToSlice() []IExpr { - return b.body -} - -func (b *Block) SetContent(body []IExpr) { - b.body = body -} - -// Append the given expr `form` to the block -func (b *Block) Append(form IExpr) { - b.body = append(b.body, form) -} - -func (b *Block) Count() int { - return len(b.body) -} - -// MakeEmptyBlock creates an empty block -func MakeEmptyBlock() *Block { - return &Block{} -} - -// MakeBlock creates a block that holds the given array of -// forms `body`. -func MakeBlock(body []IExpr) *Block { - return &Block{ - body: body, - } -} diff --git a/bootstrap/pkg/core/bool.go b/bootstrap/pkg/core/bool.go deleted file mode 100644 index 31dc683..0000000 --- a/bootstrap/pkg/core/bool.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type Bool struct { - Node - ExecutionScope - value bool -} - -func (t *Bool) GetType() ast.NodeType { - return ast.Bool -} - -func (t *Bool) String() string { - if t.value { - return TRUEFORM - } - return FALSEFORM -} - -func (t *Bool) ToDebugStr() string { - return t.String() -} - -func (t *Bool) Hash() uint32 { - bytes := []byte(t.String()) - return hash.Of(append([]byte{byte(ast.Bool)}, bytes...)) -} - -func (t *Bool) IsTrue() bool { - return t.value -} - -func (t *Bool) IsFalse() bool { - return !t.value -} - -func MakeTrue(n Node) *Bool { - return &Bool{Node: n, value: true} -} - -func MakeFalse(n Node) *Bool { - return &Bool{Node: n, value: false} -} diff --git a/bootstrap/pkg/core/builtins.go b/bootstrap/pkg/core/builtins.go deleted file mode 100644 index b166535..0000000 --- a/bootstrap/pkg/core/builtins.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// BUILTINS is used in the Runtime to support builtin functions of -// the language which are implemented in Go -var BUILTINS = map[string]NativeFunction{ - "pr": MakeNativeFn("pr", PrNativeFn), - "prn": MakeNativeFn("prn", PrnNativeFn), - "print": MakeNativeFn("print", PrintNativeFn), - "println": MakeNativeFn("println", PrintlnNativeFn), - "require": MakeNativeFn("require", RequireNativeFn), - "hash": MakeNativeFn("hash", HashNativeFn), -} - -func PrNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - Pr(rt, toRepresentables(args.Rest().(IColl))...) - return MakeNil(n), nil -} - -func PrnNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - Prn(rt, toRepresentables(args.Rest().(IColl))...) - return MakeNil(n), nil -} - -func PrintNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - Print(rt, toRepresentables(args.Rest().(IColl))...) - return MakeNil(n), nil -} - -func PrintlnNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - Println(rt, toRepresentables(args.Rest().(IColl))...) - return MakeNil(n), nil -} - -func RequireNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - switch args.Count() { - case 0: - return nil, MakeError(rt, args, "'require' function is missing") - case 1: - return nil, MakeError(rt, args.First(), "'require' function needs at least one argument") - default: - } - - var result IExpr - var err IError - for _, ns := range args.Rest().(*List).ToSlice() { - result, err = RequireNamespace(rt, ns) - if err != nil { - return nil, err - } - } - - return result, nil -} - -func HashNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - if args.Count() != 2 { - return nil, MakeError(rt, args.First(), "'hash' function needs exactly one argument") - } - - expr := args.Rest().First() - result, err := MakeInteger(expr.Hash()) - - if err != nil { - return nil, err - } - - result.Node = n - return result, nil -} diff --git a/bootstrap/pkg/core/call_stack.go b/bootstrap/pkg/core/call_stack.go deleted file mode 100644 index 44c5280..0000000 --- a/bootstrap/pkg/core/call_stack.go +++ /dev/null @@ -1,191 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// CallStack implementation: -// * A callstack should be FIFA stack -// * It should keep track of function calls. -// * Anything that implements `IFn` can be tracked by the call stack -// * Since Serene uses eval loop to eliminate tail calls we need -// the call stack to be able to track recursive calls. For -// now by just counting number of calls to a functions that is already -// in the stack. -// -// TODOs: -// * At the moment if we call the same function twice (not as a recursive) -// function call stack will record it as a recursive call. We need -// compare the stack items by their address, identity and location. -// * Add support for iteration on the stack. - -import ( - "fmt" -) - -type ICallStack interface { - // Push the given callable `f` to the stack - Push(f IFn) IError - Pop() *Frame - Peek() *Frame - Count() uint -} - -type Frame struct { - // Number of recursive calls to this function - Count uint - - // Function to call - Callee IFn - - // Where is the call happening - Caller IExpr -} - -type TraceBack = []*Frame - -type CallStackItem struct { - prev *CallStackItem - data Frame -} - -type CallStack struct { - debug bool - head *CallStackItem - count uint -} - -func (f *Frame) String() string { - return fmt.Sprintf("", f.Callee, f.Count, f.Caller) -} - -func (c *CallStack) Count() uint { - return c.count -} - -func (c *CallStack) GetCurrentFn() IFn { - if c.head == nil { - return nil - } - - return c.head.data.Callee -} - -func (c *CallStack) Push(caller IExpr, f IFn) IError { - if c.debug { - fmt.Println("[Stack] -->", f) - } - - if f == nil { - return MakePlainError("Can't push 'nil' pointer to the call stack.") - } - - if caller == nil { - return MakePlainError("Can't push 'nil' pointer to the call stack for the caller.") - } - - // Empty Stack - if c.head == nil { - c.head = &CallStackItem{ - data: Frame{ - Callee: f, - Caller: caller, - Count: 0, - }, - } - c.count++ - } - - nodeData := &c.head.data - - // If the same function was on top of the stack - if nodeData.Callee == f && caller == nodeData.Caller { - // TODO: expand the check here to support address and location as well - nodeData.Count++ - } else { - c.head = &CallStackItem{ - prev: c.head, - data: Frame{ - Callee: f, - Caller: caller, - Count: 0, - }, - } - c.count++ - } - return nil -} - -func (c *CallStack) Pop() *Frame { - if c.head == nil { - if c.debug { - fmt.Println("[Stack] <-- nil") - } - return nil - } - - result := c.head - c.head = result.prev - c.count-- - if c.debug { - fmt.Printf("[Stack] <-- %s\n", result.data.Callee) - } - return &result.data -} - -func (c *CallStack) Peek() *Frame { - if c.head == nil { - if c.debug { - fmt.Println("[Stack] <-- nil") - } - return nil - } - - result := c.head - return &result.data -} - -func (c *CallStack) ToTraceBack() *TraceBack { - var tr TraceBack - item := c.head - for { - if item == nil { - break - } - // TODO: This doesn't seem efficient. Fix it. - tr = append([]*Frame{&item.data}, tr...) - item = item.prev - } - - return &tr -} - -func MakeCallStack(debugMode bool) CallStack { - return CallStack{ - count: 0, - head: nil, - debug: debugMode, - } -} - -func MakeFrame(rt *Runtime, caller IExpr, f IFn, count uint) *Frame { - return &Frame{ - Count: count, - Caller: caller, - Callee: f, - } -} diff --git a/bootstrap/pkg/core/coll.go b/bootstrap/pkg/core/coll.go deleted file mode 100644 index 04c6027..0000000 --- a/bootstrap/pkg/core/coll.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// ISeq is an interface describing a sequence of forms -type ISeq interface { - First() IExpr - Rest() ISeq -} - -type ICountable interface { - Count() int -} - -// IColl describes a collection of values. A finite collection. -type IColl interface { - ISeq - ICountable - ToSlice() []IExpr - Cons(e IExpr) IExpr -} diff --git a/bootstrap/pkg/core/constants.go b/bootstrap/pkg/core/constants.go deleted file mode 100644 index a15f5b5..0000000 --- a/bootstrap/pkg/core/constants.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -const NSFORM string = "ns" -const NILFORM string = "nil" -const FALSEFORM string = "false" -const TRUEFORM string = "true" diff --git a/bootstrap/pkg/core/core.go b/bootstrap/pkg/core/core.go deleted file mode 100644 index fb56103..0000000 --- a/bootstrap/pkg/core/core.go +++ /dev/null @@ -1,171 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -// Package core contains the high level internal function of Serene -package core - -import ( - "bytes" - "fmt" - "html/template" - "os" - "path/filepath" - - "github.com/chzyer/readline" -) - -type mainRunner struct { - NS string - Args string -} - -func rep(rt *Runtime, line string) { - ast, err := ReadString("serene.internal", line) - - if err != nil { - PrintError(rt, err) - return - } - - // Debug data, ugly right ? :)) - if rt.IsDebugMode() { - fmt.Printf("[DEBUG] Parsed AST: %s\n", ast.String()) - } - - result, e := Eval(rt, ast) - if e != nil { - PrintError(rt, e) - return - } - Prn(rt, result) -} - -/** TODO: -Replace the readline implementation with go-prompt. -*/ - -// REPL executes a Read Eval Print Loop locally reading from stdin and -// writing to stdout -func REPL(flags map[string]bool) { - cwd, err := os.Getwd() - if err != nil { - panic(err) - } - - rt := MakeRuntime([]string{cwd}, flags) - - rt.CreateNS("user", "REPL", true) - - rl, err := readline.NewEx(&readline.Config{ - Prompt: "> ", - HistoryFile: filepath.Join(os.Getenv("HOME"), ".serene.history"), - InterruptPrompt: "^C", - EOFPrompt: "exit", - HistorySearchFold: true, - }) - - if err != nil { - panic(err) - } - - rl.HistoryEnable() - defer rl.Close() - - fmt.Print(` - _______ _______ ______ _______ _______ _______ -| __| ___| __ \ ___| | | ___| -|__ | ___| < ___| | ___| -|_______|_______|___|__|_______|__|____|_______| - - -Serene's bootstrap interpreter is used to -bootstrap the Serene's compiler. - -It comes with ABSOLUTELY NO WARRANTY; -This is free software, and you are welcome -to redistribute it under certain conditions; -for details take a look at the LICENSE file.\n\n -`) - for { - rl.SetPrompt(fmt.Sprintf("%s> ", rt.CurrentNS().GetName())) - line, err := rl.Readline() - if err != nil { // io.EOF - break - } - rep(rt, line) - } -} - -func Run(flags map[string]bool, args []string) { - cwd, e := os.Getwd() - if e != nil { - panic(e) - } - - rt := MakeRuntime([]string{cwd}, flags) - rt.CreateNS("serene.internal", "RUN", true) - - if len(args) == 0 { - PrintError(rt, MakePlainError("'run' command needs at least one argument")) - os.Exit(1) - } - - var buf bytes.Buffer - arguments := "" - ns := args[0] - - if len(args) > 1 { - for _, arg := range args[1:] { - arguments += "\"" + arg + "\"" - } - } - - tmpl, e := template.New("run").Parse( - `(require '({{.NS}} n)) -(n/main {{.Args}})`, - ) - - if e != nil { - panic(e) - } - - e = tmpl.Execute(&buf, &mainRunner{ns, arguments}) - - if e != nil { - panic(e) - } - - if rt.IsDebugMode() { - fmt.Println("[DEBUG] Evaluating the following form to run the 'main' fn:") - fmt.Println(buf.String()) - } - - ast, err := ReadString("serene.internal", buf.String()) - - if err != nil { - PrintError(rt, err) - os.Exit(1) - } - - _, err = Eval(rt, ast) - - if err != nil { - PrintError(rt, err) - return - } -} diff --git a/bootstrap/pkg/core/errors.go b/bootstrap/pkg/core/errors.go deleted file mode 100644 index e0574b7..0000000 --- a/bootstrap/pkg/core/errors.go +++ /dev/null @@ -1,186 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// Error implementations: -// * `IError` is the main interface to represent errors. -// * `Error` struct is an expression itself. -// * `IError` and any implementation of it has to implement `ILocatable` -// so we can point to the exact location of the error. -// * We have to use `IError` everywhere and avoid using Golangs errors -// since IError is an expression itself. -// -// TODOs: -// * Make errors stackable, so different pieces of code can stack related -// errors on top of each other so user can track them through the code -// * Errors should contain a help message as well to give some hints to the -// user about how to fix the problem. Something similar to Rust's error -// messages -// * Integrate the call stack with IError - -import ( - "fmt" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/errors" -) - -type ErrType uint8 - -const ( - SyntaxError ErrType = iota - SemanticError - RuntimeError -) - -func (e ErrType) String() string { - return [...]string{"Syntax Error", "Semantic Error", "Runtime Error"}[e] -} - -// IError defines the necessary functionality of the internal errors. -type IError interface { - // In order to point to a specific point in the input - ast.ILocatable - - // We want errors to be printable by the `print` family - IRepresentable - IDebuggable - - GetErrType() ErrType - GetErrno() errors.Errno - GetDescription() *string - GetStackTrace() *TraceBack - // To wrap Golan rrrrors - WithError(err error) IError - - // Some errors might doesn't have any node available to them - // at the creation time. SetNode allows us to the the appropriate - // node later in time. - SetNode(n *Node) -} - -type Error struct { - Node - errtype ErrType - errno errors.Errno - WrappedErr error - msg string - trace *TraceBack -} - -func (e *Error) String() string { - return e.msg -} - -func (e *Error) ToDebugStr() string { - _, isInternalErr := e.WrappedErr.(*Error) - if isInternalErr { - return fmt.Sprintf("%s:\n\t%s", e.msg, e.WrappedErr.(*Error).ToDebugStr()) - } - return fmt.Sprintf("%s:\n\t%s", e.msg, e.WrappedErr.Error()) -} - -func (e *Error) GetErrType() ErrType { - return e.errtype -} - -func (e *Error) WithError(err error) IError { - e.WrappedErr = err - return e -} - -func (e *Error) SetNode(n *Node) { - e.Node = *n -} - -func (e *Error) Error() string { - return e.msg -} - -func (e *Error) GetStackTrace() *TraceBack { - return e.trace -} - -func (e *Error) GetErrno() errors.Errno { - return e.errno -} - -func (e *Error) GetDescription() *string { - desc, ok := errors.ErrorsDescription[e.errno] - if ok { - return &desc - } - - desc = errors.ErrorsDescription[0] - return &desc -} - -func MakePlainError(msg string) IError { - return &Error{ - msg: msg, - } -} - -// MakeError creates an Error which points to the given IExpr `e` as -// the root of the error. -func MakeError(rt *Runtime, e IExpr, msg string) IError { - rt.Stack.Push(e, rt.Stack.GetCurrentFn()) - - return &Error{ - Node: MakeNodeFromExpr(e), - errtype: RuntimeError, - msg: msg, - trace: rt.Stack.ToTraceBack(), - } -} - -func MakeRuntimeError(rt *Runtime, e IExpr, errno errors.Errno, msg string) IError { - rt.Stack.Push(e, rt.Stack.GetCurrentFn()) - - return &Error{ - Node: MakeNodeFromExpr(e), - errtype: RuntimeError, - msg: msg, - errno: errno, - trace: rt.Stack.ToTraceBack(), - } -} - -func MakeSyntaxErrorf(n Node, msg string, a ...interface{}) IError { - return &Error{ - Node: n, - errtype: SyntaxError, - msg: fmt.Sprintf(msg, a...), - } -} - -func MakeSemanticError(rt *Runtime, e IExpr, errno errors.Errno, msg string) IError { - rt.Stack.Push(e, rt.Stack.GetCurrentFn()) - frames := &[]*Frame{ - rt.Stack.Pop(), - } - - return &Error{ - Node: MakeNodeFromExpr(e), - errtype: SemanticError, - errno: errno, - msg: msg, - trace: frames, - } -} diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go deleted file mode 100644 index 32859d1..0000000 --- a/bootstrap/pkg/core/eval.go +++ /dev/null @@ -1,689 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/errors" -) - -func restOfExprs(es []IExpr, i int) []IExpr { - if len(es)-1 > i { - return es[i+1:] - } - return []IExpr{} -} - -// evalForm evaluates the given expression `form` by a slightly different -// evaluation rules. For example if `form` is a list instead of the formal -// evaluation of a list it will evaluate all the elements and return the -// evaluated list -func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) { //nolint:gocyclo - switch form.GetType() { - case ast.Nil: - return form, nil - - case ast.Number: - return form, nil - - case ast.Fn: - return form, nil - - case ast.String: - return form, nil - - // Keyword evaluation rules: - // * Keywords evaluates to themselves with respect to a - // possible namespace alias. For example `::core/xyz` - // will evaluates to `:serene.core/xyz` only if the ns - // `serene.core` is loaded in the current ns with the - // `core` alias. Also `::xyz` will evaluete to - // `:/xyz` - case ast.Keyword: - // Eval initialize the keyword and MUTATES the state of the keyword - // and returns the updated keyword which would be the same - return form.(*Keyword).Eval(rt, scope) - - // Symbol evaluation rules: - // * If it's a NS qualified symbol (NSQS), Look it up in the external symbol table of - // the current namespace. - // * If it's not a NSQS Look up the name in the current scope. - // * Otherwise throw an error - case ast.Symbol: - var nsName string - sym := form.(*Symbol) - symbolName := sym.GetName() - - switch symbolName { - case TRUEFORM: - return MakeTrue(MakeNodeFromExpr(form)), nil - case FALSEFORM: - return MakeFalse(MakeNodeFromExpr(form)), nil - case NILFORM: - return MakeNil(MakeNodeFromExpr(form)), nil - default: - var expr *Binding - ns := scope.GetNS(rt) - if sym.IsNSQualified() { - // Whether a namespace with the given alias loaded or not - if !ns.hasExternal(sym.GetNSPart()) { - return nil, MakeError(rt, sym, - fmt.Sprintf("Namespace '%s' is no loaded", sym.GetNSPart()), - ) - } - - expr = ns.LookupGlobal(rt, sym) - nsName = sym.GetNSPart() - } else { - expr = scope.Lookup(rt, symbolName) - nsName = ns.GetName() - } - - if expr == nil { - return nil, MakeRuntimeError( - rt, - sym, - errors.E0003, - fmt.Sprintf( - "can't resolve symbol '%s' in ns '%s'", - symbolName, - nsName, - ), - ) - } - - return expr.Value, nil - } - - // Evaluate all the elements in the list instead of following the lisp convention - case ast.List: - var result []IExpr - - lst := form.(*List) - - for { - if lst.Count() > 0 { - expr, err := EvalForms(rt, scope, lst.First()) - if err != nil { - return nil, err - } - result = append(result, expr) - lst = lst.Rest().(*List) - } else { - break - } - } - - return MakeList(MakeNodeFromExpr(lst), result), nil - } - - // Default case - return nil, MakeError(rt, form, fmt.Sprintf("support for '%d' is not implemented", form.GetType())) -} - -// EvalForms evaluates the given expr `expressions` (it can be a list, block, symbol or anything else) -// with the given runtime `rt` and the scope `scope`. -func EvalForms(rt *Runtime, scope IScope, expressions IExpr) (IExpr, IError) { //nolint:funlen,gocyclo - // EvalForms is the main and the most important evaluation function on Serene. - // It's a long loooooooooooong function. Why? Well, Because we don't want to - // waste call stack spots in order to have a well organized code. - // In order to avoid stackoverflows and implement TCO ( which is a must for - // a functional language we need to avoid unnecessary calls and keep as much - // as possible in a loop. - // - // `expressions` is argument is basically a tree of expressions which - // this function walks over and rewrite it as necessary. The main purpose - // of rewriting the tree is to eliminate any unnecessary function call. - // This way we can eliminate tail calls and run everything faster. - // - // Execution scopes are just regular scopes that are attached to expressions - // in order to specify the scope that they need to get executed in. Since - // we rewrite the tree some of the nodes of the tree (expressions) has different - // scope so we need to attach a scope to those expressions. An example would be - // the `let` special form. It creates a new scope for its body, after creating - // the scope we will attach it to all the expressions in the body and rewrite - // the tree to replace the original `let` node with the expressions from the - // body of `let` and reloop over the tree. So the tree contains some nodes - // from the `let` body with an execution scope and the rest of tree with - // no execution scope (no attached scope) which we will use the `scope` - // that we got as an argument. - var ret IExpr - var err IError - -tco: - // TODO: With the new tree rewrite we might be able to get ride of this `for` - for { - // The TCO loop is there to take advantage or the fact that - // in order to call a function or a block we simply can change - // the value of the `expressions` and `scope` - var exprs []IExpr - - // Block evaluation rules: - // * If empty, return Nothing - // * Otherwise evaluate the expressions in the block one by one - // and return the last result - if expressions.GetType() == ast.Block { - if expressions.(*Block).Count() == 0 { - return &Nothing, nil - } - exprs = expressions.(*Block).ToSlice() - } else { - exprs = []IExpr{expressions} - } - - body: - for i := 0; i < len(exprs); i++ { - forms := exprs[i] - executionScope := forms.GetExecutionScope() - scope := scope - - if executionScope != nil { - scope = executionScope - } - - if rt.IsDebugMode() { - fmt.Printf("[DEBUG] Evaluating forms in NS: %s, Forms: %s\n", scope.GetNS(rt).GetName(), forms) - fmt.Printf("[DEBUG] * State: I: %d, Exprs: %s\n", i, exprs) - } - - // Evaluate any internal instruction that has to be run. - // Instructions should change the return value, but errors - // are ok - if forms.GetType() == ast.Instruction { - e := ProcessInstruction(rt, forms.(*Instruction)) - if e != nil { - return nil, e - } - - continue - } - - // Evaluating forms one by one - if forms.GetType() != ast.List { - ret, err = evalForm(rt, scope, forms) - if err != nil { - return nil, err - } - - continue body - } - - // Expand macroes that exists in the given array of expression `forms`. - // Since this implementation of Serene is an interpreter, the line - // between compile time and runtime is unclear (afterall every thing - // is happening in runtime). So we need to expand macroes before evaluating - // other forms. In the future we might want to cache the expanded AST - // as a cache and some sort of a bytecode for faster evaluation. - forms, err = macroexpand(rt, scope, forms) - if err != nil { - return nil, err - } - - if forms.GetType() != ast.List { - return evalForm(rt, scope, forms) - } - - list := forms.(*List) - - // Empty list evaluates to itself - if list.Count() == 0 { - ret = list - break tco // return &Nil, nil here - } - - rawFirst := list.First() - sform := "" - - // Handling special forms by looking up the first - // element of the list. If it is a symbol, Grab - // the name and check it for build it forms. - // - // Note: If we don't care about recursion in any - // case we can simply extract it to a function - // for example in `def` since we are going to - // evaluate the value separately, we don't care - // about recursion because we're going to handle - // it wen we're evaluating the value. But in the - // case of let it's a different story. - if rawFirst.GetType() == ast.Symbol { - sform = rawFirst.(*Symbol).GetName() - } - - switch sform { - // `ns` evaluation rules: - // * The first element has to be a symbol representing the - // name of the namespace. ( We won't evaluate the first - // element ) - // TODO: decide on the syntax and complete the docs - case NSFORM: - // TODO: Move this to a native function - ret, err = NSForm(rt, scope, list) - if err != nil { - return nil, err - } - - continue body // no rewrite - - // `quote` evaluation rules: - // * Only takes one argument - // * Returns the argument without evaluating it - case "quote": - // Including the `quote` itself - if list.Count() != 2 { - return nil, MakeError(rt, list, "'quote' quote only accepts one argument.") - } - ret = list.Rest().First() - err = nil - continue body // no rewrite - - // case "quasiquote-expand": - // return quasiquote(list.Rest().First()), nil - - // // For `quasiquote` evaluation rules, check out the documentation on - // // the `quasiquote` function in `quasiquote.go` - // case "quasiquote": - // expressions = quasiquote(list.Rest().First()) - // continue tco // Loop over to execute the new expressions - - // TODO: Implement `list` in serene itself when we have destructuring available - // Creates a new list form it's arguments. - case "list": - ret, err = evalForm(rt, scope, list.Rest().(*List)) - if err != nil { - return nil, err - } - - continue body // no rewrite - - // TODO: Implement `concat` in serene itself when we have protocols available - // Concats all the collections together. - case "concat": - evaledForms, e := evalForm(rt, scope, list.Rest().(*List)) - - if e != nil { - return nil, e - } - - lists := evaledForms.(*List).ToSlice() - - result := []IExpr{} - for _, lst := range lists { - if lst.GetType() != ast.List { - return nil, MakeError(rt, lst, fmt.Sprintf("don't know how to concat '%s'", lst.String())) - } - - result = append(result, lst.(*List).ToSlice()...) - } - - n := MakeNodeFromExprs(result) - - if n == nil { - n = &list.Node - } - - node := *n - - ret, err = MakeList(node, result), nil - if err != nil { - return nil, err - } - - continue body // no rewrite - - // TODO: Implement `list` in serene itself when we have destructuring available - // Calls the `Cons` function on the second argument to cons the first arg to it. - // In terms of a list, cons adds the first argument to as the new head of the list - // given in the second argument. - case "cons": - if list.Count() != 3 { - return nil, MakeError(rt, list, "'cons' needs exactly 3 arguments") - } - - evaledForms, e := evalForm(rt, scope, list.Rest().(*List)) - - if e != nil { - return nil, e - } - coll, ok := evaledForms.(*List).Rest().First().(IColl) - - if !ok { - return nil, MakeError(rt, list, "second arg of 'cons' has to be a collection") - } - - ret, err = coll.Cons(evaledForms.(*List).First()), nil - if err != nil { - return nil, err - } - - continue body // no rewrite - - // `def` evaluation rules - // * The first argument has to be a symbol. - // * The second argument has to be evaluated and be used as - // the value. - // * Defines a global binding in the current namespace using - // the symbol name binded to the value - case "def": - ret, err = Def(rt, scope, list.Rest().(*List)) - if err != nil { - return nil, err - } - - continue body // no rewrite - - // `defmacro` evaluation rules: - // * The first argument has to be a symbol - // * The second argument has to be a list of argument for the macro - // * The rest of the arguments will form a block that acts as the - // body of the macro. - case "defmacro": - ret, err = DefMacro(rt, scope, list) - if err != nil { - return nil, err - } - - continue body // no rewrite - - // `macroexpand` evaluation rules: - // * It has to have only one argument - // * It WILL evaluate the only argument and tries to expand it - // as a macro and returns the expanded forms. - case "macroexpand": - if list.Count() != 2 { - return nil, MakeError(rt, list, "'macroexpand' needs exactly one argument.") - } - evaledForm, e := evalForm(rt, scope, list.Rest().(*List)) - - if e != nil { - return nil, e - } - - ret, err = macroexpand(rt, scope, evaledForm.(*List).First()) - if err != nil { - return nil, err - } - continue body // no rewrite - - // `fn` evaluation rules: - // * It needs at least a collection of arguments - // * Defines an anonymous function. - case "fn": - ret, err = Fn(rt, scope, list) - if err != nil { - return nil, err - } - continue body // no rewrite - - // `if` evaluation rules: - // * It has to get only 3 arguments: PRED THEN ELSE - // * Evaluate only the PRED expression if the result - // is not `nil` or `false` evaluates THEN otherwise - // evaluate the ELSE expression and return the result. - case "if": - args := list.Rest().(*List) - if args.Count() != 3 { - return nil, MakeError(rt, args, "'if' needs exactly 3 aruments") - } - - pred, e := EvalForms(rt, scope, args.First()) - result := pred.GetType() - - if e != nil { - return nil, e - } - - if (result == ast.Bool && pred.(*Bool).IsFalse()) || result == ast.Nil { - // Falsy clause - exprs = append([]IExpr{args.Rest().Rest().First()}, restOfExprs(exprs, i)...) - } else { - // Truthy clause - exprs = append([]IExpr{args.Rest().First()}, restOfExprs(exprs, i)...) - } - - i = 0 - goto body // rewrite - - // `do` evaluation rules: - // * Evaluate the body as a new block in the TCO loop - // and return the result of the last expression - case "do": - // create a new slice of expressions by using the - // do body and merging it by the remaining expressions - // in the old `exprs` value and loop over it - doExprs := list.Rest().(*List).ToSlice() - exprs = append(doExprs, exprs[i+1:]...) - i = 0 - goto body // rewrite - - // TODO: Implement `eval` as a native function - // `eval` evaluation rules: - // * It only takes on arguments. - // * The argument has to be a form. For example if we pass a string - // to it as an argument that contains some expressions it will - // evaluate the string as string which will result to the same - // string. So IT DOES NOT READ the argument. - // * It will evaluate the given form as the argument and return - // the result. - case "eval": - if list.Count() != 2 { - return nil, MakeError(rt, list, "'eval' needs exactly 1 arguments") - } - form, e := evalForm(rt, scope, list.Rest().(*List)) - if e != nil { - return nil, e - } - - ret, err = EvalForms(rt, scope, form) - if err != nil { - return nil, err - } - - continue body // no rewrite - - // `let` evaluation rules: - // Let's assume the following: - // L = (let (A B C D) BODY) - // * Create a new scope which has the current scope as the parent - // * Evaluate the bindings by evaluating `B` and bind it to the name `A` - // in the scope. - // * Repeat the prev step for expr D and name C - // * Eval the block `BODY` using the created scope and return the result - // which is the result of the last expre in `BODY` - case "let": - if list.Count() < 2 { - return nil, MakeError(rt, list, "'let' needs at list 1 aruments") - } - - letScope := MakeScope(rt, scope.(*Scope), nil) - - // Since we're using IColl for the bindings, we can use either lists - // or vectors or even hashmaps for bindings - var bindings IColl - bindings = list.Rest().First().(IColl) - - body := list.Rest().Rest().(*List).ToSlice() - - if bindings.Count()%2 != 0 { - return nil, MakeError(rt, list.Rest().First(), "'let' bindings has to have even number of forms.") - } - - for { - // We're reducing over bindings here - if bindings.Count() == 0 { - break - } - - name := bindings.First() - expr := bindings.Rest().First() - - // TODO: We need to destruct the bindings here and remove this check - // for the symbol type - if name.GetType() != ast.Symbol { - return nil, MakeError(rt, name, "'let' doesn't support desbbtructuring yet, use a symbol.") - } - - // You might be wondering why we're using `EvalForms` here to evaluate - // the exprs in bindings, what about TCO ? - // Well, It's called TAIL call optimization for a reason. Exprs in the - // bindings are not tail calls - evaluatedExpr, e := EvalForms(rt, letScope, expr) - - if e != nil { - return nil, e - } - - letScope.Insert(name.String(), evaluatedExpr, false) - bindings = bindings.Rest().Rest().(IColl) - } - - changeExecutionScope(body, letScope) - exprs = append(body, exprs[i+1:]...) - i = 0 - goto body - - // list evaluation rules: - // * The first element of the list has to be an expression which is callable - // * An empty list evaluates to itself. - default: - // Evaluating all the elements of the list - listExprs, e := evalForm(rt, scope, list) - - if e != nil { - return nil, e - } - - f := listExprs.(*List).First() - - switch f.GetType() { - case ast.Fn: - // If the first element of the evaluated list is a function - // create a scope for it by creating the binding to the given - // parameters in the new scope and set the parent of it to - // the scope which the function defined in and then set the - // `expressions` to the body of function and loop again - fn := f.(*Function) - if e != nil { - return nil, e - } - - argList := listExprs.(*List).Rest().(*List) - - fnScope, e := MakeFnScope(rt, fn.GetScope(), fn.GetParams(), argList) - if e != nil { - return nil, e - } - rt.Stack.Push(list, fn) - - body := append( - fn.GetBody().ToSlice(), - // Add the PopStack instruction to clean up the stack after - // returning from the function. - MakeStackPop(rt), - ) - changeExecutionScope(body, fnScope) - body = append(body, restOfExprs(exprs, i)...) - exprs = body // Just because of the stupid linters - goto body // rewrite - - // If the function was a native function which is represented - // by the `NativeFunction` struct - case ast.NativeFn: - fn := f.(*NativeFunction) - - rt.Stack.Push(list, fn) - - ret, err = fn.Apply( - rt, - scope, - MakeNodeFromExpr(fn), - listExprs.(*List), - ) - - if err != nil { - return nil, err - } - - rt.Stack.Pop() - continue body // no rewrite - - default: - err = MakeError(rt, f, "don't know how to execute anything beside function") - ret = nil - break tco - } - } - } - break tco - } - - return ret, err -} - -// Eval the given `Block` of code with the given runtime `rt`. -// The Important part here is that any expression that we need -// to Eval has to be wrapped in a Block. Don't confused the -// concept of Block with blocks from other languages which -// specify by using `{}` or indent or what ever. Blocks in terms -// of Serene are just arrays of expressions and nothing more. -func Eval(rt *Runtime, forms *Block) (IExpr, IError) { - if forms.Count() == 0 { - // Nothing is literally Nothing - return &Nothing, nil - } - - v, err := EvalForms(rt, rt.CurrentNS().GetRootScope(), forms) - - if err != nil { - return nil, err - } - - return v, nil -} - -// EvalNSBody evals the body of the given namespace `ns` using the given -// runtime `rt`. It makes sure that the body starts with a `ns` special -// form with the same name as the ns argument. -func EvalNSBody(rt *Runtime, ns *Namespace) (*Namespace, IError) { - body := ns.getForms() - exprs := body.ToSlice() - - if len(exprs) == 0 { - return nil, MakeSemanticError( - rt, - ns, - errors.E0001, - fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()), - ) - } - - if exprs[0].GetType() == ast.List { - firstForm := exprs[0].(*List).First() - if firstForm.GetType() == ast.Symbol && firstForm.(*Symbol).GetName() == NSFORM { - _, err := EvalForms(rt, ns.GetRootScope(), body) - if err != nil { - return nil, err - } - return ns, nil - } - } - - return nil, MakeError(rt, ns, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName())) -} diff --git a/bootstrap/pkg/core/function.go b/bootstrap/pkg/core/function.go deleted file mode 100644 index 6de8be3..0000000 --- a/bootstrap/pkg/core/function.go +++ /dev/null @@ -1,276 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// Function implementations: -// * We have two different types of functions. User defined functions and -// native functions -// * User defined functions are represented via the `Function` struct. -// * Native functions are represented via the `NativeFunction` struct. -// * User defined functions gets evaluated by: -// - Creating a new scope a direct child of the scope that the function -// defined in -// - Creating bindings in the new scope to bind the passed values to their -// arguments names -// - Evaluate the body of the function in context of the new scope and return -// the result of the last expression -// * Native functions evaluates by calling the `Apply` method of the `IFn` -// interface which is quite simple. -// -// TODOs: -// * Support for multi-arity functions -// * Support for protocol functions -// * `IFn` protocol - -import ( - "fmt" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/errors" - "serene-lang.org/bootstrap/pkg/hash" -) - -type nativeFnHandler = func(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) - -type IFn interface { - IExpr - Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) - GetName() string -} - -// Function struct represent a user defined function. -type Function struct { - // Node struct holds the necessary functions to make - // Functions locatable - Node - ExecutionScope - // Name of the function, it can be empty and it has to be - // set via `def` - name string - - // Parent scope of the function. The scope which the function - // is defined in - scope IScope - - // A collection of arguments. Why IColl? because we can use - // Lists and Vectors for the argument lists. Maybe even - // hashmaps in future. - params IColl - - // A reference to the body block of the function - body *Block - isMacro bool -} - -type NativeFunction struct { - // Node struct holds the necessary functions to make - // Functions locatable - Node - ExecutionScope - name string - fn nativeFnHandler -} - -func (f *Function) GetType() ast.NodeType { - return ast.Fn -} - -func (f *Function) Hash() uint32 { - // TODO: Fix this function to return an appropriate hash for a function - return hash.Of([]byte(f.String())) -} - -func (f *Function) IsMacro() bool { - return f.isMacro -} - -func (f *Function) String() string { - if f.isMacro { - return fmt.Sprintf("", f.name, f) - } - - return fmt.Sprintf("", f.name, f) -} - -func (f *Function) GetName() string { - // TODO: Handle ns qualified symbols here - return f.name -} - -func (f *Function) SetName(name string) { - f.name = name -} - -func (f *Function) GetScope() IScope { - return f.scope -} - -func (f *Function) GetParams() IColl { - return f.params -} - -func (f *Function) ToDebugStr() string { - if f.isMacro { - return fmt.Sprintf("", f.name, f) - } - - return fmt.Sprintf("", f.name, f) -} - -func (f *Function) GetBody() *Block { - return f.body -} - -func (f *Function) Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - application := args.Cons(f) - return EvalForms(rt, scope, application) -} - -// MakeFunction Create a function with the given `params` and `body` in -// the given `scope`. -func MakeFunction(n Node, scope IScope, params IColl, body *Block) *Function { - return &Function{ - Node: n, - scope: scope, - params: params, - body: body, - isMacro: false, - } -} - -// MakeFnScope a new scope for the body of a function. It binds the `bindings` -// to the given `values`. -func MakeFnScope(rt *Runtime, parent IScope, bindings, values IColl) (*Scope, IError) { //nolint:gocyclo - // TODO: Break this function into smaller functions - scope := MakeScope(rt, parent.(*Scope), nil) - // TODO: Implement destructuring - binds := bindings.ToSlice() - exprs := values.ToSlice() - numberOfBindings := len(binds) - - if len(binds) > 0 { - lastBinding := binds[len(binds)-1] - - if lastBinding.GetType() == ast.Symbol && lastBinding.(*Symbol).IsRestable() { - numberOfBindings = len(binds) - 1 - } - - if lastBinding.GetType() == ast.Symbol && !lastBinding.(*Symbol).IsRestable() && numberOfBindings < len(exprs) { - return nil, MakeSemanticError( - rt, - values.(IExpr), - errors.E0002, - fmt.Sprintf("expected '%d' arguments, got '%d'.", bindings.Count(), values.Count()), - ) - } - } - - if numberOfBindings > len(exprs) { - if rt.IsDebugMode() { - fmt.Printf("[DEBUG] Mismatch on bindings and values: Bindings: %s, Values: %s\n", bindings, values) - } - - return nil, MakeSemanticError( - rt, - values.(IExpr), - errors.E0002, - fmt.Sprintf("expected '%d' arguments, got '%d'.", bindings.Count(), values.Count()), - ) - } - - for i := 0; i < len(binds); i++ { - // If an argument started with char `&` use it to represent - // rest of values. - // - // for example: `(fn (x y &z) ...)` - if binds[i].GetType() == ast.Symbol && binds[i].(*Symbol).IsRestable() { - if i != len(binds)-1 { - return nil, MakeError(rt, binds[i], "The function argument with '&' has to be the last argument.") - } - - // if the number of values are one less than the number of bindings - // but the last binding is a Restable (e.g &x) the the last bindings - // has to be an empty list. Note the check for number of vlaues comes - // next. - rest := MakeEmptyList(MakeNodeFromExpr(binds[i])) - - if i <= len(exprs)-1 { - // If the number of values matches the number of bindings - // or it is more than that create a list from them - // to pass it to the last argument that has to be Restable (e.g &x) - elements := exprs[i:] - var node Node - - if len(elements) > 0 { - n := MakeNodeFromExprs(elements) - - if n == nil { - n = &values.(*List).Node - } - - node = *n - } else { - node = MakeNodeFromExpr(binds[i]) - } - - rest = MakeList(node, elements) - } - - scope.Insert(binds[i].(*Symbol).GetName()[1:], rest, false) - break - } else { - scope.Insert(binds[i].(*Symbol).GetName(), exprs[i], false) - } - } - - return scope, nil -} - -func (f *NativeFunction) GetType() ast.NodeType { - return ast.NativeFn -} - -func (f *NativeFunction) GetName() string { - return f.name -} - -func (f *NativeFunction) String() string { - return fmt.Sprintf("", f.name, f) -} - -func (f *NativeFunction) Hash() uint32 { - // TODO: Fix this function to return an appropriate hash for a function - return hash.Of([]byte(f.String())) -} - -func (f *NativeFunction) ToDebugStr() string { - return fmt.Sprintf("", f.name) -} - -func (f *NativeFunction) Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) { - return f.fn(rt, scope, n, args) -} - -func MakeNativeFn(name string, f nativeFnHandler) NativeFunction { - return NativeFunction{ - Node: MakeNodeFromLocation(ast.MakeUnknownLocation()), - name: name, - fn: f, - } -} diff --git a/bootstrap/pkg/core/instructions.go b/bootstrap/pkg/core/instructions.go deleted file mode 100644 index 71a26de..0000000 --- a/bootstrap/pkg/core/instructions.go +++ /dev/null @@ -1,91 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// Instructions Implementation: -// * Instructions are expressions as well but they are not doing -// anything special by themselves. -// * We need them to be expressions because we need to process -// them as part of the evaluation loop -// * We use instructions as nodes in the AST to instruct Serene -// to do specific tasks after rewriting the AST. For example -// `PopStack` instructs Serene to simply pop a call from the -// call stack. -// * Instructions doesn't return a value and should not alter -// the return value of the eval loop. But they might interrupt -// the loop by raising an error `IError`. -import ( - "fmt" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type instructionType int - -// Instruction types -const ( - // Pop a function from the call stack. We use this - // instruction at the end of function bodies. So - // function bodies will clean up after themselves - PopStack instructionType = iota -) - -type Instruction struct { - // Just to be compatible with IExpr --- - Node - ExecutionScope - // ------------------------------------ - - Type instructionType -} - -func (n *Instruction) GetType() ast.NodeType { - return ast.Instruction -} - -func (n *Instruction) String() string { - return fmt.Sprintf("", n.Type) -} - -func (n *Instruction) ToDebugStr() string { - return n.String() -} - -func (n *Instruction) Hash() uint32 { - bytes := []byte(fmt.Sprintf("%d", n.Type)) - return hash.Of(append([]byte{byte(ast.Instruction)}, bytes...)) -} - -func MakeStackPop(rt *Runtime) IExpr { - return &Instruction{ - Type: PopStack, - } -} - -// ProcessInstruction is the main function to process instructions -func ProcessInstruction(rt *Runtime, form *Instruction) IError { - switch form.Type { - case PopStack: - rt.Stack.Pop() - return nil - default: - panic(fmt.Sprintf("Unknown instruction: '%d'", form.Type)) - } -} diff --git a/bootstrap/pkg/core/keyword.go b/bootstrap/pkg/core/keyword.go deleted file mode 100644 index a7c3183..0000000 --- a/bootstrap/pkg/core/keyword.go +++ /dev/null @@ -1,220 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// Keyword implementation: -// IMPORTANT NOTE: This implementation keyword is not decent at all -// it lacks many aspects of keywords which makes them great. But -// it is good enough for our use case. So we'll leave it as it is. -// -// Keywords are simple names and just names and nothing more. They -// match the following grammar: -// -// ``` -// KEYWORD = COLON [COLON] SYMBOL -// ``` -// Normally keywords doesn't have any namespace for example `:xyz` -// is just a name, but in order to avoid any name collision Serene -// supports namespace qualified keywords which basically put a keyword -// under a namespace. But it doesn't mean that you need to load a -// namespace in order to use any keyword that lives under that ns. -// because keywords are just names remember? There is two ways to -// use namespace qualified keywords: -// -// 1. Using full namespace name. For example, `:serene.core/xyz` -// To use this keyword you don't need the namespace `serene.core` -// to be loaded. It's just a name after all. -// -// 2. Using aliased namespaces. For example `::xyz` or `::core/xyz`. -// Using two colons instructs Serene to use aliased namespaces -// with the keyword. In the `::xyz` since the ns part is missing -// Serene will use the current namespace. For instance: -// -// ``` -// user> ::xyz -// :user/xyz -// ``` -// As you can see `::xyz` and `:user/xyz` (`user` being the ns name) -// are literally the same. -// -// But if we provide the ns part (`::core/xyz` example), a namespace -// with that alias has to be loaded and present in the current -// namespace. For example: -// -// ``` -// user> (require '(examples.hello-world hello)) -// -// user> ::hello/xyz -// :examples.hello-world/xyz -// ``` -// As you can see we had to load the ns with the `hello` alias to be -// able to use the alias in a keyword. -// -// TODO: Cache the keywords in the runtime on the first eval so we -// done have to evaluate them over and over again. It can be achieved -// by caching the `hash` value in the keyword itself and maintain a -// hashmap in the runtime from hash codes to a pointer to the keyword. -// But garbage collecting it would be an issue since Golang doesn't support -// weak pointer, but since bootstrap version of Serene is used only to -// bootstrap the compiler it's ok to ignore that for now - -import ( - "fmt" - "strings" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type Keyword struct { - Node - ExecutionScope - name string - // nsName is the string that is used as the namespace name. It - // might be an ns alias in the current ns or the full namespace - // as well. The first time that this keyword gets evaluated the - // `ns` field will be populated by a pointer to the actual - // namespace which is referrenced to via `nsName` and after that - // nsName will be pretty much useless. - nsName string - // It will be populated after the first evaluation of this keyword - ns *Namespace - - // Is it like :serene.core/something - nsQualified bool - - // Is it like ::s/something ? - aliased bool -} - -func (k *Keyword) GetType() ast.NodeType { - return ast.Keyword -} - -func (k *Keyword) String() string { - if k.nsQualified { - if k.ns == nil { - return ":" + k.nsName + "/" + k.name - } - return ":" + k.ns.GetName() + "/" + k.name - } - return ":" + k.name -} - -func (k *Keyword) ToDebugStr() string { - var ns string - if k.nsQualified { - ns = k.ns.GetName() + "/" - } else { - ns = "" - } - return fmt.Sprintf("", ns, k.name, k) -} - -func (k *Keyword) Hash() uint32 { - bytes := []byte(k.name) - nameHash := hash.Of(append([]byte{byte(ast.Keyword)}, bytes...)) - - if k.nsQualified { - if k.ns != nil { - return hash.CombineHashes(hash.Of([]byte(k.ns.GetName())), nameHash) - } - } - - return nameHash -} - -func (k *Keyword) SetNS(ns *Namespace) { - k.ns = ns -} - -func (k *Keyword) IsNSQualified() bool { - return k.nsQualified -} - -// Eval initializes the keyword by looking up the possible -// alias name and set it in the keyword. -func (k *Keyword) Eval(rt *Runtime, scope IScope) (*Keyword, IError) { - if k.nsQualified && k.aliased { - aliasedNS := rt.CurrentNS() - - if k.nsName != "" { - aliasedNS = rt.CurrentNS().LookupExternal(k.nsName) - } - - if aliasedNS == nil { - return nil, MakeError(rt, k, fmt.Sprintf("can't find the alias '%s' in the current namespace.", k.nsName)) - } - k.ns = aliasedNS - return k, nil - } - - return k, nil -} - -// Extracts the different parts of the keyword -func extractParts(s string) (nspart, namepart string) { - parts := strings.Split(s, "/") - namepart = parts[0] - - if len(parts) == 2 { - nspart = parts[0] - namepart = parts[1] - } - - return -} - -func MakeKeyword(n Node, name string) (*Keyword, IError) { - if strings.Count(name, ":") > 2 { - return nil, MakeSyntaxErrorf(n, "can't parse the keyword with more that two colons: '%s'", name) - } - - if strings.Count(name, "/") > 1 { - return nil, MakeSyntaxErrorf(n, "illegal namespace path for the given keyword: '%s'", name) - } - - var nsName string - var kwName string - keyword := name - - nsQualified := false - aliased := false - - if strings.HasPrefix(name, "::") { - nsQualified = true - aliased = true - keyword = name[2:] - } else if strings.HasPrefix(name, ":") && strings.Count(name, "/") == 1 { - nsQualified = true - keyword = name[1:] - } else if strings.HasPrefix(name, ":") { - keyword = name[1:] - } - - nsName, kwName = extractParts(keyword) - - return &Keyword{ - Node: n, - name: kwName, - nsName: nsName, - nsQualified: nsQualified, - aliased: aliased, - }, nil -} diff --git a/bootstrap/pkg/core/list.go b/bootstrap/pkg/core/list.go deleted file mode 100644 index 5565db6..0000000 --- a/bootstrap/pkg/core/list.go +++ /dev/null @@ -1,152 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - "strings" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -/** WARNING: -This List implementation may look simple and performant but since -we're using a slice here. But in fact it's not memory efficient at -all. We need to rewrite this later to be a immutable and persistent -link list of cons. -*/ - -type List struct { - Node - ExecutionScope - exprs []IExpr -} - -// Implementing IExpr for List --- - -func (l *List) GetType() ast.NodeType { - return ast.List -} - -func (l *List) String() string { - var strs []string - for _, e := range l.exprs { - strs = append(strs, e.String()) - } - return fmt.Sprintf("(%s)", strings.Join(strs, " ")) -} - -func (l *List) ToDebugStr() string { - return fmt.Sprintf("%#v", l) -} - -// END: IExpr --- - -// Implementing ISeq for List --- - -func (l *List) First() IExpr { - if l.Count() == 0 { - return MakeNil(MakeNodeFromExpr(l)) - } - return l.exprs[0] -} - -func (l *List) Rest() ISeq { - if l.Count() < 2 { - return MakeEmptyList(l.Node) - } - - rest := l.exprs[1:] - node := l.Node - if len(rest) > 0 { - // n won't be nil here but we should check anyway - n := MakeNodeFromExprs(rest) - if n == nil { - panic("'MakeNodeFromExprs' has returned nil for none empty array of exprs") - } - node = *n - } - - return MakeList(node, rest) -} - -func (l *List) Hash() uint32 { - bytes := []byte("TODO") - return hash.Of(append([]byte{byte(ast.List)}, bytes...)) -} - -// END: ISeq --- - -// Implementing ICountable for List --- - -func (l *List) Count() int { - return len(l.exprs) -} - -// END: ICountable --- - -// Implementing IColl for List --- - -func (l *List) ToSlice() []IExpr { - return l.exprs -} - -func (l *List) Cons(e IExpr) IExpr { - elements := append([]IExpr{e}, l.ToSlice()...) - node := MakeNodeFromExprs(elements) - - // Since 'elements' is not empty node won't be nil but we should - // check anyway - if node == nil { - node = &l.Node - } - return MakeList(*node, elements) -} - -// END: IColl --- - -func (l *List) AppendToList(e IExpr) *List { - l.exprs = append(l.exprs, e) - return l -} - -func ListStartsWith(l *List, sym string) bool { - if l.Count() > 0 { - firstElem := l.First() - if firstElem.GetType() == ast.Symbol { - return firstElem.(*Symbol).GetName() == sym - } - } - return false -} - -func MakeList(n Node, elements []IExpr) *List { - return &List{ - Node: n, - exprs: elements, - } -} - -func MakeEmptyList(n Node) *List { - return &List{ - Node: n, - exprs: []IExpr{}, - } -} diff --git a/bootstrap/pkg/core/macro.go b/bootstrap/pkg/core/macro.go deleted file mode 100644 index 5b2389b..0000000 --- a/bootstrap/pkg/core/macro.go +++ /dev/null @@ -1,106 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// TODO: -// * Add support for `before` and `after` state in macroexpantion -// and call stack. So in case of an error. Users should be able -// to see the forms before and after expansion. - -import ( - "serene-lang.org/bootstrap/pkg/ast" -) - -// Serene macros are in fact functions with the `isMacro` flag set to true. -// We have only normal macro implementation in bootstrap version of serene in -// compare to reader macros and evaluator macros and and other types. - -// MakeMacro creates a macro with the given `params` and `body` in -// the given `scope`. -func MakeMacro(scope IScope, name string, params IColl, body *Block) *Function { - return &Function{ - name: name, - scope: scope, - params: params, - body: body, - isMacro: true, - } -} - -// isMacroCall looks up the given `form` in the given `scope` if it is a symbol. -// If there is a value associated with the symbol in the scope, it will be checked -// to be a macro. -func isMacroCall(rt *Runtime, scope IScope, form IExpr) (*Function, bool) { //nolint:interfacer - if form.GetType() == ast.List { - list := form.(*List) - if list.Count() == 0 { - return nil, false - } - - first := list.First() - var macro IExpr = nil - - if first.GetType() == ast.Symbol { - binding := scope.Lookup(rt, first.(*Symbol).GetName()) - if binding != nil && binding.Public { - macro = binding.Value - } - } - if macro != nil { - if macro.GetType() == ast.Fn && macro.(*Function).IsMacro() { - return macro.(*Function), true - } - } - } - return nil, false -} - -// applyMacro works very similar to how we evaluate function calls the only difference -// is that we don't evaluate the arguments and create the bindings in the scope of the -// body directly as they are. It's Lisp Macroes after all. -func applyMacro(rt *Runtime, macro *Function, args IColl) (IExpr, IError) { - mscope, e := MakeFnScope(rt, macro.GetScope(), macro.GetParams(), args) - - if e != nil { - return nil, e - } - - return EvalForms(rt, mscope, macro.GetBody()) -} - -// macroexpand expands the given `form` as a macro and returns the resulted -// expression -func macroexpand(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) { - var macro *Function - var e IError - ok := false - - for { - macro, ok = isMacroCall(rt, scope, form) - if !ok { - return form, nil - } - - form, e = applyMacro(rt, macro, form.(IColl).Rest().(*List)) - - if e != nil { - return nil, e - } - } -} diff --git a/bootstrap/pkg/core/namespace.go b/bootstrap/pkg/core/namespace.go deleted file mode 100644 index 61ef669..0000000 --- a/bootstrap/pkg/core/namespace.go +++ /dev/null @@ -1,264 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type INamespace interface { - // TODO: Add a method to fetch the source code based on the source value - DefineGlobal() - LookupGlobal() - GetRootScope() IScope - // return the fully qualified name of the namespace - GetName() string - getForms() *Block - setForms(forms *Block) -} - -type Namespace struct { - // Fully qualified name of the ns. e.g `serene.core` - name string - - // The root scope of the namespace which keeps the global definitions - // and values of the scope. But not the external namespaces and values - rootScope Scope - - // TODO: Add a method to fetch the source code based on this value - // Path to the source of the name space, it can be a file path - // or anything related to the ns loader (not implemented yet). - source string - externals map[string]*Namespace - forms Block -} - -func (n *Namespace) GetType() ast.NodeType { - return ast.Namespace -} - -func (n *Namespace) GetLocation() *ast.Location { - return ast.MakeUnknownLocation() -} - -func (n *Namespace) String() string { - return fmt.Sprintf("", n.name, n.source) -} - -func (n *Namespace) ToDebugStr() string { - return fmt.Sprintf("", n.name, n.source) -} - -func (n *Namespace) GetExecutionScope() IScope { - return nil -} - -func (n *Namespace) SetExecutionScope(scope IScope) {} - -// DefineGlobal inserts the given expr `v` to the root scope of -// `n`. The `public` parameter determines whether the public -// value is accessible publicly or not (in other namespaces). -func (n *Namespace) DefineGlobal(k string, v IExpr, public bool) { - n.rootScope.Insert(k, v, public) -} - -// LookupGlobal looks up the value represented by the ns qualified -// symbol `sym` in the external symbols table. Simply looking up -// a public value from an external namespace. -func (n *Namespace) LookupGlobal(rt *Runtime, sym *Symbol) *Binding { - // TODO: Find a better name for this method, `LookupExternal` maybe - if !sym.IsNSQualified() { - return nil - } - - externalNS, ok := n.externals[sym.GetNSPart()] - - if !ok { - return nil - } - - externalScope := externalNS.GetRootScope() - return externalScope.Lookup(rt, sym.GetName()) -} - -func (n *Namespace) GetRootScope() IScope { - return &n.rootScope -} - -func (n *Namespace) GetName() string { - return n.name -} - -func (n *Namespace) Hash() uint32 { - return hash.Of([]byte(n.String())) -} - -func (n *Namespace) hasExternal(nsName string) bool { - _, ok := n.externals[nsName] - return ok -} - -// LookupExternal looks up the given `alias` in the `externals` table -// of the namespace. -func (n *Namespace) LookupExternal(alias string) *Namespace { - if n.hasExternal(alias) { - return n.externals[alias] - } - - return nil -} - -func (n *Namespace) setExternal(name string, ns *Namespace) { - n.externals[name] = ns -} - -func (n *Namespace) setForms(block *Block) { - n.forms = *block -} - -func (n *Namespace) getForms() *Block { - return &n.forms -} - -// requireNS finds and loads the namespace addressed by the given -// `ns` string. -func requireNS(rt *Runtime, ns *Symbol) (*Namespace, IError) { - // TODO: use a hashing algorithm to avoid reloading an unchanged namespace - loadedForms, err := rt.loadNS(ns) - - if err != nil { - return nil, err - } - - body := loadedForms.forms - source := loadedForms.source - - if body.Count() == 0 { - return nil, MakeError( - rt, - body, - fmt.Sprintf("The '%s' ns source code doesn't start with an 'ns' form.", ns), - ) - } - - namespace := MakeNS(rt, ns.GetName(), source) - namespace.setForms(body) - - return &namespace, nil -} - -// RequireNamespace finds and loads the naemspace which is addressed by the -// given expression `namespace` and add the loaded namespace to the list -// of available namespaces on the runtime and add the correct reference -// to the current namespace. If `namespace` is a symbol, then the name -// of the symbol would be used as the ns name and an alias with the -// same name will be added to the current namespace as the external -// reference. If it is a IColl (List at the moment), then the symbol -// in the first element would be the ns name and the second symbol -// will be the name of the alias to be used. -func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) { - var alias string - var ns *Symbol - - switch namespace.GetType() { - case ast.Symbol: - ns = namespace.(*Symbol) - alias = ns.GetName() - - case ast.List: - list := namespace.(*List) - first := list.First() - - if first.GetType() != ast.Symbol { - return nil, MakeError(rt, first, "The first element has to be a symbol") - } - - second := list.Rest().First() - if second.GetType() != ast.Symbol { - return nil, MakeError(rt, first, "The second element has to be a symbol") - } - - ns = first.(*Symbol) - alias = second.(*Symbol).GetName() - - default: - return nil, MakeError(rt, ns, "Don't know how to load the given namespace") - } - - loadedNS, err := requireNS(rt, ns) - - if err != nil { - return nil, err - } - - // Since we want to change the current ns to the loaded ns while evaluating it. - prevNS := rt.CurrentNS() - - rt.InsertNS(ns.GetName(), loadedNS) - inserted := rt.setCurrentNS(loadedNS.GetName()) - - if !inserted { - return nil, MakeError( - rt, - loadedNS, - fmt.Sprintf( - "the namespace '%s' didn't get inserted in the runtime.", - loadedNS.GetName()), - ) - } - - // Evaluating the body of the loaded ns (Check for ns validation happens here) - loadedNS, e := EvalNSBody(rt, loadedNS) - - // Set the current ns back first and then check for an error - inserted = rt.setCurrentNS(prevNS.GetName()) - if !inserted { - return nil, MakeError( - rt, - loadedNS, - fmt.Sprintf( - "can't set the current ns back to '%s' from '%s'.", - prevNS.GetName(), - loadedNS.GetName()), - ) - } - - if e != nil { - return nil, e - } - - // Set the external reference to the loaded ns in the current ns - prevNS.setExternal(alias, loadedNS) - return loadedNS, nil -} - -// MakeNS creates a new namespace with the given `name` and `source` -func MakeNS(rt *Runtime, name, source string) Namespace { - s := MakeScope(rt, nil, &name) - - return Namespace{ - name: name, - rootScope: *s, - source: source, - externals: map[string]*Namespace{}, - } -} diff --git a/bootstrap/pkg/core/nil.go b/bootstrap/pkg/core/nil.go deleted file mode 100644 index 0d12e5c..0000000 --- a/bootstrap/pkg/core/nil.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import "serene-lang.org/bootstrap/pkg/ast" - -type Nil struct { - Node - ExecutionScope -} - -func (n *Nil) GetType() ast.NodeType { - return ast.Nil -} - -func (n *Nil) String() string { - return NILFORM -} - -func (n *Nil) ToDebugStr() string { - return NILFORM -} - -func (n *Nil) Hash() uint32 { - return 0 -} - -func MakeNil(n Node) *Nil { - return &Nil{Node: n} -} diff --git a/bootstrap/pkg/core/nothing.go b/bootstrap/pkg/core/nothing.go deleted file mode 100644 index 9c9a26f..0000000 --- a/bootstrap/pkg/core/nothing.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type NothingType struct{} - -var Nothing = NothingType{} - -func (n NothingType) GetType() ast.NodeType { - return ast.Nothing -} - -func (n NothingType) Hash() uint32 { - bytes := []byte("Nothing") - return hash.Of(append([]byte{byte(ast.Block)}, bytes...)) -} - -func (n NothingType) GetLocation() *ast.Location { - return ast.MakeUnknownLocation() -} - -func (n NothingType) String() string { - return "" -} - -func (n NothingType) ToDebugStr() string { - return "" -} - -func (n NothingType) GetExecutionScope() IScope { - return nil -} - -func (n NothingType) SetExecutionScope(scope IScope) {} diff --git a/bootstrap/pkg/core/numbers.go b/bootstrap/pkg/core/numbers.go deleted file mode 100644 index d24cf3b..0000000 --- a/bootstrap/pkg/core/numbers.go +++ /dev/null @@ -1,204 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "encoding/binary" - "fmt" - "math" - "strconv" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type INumber interface { - IExpr - // Int() int - // I8() int8 - // I16() int16 - // I32() int32 - I64() int64 - - // UInt() uint - // UI8() uint8 - // UI16() uint16 - // UI32() uint32 - // UI64() uint64 - - // F32() float32 - F64() float64 - - // Add(n Number) Number - // TDOD: Add basic operators here -} - -/** WARNING: -These are really stupid implementations of numbers we -need better implmentations later, but it's ok for something -to begin with -*/ - -type Integer struct { - Node - // ExecutionScope checkout IScopable - scope IScope - - ExecutionScope - value int64 -} - -func (i *Integer) GetType() ast.NodeType { - return ast.Number -} - -func (i *Integer) Hash() uint32 { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, uint64(i.value)) - return hash.Of(b) -} - -func (i *Integer) GetExecutionScope() IScope { - return i.scope -} - -func (i *Integer) SetExecutionScope(scope IScope) { - i.scope = scope -} - -func (i *Integer) String() string { - return fmt.Sprintf("%d", i.value) -} - -func (i *Integer) ToDebugStr() string { - return fmt.Sprintf("%#v", i) -} - -func (i *Integer) I64() int64 { - return i.value -} - -func (i *Integer) F64() float64 { - return float64(i.value) -} - -func MakeInteger(x interface{}) (*Integer, IError) { - var value int64 - switch x := x.(type) { - case uint32: - value = int64(x) - case int32: - value = int64(x) - default: - return nil, MakePlainError(fmt.Sprintf("don't know how to make 'integer' out of '%s'", x)) - } - - return &Integer{ - value: value, - }, nil -} - -type Double struct { - Node - // ExecutionScope checkout IScopable - scope IScope - value float64 -} - -func (d *Double) GetType() ast.NodeType { - return ast.Number -} - -func (d *Double) Hash() uint32 { - b := make([]byte, 8) - - binary.BigEndian.PutUint64(b, math.Float64bits(d.value)) - return hash.Of(b) -} - -func (d *Double) String() string { - return fmt.Sprintf("%f", d.value) -} - -func (d *Double) ToDebugStr() string { - return fmt.Sprintf("%#v", d) -} - -func (d *Double) GetExecutionScope() IScope { - return d.scope -} - -func (d *Double) SetExecutionScope(scope IScope) { - d.scope = scope -} - -func (d *Double) I64() int64 { - return int64(d.value) -} - -func (d *Double) F64() float64 { - return d.value -} - -func MakeNumberFromStr(n Node, strValue string, isDouble bool) (INumber, error) { - var ret INumber - - if isDouble { - v, err := strconv.ParseFloat(strValue, 64) - - if err != nil { - return nil, err - } - - ret = &Double{ - Node: n, - value: v, - } - } else { - v, err := strconv.ParseInt(strValue, 10, 64) - if err != nil { - return nil, err - } - - ret = &Integer{ - Node: n, - value: v, - } - } - - return ret, nil -} - -func MakeDouble(x interface{}) (*Double, IError) { - var value float64 - switch x := x.(type) { - case uint32: - value = float64(x) - case int32: - value = float64(x) - case float32: - value = float64(x) - default: - return nil, MakePlainError(fmt.Sprintf("don't know how to make 'double' out of '%s'", x)) - } - - return &Double{ - value: value, - }, nil -} diff --git a/bootstrap/pkg/core/parser.go b/bootstrap/pkg/core/parser.go deleted file mode 100644 index ad7e303..0000000 --- a/bootstrap/pkg/core/parser.go +++ /dev/null @@ -1,589 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// Parser Implementation: -// * `ParseToAST` is the entry point of the parser -// * It's a manual parser with look ahead factor of (1) -// * It parsers the input string to a tree of `IEpxr`s -// -// TODOs: -// * Add a shortcut for anonymous functions similar to `#(...)` clojure -// syntax -// * Add the support for strings -// * Add the support for kewords -// * Add a shortcut for the `deref` function like `@x` => `(deref x)` -// * A line of comment at the end of a list definition causes a synxtax error. -// We need to fix it. For example: -// (asdb xyz -// ;; problematic comment line -// ) -// Will fails. The reason being we call `readExpr` in `readList` and in the -// `readExpr` when we read a line of comment we jump to a label and try to -// read another expr which in our case it would read the end of list and throw -// and error - -import ( - "strings" - "unicode" - - "serene-lang.org/bootstrap/pkg/ast" -) - -// An array of the valid characters that be be used in a symbol -var validChars = []rune{'!', '$', '%', '&', '*', '+', '-', '.', '~', '/', ':', '<', '=', '>', '?', '@', '^', '_'} - -// IParsable defines the common interface which any parser has to implement. -type IParsable interface { - // Reads the next character in the buffer with respect to skipWhitespace - // parameter which basically jumps over whitespace and some conceptual - // equivalent of a whitespace like '\n' - next(skipWhitespace bool) *string - - // Similar to the `next` but it won't change the position in the buffer - // so an imidiate `next` function after a `peek` will read the same char - // but will move the position, and a series of `peek` calls will read the - // same function over and over again without changing the position in the - // buffer. - peek(skipWhitespace bool) *string - - // Moves back the position by one in the buffer. - back() - - // Returns the current position in the buffer - GetLocation() int - GetSource() *ast.Source - Buffer() *[]string -} - -// StringParser is an implementation of the IParsable that operates on strings. -// To put it simply it parses input strings -type StringParser struct { - buffer []string - pos int - source string - - // This slice holds the boundaries of lines in the buffer. Basically - // each element determines the position which a line ends and the line - // number directly maps to the position of it's boundary in the slice. - lineIndex []int -} - -// Implementing IParsable for StringParser --- - -// updateLineIndex reads the current character and if it is an end of line, then -// it will update the line index to add the boundaries of the current line. -func (sp *StringParser) updateLineIndex(pos int) { - if pos < len(sp.buffer) { - c := sp.buffer[pos] - if c == "\n" { - if len(sp.lineIndex) > 0 { - if sp.lineIndex[len(sp.lineIndex)-1] != pos+1 { - // Including the \n itself - sp.lineIndex = append(sp.lineIndex, pos+1) - } - } else { - sp.lineIndex = append(sp.lineIndex, pos+1) - } - } - } -} - -// Returns the next character in the buffer -func (sp *StringParser) next(skipWhitespace bool) *string { - if sp.pos >= len(sp.buffer) { - return nil - } - char := sp.buffer[sp.pos] - sp.updateLineIndex(sp.pos) - sp.pos++ - - if skipWhitespace && isSeparator(&char) { - return sp.next(skipWhitespace) - } - - return &char -} - -// isSeparator returns a boolean indicating whether the given character `c` -// contains a separator or not. In a Lisp whitespace and someother characters -// are conceptually the same and we need to treat them the same as well. -func isSeparator(c *string) bool { - if c == nil { - return false - } - - r := []rune(*c)[0] - if r == ' ' || r == '\t' || r == '\n' || r == '\f' { - return true - } - - return false -} - -// Return the character of the buffer without consuming it -func (sp *StringParser) peek(skipWhitespace bool) *string { - if sp.pos >= len(sp.buffer) { - return nil - } - - c := sp.buffer[sp.pos] - if isSeparator(&c) && skipWhitespace { - sp.updateLineIndex(sp.pos) - sp.pos++ - return sp.peek(skipWhitespace) - } - return &c -} - -// Move the char pointer back by one character -func (sp *StringParser) back() { - if sp.pos > 0 { - sp.pos-- - } -} - -func (sp *StringParser) GetLocation() int { - return sp.pos -} - -func (sp *StringParser) GetSource() *ast.Source { - return &ast.Source{ - Buffer: &sp.buffer, - NS: sp.source, - LineIndex: &sp.lineIndex, - } -} - -func (sp *StringParser) Buffer() *[]string { - return &sp.buffer -} - -// END: IParsable --- - -// makeErrorAtPoint is a helper function which generates an `IError` that -// points at the current position of the buffer. -func makeErrorAtPoint(p IParsable, msg string, a ...interface{}) IError { - n := MakeSinglePointNode(p.GetSource(), p.GetLocation()) - return MakeSyntaxErrorf(n, msg, a...) -} - -// makeErrorFromError is a function which wraps a Golang error in an IError -func makeErrorFromError(parser IParsable, e error) IError { - //nolint:govet - return makeErrorAtPoint(parser, "%w", e) -} - -func contains(s []rune, c rune) bool { - for _, v := range s { - if v == c { - return true - } - } - - return false -} - -func isValidForSymbol(char string) bool { - c := rune(char[0]) - return contains(validChars, c) || unicode.IsLetter(c) || unicode.IsDigit(c) -} - -func readKeyword(parser IParsable) (IExpr, IError) { - symbol, err := readRawSymbol(parser) - if err != nil { - return nil, err - } - - node := MakeNodeFromExpr(symbol) - return MakeKeyword(node, ":"+symbol.(*Symbol).String()) -} - -// readRawSymbol reads a symbol from the current position forward -func readRawSymbol(parser IParsable) (IExpr, IError) { - c := parser.peek(false) - var symbol string - - if c == nil { - return nil, makeErrorAtPoint(parser, "unexpected enf of file while parsing a symbol") - } - - // Does the symbol starts with a valid character or not - if isValidForSymbol(*c) { - parser.next(false) - symbol = *c - } else { - return nil, makeErrorAtPoint(parser, - "unexpected character: got '%s', expected a symbol at %d", - *c, - parser.GetLocation(), - ) - } - - // read the rest of the symbol - for { - c := parser.next(false) - - if c == nil { - break - } - - if isValidForSymbol(*c) { - symbol += *c - } else { - parser.back() - break - } - } - - node := MakeNode(parser.GetSource(), parser.GetLocation()-len(symbol), parser.GetLocation()) - sym, err := MakeSymbol(node, symbol) - - if err != nil { - err.SetNode(&node) - return nil, err - } - - return sym, nil -} - -func readString(parser IParsable) (IExpr, IError) { - str := "" - - for { - c := parser.next(false) - if c == nil { - return nil, makeErrorAtPoint(parser, "reached end of file while scanning a string") - } - - if *c == "\"" { - node := MakeNode(parser.GetSource(), parser.GetLocation()-len(str), parser.GetLocation()) - return MakeString(node, str), nil - } - - if *c == "\\" { - c = parser.next(false) - switch *c { - case "n": - str += "\n" - case "t": - str += "\t" - case "r": - str += "\r" - case "\\": - str += "\\" - case "\"": - str += "\"" - default: - return nil, makeErrorAtPoint(parser, "Unsupported escape character: \\%s", *c) - } - } else { - str += *c - } - } -} - -// readNumber reads a number with respect to its sign and whether it's, a ...interface{} -// a decimal or a float -func readNumber(parser IParsable, neg bool) (IExpr, IError) { - isDouble := false - result := "" - - if neg { - result = "-" - } - - for { - c := parser.next(false) - - if c == nil { - break - } - - if *c == "." && isDouble { - return nil, makeErrorAtPoint(parser, "a double with more that one '.' ???") - } - - if *c == "." { - isDouble = true - result += *c - continue - } - - // Weird, But go won't stop complaining without this swap - char := *c - r := rune(char[0]) - if unicode.IsDigit(r) { - result += *c - } else if isValidForSymbol(char) { - return nil, makeErrorAtPoint(parser, "Illegal token while scanning for a number.") - } else { - parser.back() - break - } - } - n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) - n.location.DecStart(len(result)) - value, err := MakeNumberFromStr(n, result, isDouble) - - if err != nil { - return nil, makeErrorFromError(parser, err) - } - - return value, nil -} - -// readSymbol reads a symbol and return the appropriate type of expression -// based on the symbol conditions. For example it will read a number if the -// symbol starts with a number or a neg sign or a string if it starts with '\"' -// and a raw symbol otherwise -func readSymbol(parser IParsable) (IExpr, IError) { - c := parser.peek(false) - - if c == nil { - return nil, makeErrorAtPoint(parser, "unexpected end of file while scanning a symbol") - } - - if *c == "\"" { - parser.next(false) - return readString(parser) - } - - // Weird, But go won't stop complaining without this swap - char := *c - r := rune(char[0]) - if unicode.IsDigit(r) { - return readNumber(parser, false) - } - - if *c == "-" { - parser.next(true) - c := parser.peek(false) - - // Weird, But go won't stop complaining without this swap - char := *c - r := rune(char[0]) - - if unicode.IsDigit(r) { - return readNumber(parser, true) - } - // Unread '-' - parser.back() - return readRawSymbol(parser) - } - return readRawSymbol(parser) -} - -// readList reads a List recursively. -func readList(parser IParsable) (IExpr, IError) { - list := []IExpr{} - - for { - c := parser.peek(true) - if c == nil { - return nil, makeErrorAtPoint(parser, "reaching the end of file while reading a list") - } - if *c == ")" { - parser.next(true) - break - } else { - val, err := readExpr(parser) - - if err != nil { - return nil, err - } - list = append(list, val) - } - } - - node := MakeNodeFromExprs(list) - if node == nil { - n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) - node = &n - } - - node.location.DecStart(1) - node.location.IncEnd(1) - - return MakeList(*node, list), nil -} - -//nolint:unparam -func readComment(parser IParsable) (IExpr, IError) { - for { - c := parser.next(false) - if c == nil || *c == "\n" { - return nil, nil - } - } -} - -// readQuotedExpr reads the backquote and replace it with a call -// to the `quasiquote` macro. -func readQuotedExpr(parser IParsable, quote string) (IExpr, IError) { - expr, err := readExpr(parser) - if err != nil { - return nil, err - } - - node := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation()) - sym, err := MakeSymbol(node, quote) - if err != nil { - err.SetNode(&node) - return nil, err - } - - listElems := []IExpr{sym, expr} - listNode := MakeNodeFromExprs(listElems) - // listNode won't be nil in this case but it doesn't - // mean we shouldn't check - if listNode == nil { - n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) - listNode = &n - } - - listNode.location.DecStart(1) - listNode.location.IncStart(1) - - return MakeList(*listNode, listElems), nil -} - -// readUnquotedExpr reads different unquoting expressions from their short representaions. -// ~a => (unquote a) -// ~@a => (unquote-splicing a) -// Note: `unquote` and `unquote-splicing` are not global functions or special, they are bounded -// to quasiquoted expressions only. -func readUnquotedExpr(parser IParsable) (IExpr, IError) { - c := parser.peek(true) - - if c == nil { - return nil, makeErrorAtPoint(parser, "end of file while reading an unquoted expression") - } - - var sym IExpr - var err IError - var expr IExpr - - node := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation()) - - if *c == "@" { - parser.next(true) - sym, err = MakeSymbol(node, "unquote-splicing") - if err != nil { - err.SetNode(&node) - } else { - expr, err = readExpr(parser) - } - } else { - sym, err = MakeSymbol(node, "unquote") - if err != nil { - err.SetNode(&node) - } else { - expr, err = readExpr(parser) - } - } - - if err != nil { - return nil, err - } - - listElems := []IExpr{sym, expr} - - listNode := MakeNodeFromExprs(listElems) - - // listNode won't be nil in this case but it doesn't - // mean we shouldn't check - if listNode == nil { - n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) - listNode = &n - } - - listNode.location.DecStart(1) - listNode.location.IncStart(1) - return MakeList(*listNode, listElems), nil -} - -// readExpr reads one expression from the input. This function is the most -// important function in the parser which dispatches the call to different -// reader functions based on the first character -func readExpr(parser IParsable) (IExpr, IError) { -loop: - c := parser.next(true) - - if c == nil { - // We're done reading - return nil, nil - } - - if *c == "'" { - return readQuotedExpr(parser, "quote") - } - - if *c == "~" { - return readUnquotedExpr(parser) - } - - if *c == "`" { - return readQuotedExpr(parser, "quasiquote") - } - if *c == "(" { - return readList(parser) - } - if *c == ";" { - readComment(parser) - goto loop - } - - if *c == ":" { - return readKeyword(parser) - } - - parser.back() - return readSymbol(parser) -} - -// ParseToAST is the entry function to the reader/parser which -// converts the `input` string to a `Block` of code. A block -// by itself is not something available to the language. It's -// just anbstraction for a ordered collection of expressions. -// It doesn't have anything to do with the concept of blocks -// from other programming languages. -func ParseToAST(ns, input string) (*Block, IError) { - var forms Block - parser := StringParser{ - buffer: strings.Split(input, ""), - pos: 0, - source: ns, - } - - for { - expr, err := readExpr(&parser) - if err != nil { - return nil, err - } - - if expr == nil { - break - } - - forms.Append(expr) - } - - return &forms, nil -} diff --git a/bootstrap/pkg/core/printer.go b/bootstrap/pkg/core/printer.go deleted file mode 100644 index bfa4cc9..0000000 --- a/bootstrap/pkg/core/printer.go +++ /dev/null @@ -1,195 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - "strings" - - "github.com/gookit/color" - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/errors" -) - -func toRepresanbleString(forms ...IRepresentable) string { - var results []string - - for _, x := range forms { - results = append(results, x.String()) - } - - return strings.Join(results, " ") -} - -func toPrintableString(forms ...IRepresentable) string { - var results []string - - for _, x := range forms { - if printable, ok := x.(IPrintable); ok { - results = append(results, printable.PrintToString()) - continue - } - results = append(results, x.String()) - } - - return strings.Join(results, " ") -} - -func Pr(rt *Runtime, forms ...IRepresentable) { - fmt.Print(toRepresanbleString(forms...)) -} - -func Prn(rt *Runtime, forms ...IRepresentable) { - fmt.Println(toRepresanbleString(forms...)) -} - -func Print(rt *Runtime, forms ...IRepresentable) { - fmt.Print(toPrintableString(forms...)) -} - -func Println(rt *Runtime, forms ...IRepresentable) { - fmt.Println(toPrintableString(forms...)) -} - -func printError(_ *Runtime, err IError, stage int) { - loc := err.GetLocation() - source := loc.GetSource() - - startline := source.LineNumberFor(loc.GetStart()) - - if startline > 0 { - startline-- - } - - endline := source.LineNumberFor(loc.GetEnd()) + 1 - - var lines string - for i := startline; i <= endline; i++ { - line := source.GetLine(i) - if line != "----" { - lines += fmt.Sprintf("%d:\t%s\n", i, line) - } - } - - color.Yellow.Printf( - "%d: At '%s':%d\n", - stage, - source.NS, - source.LineNumberFor(loc.GetStart()), - ) - - color.White.Printf("%s\n", lines) - - errTag := color.Red.Sprint(err.GetErrType().String()) - fmt.Printf("%s: %s\nAt: %d to %d\n", errTag, err.String(), loc.GetStart(), loc.GetEnd()) -} - -func frameCaption(traces *TraceBack, frameIndex int) string { - if frameIndex >= len(*traces) || frameIndex < 0 { - panic("Out of range index for the traceback array. It shouldn't happen!!!") - } - var prevFrame *Frame - place := "*run*" - frame := (*traces)[frameIndex] - - loc := frame.Caller.GetLocation() - source := loc.GetSource() - - if frameIndex != 0 { - prevFrame = (*traces)[frameIndex-1] - place = prevFrame.Callee.GetName() - } - - return color.Yellow.Sprintf( - "%d: In function '%s' at '%s':%d\n", - frameIndex, - place, - source.NS, - source.LineNumberFor(loc.GetStart()), - ) -} - -func frameSource(traces *TraceBack, frameIndex int) string { - if frameIndex >= len(*traces) || frameIndex < 0 { - panic("Out of range index for the traceback array. It shouldn't happen!!!") - } - - frame := (*traces)[frameIndex] - caller := frame.Caller - callerLoc := caller.GetLocation() - callerSource := callerLoc.GetSource() - - startline := callerSource.LineNumberFor(callerLoc.GetStart()) - - if startline > 0 { - startline-- - } - - endline := callerSource.LineNumberFor(callerLoc.GetEnd()) + 1 - - var lines string - for i := startline; i <= endline; i++ { - fLoc := frame.Caller.GetLocation() - - if fLoc.IsKnownLocaiton() { - line := fLoc.GetSource().GetLine(i) - if line != ast.OutOfRangeLine { - lines += fmt.Sprintf("%d:\t%s\n", i, line) - } - } else { - lines += "Builtin\n" - } - } - - return lines -} - -func printErrorWithTraceBack(_ *Runtime, err IError) { - trace := err.GetStackTrace() - - for i := range *trace { - fmt.Print(frameCaption(trace, i)) - color.White.Printf(frameSource(trace, i)) - } - loc := err.GetLocation() - errTag := color.Red.Sprint(err.GetErrType().String()) - fmt.Printf( - "%s: %s\nAt: %d to %d\n", - errTag, - err.String(), - loc.GetStart(), - loc.GetEnd(), - ) - if err.GetErrno() != errors.E0000 { - fmt.Printf("For more information on this error try: `serene explain %s`\n", err.GetErrno()) - } -} - -func PrintError(rt *Runtime, err IError) { - switch err.GetErrType() { - case SyntaxError, SemanticError: - printError(rt, err, 0) - return - case RuntimeError: - printErrorWithTraceBack(rt, err) - return - default: - panic(fmt.Sprintf("Don't know about error type '%d'", err.GetErrType())) - } -} diff --git a/bootstrap/pkg/core/quasiquote.go b/bootstrap/pkg/core/quasiquote.go deleted file mode 100644 index 53a4f04..0000000 --- a/bootstrap/pkg/core/quasiquote.go +++ /dev/null @@ -1,173 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -// func qqLoop(xs []IExpr) IExpr { -// acc := MakeEmptyList() -// for i := len(xs) - 1; 0 <= i; i -= 1 { -// elem := xs[i] -// switch elem.GetType() { -// case ast.List: -// if ListStartsWith(elem.(*List), "unquote-splicing") { -// acc = MakeList([]IExpr{ -// MakeSymbol(MakeNodeFromExpr(elem), "concat"), -// elem.(*List).Rest().First(), -// acc}) -// continue -// } -// default: -// } -// acc = MakeList([]IExpr{ -// MakeSymbol(MakeNodeFromExpr(elem), "cons"), -// quasiquote(elem), -// acc}) -// } -// return acc -// } - -// func quasiquote(e IExpr) IExpr { -// switch e.GetType() { -// case ast.Symbol: -// return MakeList([]IExpr{ -// MakeSymbol(MakeNodeFromExpr(e), "quote"), e}) -// case ast.List: -// list := e.(*List) -// if ListStartsWith(list, "unquote") { -// return list.Rest().First() -// } -// if ListStartsWith(list, "quasiquote") { -// return quasiquote(qqLoop(list.ToSlice())) -// } - -// return qqLoop(list.ToSlice()) -// default: -// return e -// } -// } -// const qqQUOTE string = "*quote*" - -// func isSymbolEqual(e IExpr, name string) bool { -// if e.GetType() == ast.Symbol && e.(*Symbol).GetName() == name { -// return true -// } -// return false -// } -// func isQuasiQuote(e IExpr) bool { -// return isSymbolEqual(e, "quasiquote") -// } - -// func isUnquote(e IExpr) bool { -// return isSymbolEqual(e, "unquote") -// } - -// func isUnquoteSplicing(e IExpr) bool { -// return isSymbolEqual(e, "unquote-splicing") -// } - -// func qqSimplify(e IExpr) (IExpr, IError) { -// return e, nil -// } - -// func qqProcess(rt *Runtime, e IExpr) (IExpr, IError) { -// switch e.GetType() { - -// // Example: `x => (*quote* x) => (quote x) -// case ast.Symbol: -// sym, err := MakeSymbol(MakeNodeFromExpr(e), qqQUOTE) -// if err != nil { -// //newErr := makeErrorAtPoint() -// // TODO: uncomment next line when we have stackable errors -// // newErr.stack(err) -// return nil, err -// } -// elems := []IExpr{ -// sym, -// e, -// } - -// n := MakeNodeFromExprs(elems) -// if n == nil { -// n = &sym.Node -// } -// return MakeList( -// *n, -// elems, -// ), nil - -// case ast.List: -// list := e.(*List) -// first := list.First() - -// // Example: ``... reads as (quasiquote (quasiquote ...)) and this if will check -// // for the second `quasiquote` -// if isQuasiQuote(first) { -// result, err := qqCompletelyProcess(rt, list.Rest().First()) - -// if err != nil { -// return nil, err -// } - -// return qqProcess(rt, result) -// } - -// // Example: `~x reads as (quasiquote (unquote x)) -// if isUnquote(first) { -// return list.Rest().First(), nil -// } -// // ??? -// if isUnquoteSplicing(first) { -// return nil, MakeError(rt, first, "'unquote-splicing' is not allowed out of a collection.") -// } - -// // p := list -// // q := MakeEmptyList() -// // for { -// // p = p.Rest().(*List) -// // } - -// } - -// return e, nil -// } - -// func qqRemoveQQFunctions(e IExpr) (IExpr, IError) { -// return e, nil -// } - -// func qqCompletelyProcess(rt *Runtime, e IExpr) (IExpr, IError) { -// rawResult, err := qqProcess(rt, e) - -// if err != nil { -// return nil, err -// } - -// if rt.IsQQSimplificationEnabled() { -// rawResult, err = qqSimplify(rawResult) - -// if err != nil { -// return nil, err -// } -// } - -// return qqRemoveQQFunctions(rawResult) -// } - -// func quasiquote(rt *Runtime, e IExpr) (IExpr, IError) { -// return qqCompletelyProcess(rt, e) -// } diff --git a/bootstrap/pkg/core/reader.go b/bootstrap/pkg/core/reader.go deleted file mode 100644 index 688dad0..0000000 --- a/bootstrap/pkg/core/reader.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -func ReadString(ns, input string) (*Block, IError) { - return ParseToAST(ns, input) -} diff --git a/bootstrap/pkg/core/runtime.go b/bootstrap/pkg/core/runtime.go deleted file mode 100644 index c51e98f..0000000 --- a/bootstrap/pkg/core/runtime.go +++ /dev/null @@ -1,217 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strings" -) - -/** TODO: -Create an IRuntime interface to avoid using INamespace directly -*/ - -/** TODO: -Handle concurrency on the runtime level -*/ - -// loadedForms is used as "pair" implementation to keep the loaded -// expressions and the source where the expressions are coming from -type loadedForms struct { - source string - forms *Block -} - -// TODO: Make the Runtime and it's fields thread safe - -// Runtime is the most important data structure in Serene which hold -// the necessary information and the state of the interpreter at runtime (duh!). -// At any given time and thread only on Runtime has to exist and we always need -// to pass a pointer to the runtime around and avoid copying. (We don't have -// multithread support just yet but the Runtime must be thread safe). -type Runtime struct { - // A mapping from ns names (e.g some.ns.over.there) to the namespace - // data. This hashmap is owner of the namespaces, meaning that we only - // pass pointers to the namespaces around and any mutation has to happen - // here - namespaces map[string]Namespace - - // A mapping from the builtin function names to the corresponding - // NativeFunction struct that implements them as expressions (IExpr). - // native functions are those which can be special form as well but - // they are more suited to be a function and at the same time we - // can't implement them in Serene itself. - builtins map[string]NativeFunction - - // currentNS is the fully qualified name of the current namespace which - // is being processed (evaluates) at any given time. Since it's not - // thread safe at the moment we need to be careful changeing its value. - currentNS string - - // paths is an array of filesystem paths that have we need to look into - // in order to find and load the namespaces. Similar to `load_path` in other - // languages - paths []string - - Stack CallStack - // A to turn on the verbose mode, FOR DEVELOPMENT USE ONLY - debugMode bool -} - -func (r *Runtime) IsDebugMode() bool { - return r.debugMode -} - -func (r *Runtime) CurrentNS() *Namespace { - if r.currentNS == "" { - panic("current ns is not set on the runtime.") - } - - ns, ok := r.namespaces[r.currentNS] - - if !ok { - panic(fmt.Sprintf("namespace '%s' doesn't exist in the runtime.", r.currentNS)) - } - - return &ns -} - -func (r *Runtime) setCurrentNS(nsName string) bool { - _, ok := r.namespaces[nsName] - - if ok { - r.currentNS = nsName - return true - } - return false -} - -// GetNS returns a pointer to the `Namespace` specified with the given name `ns` -// on the runtime. -func (r *Runtime) GetNS(ns string) (*Namespace, bool) { - namespace, ok := r.namespaces[ns] - return &namespace, ok -} - -// CreateNS is a helper function to create a namespace and set it to be -// the current namespace of the runtime. `MakeNS` is much preferred -func (r *Runtime) CreateNS(name, source string, setAsCurrent bool) { - ns := MakeNS(r, name, source) - - if setAsCurrent { - r.currentNS = name - } - r.namespaces[name] = ns -} - -// IsQQSimplificationEnabled returns a boolean value indicating whether -// simplification of quasiquotation is enabled or not. If yes, we have -// to replace the quasiquote expanded forms with a simpler form to gain -// a better performance. -func (r *Runtime) IsQQSimplificationEnabled() bool { - // TODO: read the value of this flag from the arguments of serene - // and set the default to true - return false -} - -// nsNameToPath converts a namespace name to the filesystem equivalent path -func nsNameToPath(ns string) string { - replacer := strings.NewReplacer( - ".", "/", - // TODO: checkout the different OSs for character supports in - // the filesystem level - // "-", "_", - ) - return replacer.Replace(ns) + ".srn" -} - -// LoadNS looks up the namespace specified by the given name `ns` -// and reads the content as expressions (parse it) and returns the -// expressions. -func (r *Runtime) loadNS(ns *Symbol) (*loadedForms, IError) { - nsFile := nsNameToPath(ns.GetName()) - for _, loadPath := range r.paths { - possibleFile := path.Join(loadPath, nsFile) - - if r.debugMode { - fmt.Printf("[DEBUG] Looking for '%s' in '%s'", possibleFile, loadPath) - } - - _, err := os.Stat(possibleFile) - - if err != nil { - continue - } - - data, err := ioutil.ReadFile(possibleFile) - - if err != nil { - readError := MakeError( - r, - ns, - fmt.Sprintf("error while reading the file at %s", possibleFile), - ) - readError.WithError(err) - return nil, readError - } - - body, e := ReadString(ns.GetName(), string(data)) - if e != nil { - return nil, e - } - - return &loadedForms{possibleFile, body}, nil - } - - // TODO: Add the load paths to the error message here - return nil, MakeError(r, ns, fmt.Sprintf("Can't find the namespace '%s' in any of load paths.", ns)) -} - -func (r *Runtime) InsertNS(nsName string, ns *Namespace) { - r.namespaces[nsName] = *ns -} - -func (r *Runtime) LookupBuiltin(k string) IExpr { - builtinfn, ok := r.builtins[k] - - if ok { - return &builtinfn - } - - return nil -} - -// MakeRuntime creates a Runtime and returns a pointer to it. Any -// runtime initialization such as adding default namespaces and vice -// versa has to happen here. -func MakeRuntime(paths []string, flags map[string]bool) *Runtime { - rt := Runtime{ - namespaces: map[string]Namespace{}, - currentNS: "", - debugMode: flags["debugMode"], - paths: paths, - Stack: MakeCallStack(flags["stackDebugMode"]), - } - - rt.builtins = BUILTINS - return &rt -} diff --git a/bootstrap/pkg/core/scope.go b/bootstrap/pkg/core/scope.go deleted file mode 100644 index a91ef3e..0000000 --- a/bootstrap/pkg/core/scope.go +++ /dev/null @@ -1,107 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import "fmt" - -type IScope interface { - Lookup(rt *Runtime, k string) *Binding - Insert(k string, v IExpr, public bool) - GetNS(rt *Runtime) *Namespace - SetNS(ns *string) -} - -type Binding struct { - Value IExpr - Public bool -} - -type Scope struct { - bindings map[string]Binding - parent *Scope - ns *string -} - -func (s *Scope) Lookup(rt *Runtime, k string) *Binding { - if rt.IsDebugMode() { - fmt.Printf("[DEBUG] Looking up '%s'\n", k) - } - - v, ok := s.bindings[k] - if ok { - if rt.IsDebugMode() { - fmt.Printf("[DEBUG] Found '%s': '%s'\n", k, v.Value.String()) - } - return &v - } - - if s.parent != nil { - return s.parent.Lookup(rt, k) - } - - builtin := rt.LookupBuiltin(k) - - if builtin != nil { - if rt.IsDebugMode() { - fmt.Printf("[DEBUG] Found builtin '%s': '%s'\n", k, builtin) - } - - return &Binding{builtin, true} - } - - return nil -} - -func (s *Scope) Insert(k string, v IExpr, public bool) { - s.bindings[k] = Binding{Value: v, Public: public} -} - -func (s *Scope) GetNS(rt *Runtime) *Namespace { - if s.ns == nil { - panic("A scope with no namespace !!!!") - } - ns, ok := rt.GetNS(*s.ns) - if !ok { - panic(fmt.Sprintf("A scope with the wrong namespace! '%s'", *(s.ns))) - } - return ns -} - -func (s *Scope) SetNS(ns *string) { - s.ns = ns -} - -func MakeScope(rt *Runtime, parent *Scope, namespace *string) *Scope { - var belongsTo *string - - if parent != nil { - nsName := parent.GetNS(rt).GetName() - belongsTo = &nsName - } else if namespace == nil { - panic("When the 'parent' is nil, you have to provide the 'namespace' name.") - } else { - belongsTo = namespace - } - - return &Scope{ - parent: parent, - bindings: map[string]Binding{}, - ns: belongsTo, - } -} diff --git a/bootstrap/pkg/core/sforms.go b/bootstrap/pkg/core/sforms.go deleted file mode 100644 index 537feaa..0000000 --- a/bootstrap/pkg/core/sforms.go +++ /dev/null @@ -1,156 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - - "serene-lang.org/bootstrap/pkg/ast" -) - -// Def defines a global binding in the current namespace. The first -// arguments in `args` has to be a symbol ( none ns qualified ) and -// the second param should be the value of the binding -func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) { - // TODO: Add support for docstrings and meta - if args.Count() == 2 { - name := args.First() - - if name.GetType() != ast.Symbol { - return nil, MakeError(rt, name, "the first argument of 'def' has to be a symbol") - } - - sym := name.(*Symbol) - - valueExpr := args.Rest().First() - value, err := EvalForms(rt, scope, valueExpr) - - if err != nil { - return nil, err - } - - if value.GetType() == ast.Fn { - value.(*Function).SetName(sym.GetName()) - } - - ns := rt.CurrentNS() - ns.DefineGlobal(sym.GetName(), value, true) - return sym, nil - } - - return nil, MakeError(rt, args, "'def' form need at least 2 arguments") -} - -// Def defines a macro in the current namespace. The first -// arguments in `args` has to be a symbol ( none ns qualified ) and -// the rest of params should be the body of the macro. Unlike other -// expressions in Serene `defmacro` DOES NOT evaluate its arguments. -// That is what makes macros great -func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) { - // TODO: Add support for docstrings and meta - - if args.Count() < 2 { - return nil, MakeError(rt, args, "'defmacro' form need at least 2 arguments") - } - - name := args.Rest().First() - - if name.GetType() != ast.Symbol { - return nil, MakeError(rt, name, "the first argument of 'defmacro' has to be a symbol") - } - - sym := name.(*Symbol) - - var params IColl - body := MakeEmptyBlock() - - arguments := args.Rest().Rest().First() - - // TODO: Add vector in here - // Or any other icoll - if arguments.GetType() == ast.List { - params = arguments.(IColl) - } - - if args.Count() > 2 { - body.SetContent(args.Rest().Rest().Rest().(*List).ToSlice()) - } - - macro := MakeMacro(scope, sym.GetName(), params, body) - - ns := scope.GetNS(rt) - ns.DefineGlobal(sym.GetName(), macro, true) - - return macro, nil -} - -// Fn defines a function inside the given scope `scope` with the given `args`. -// `args` contains the argument list, docstring and body of the function. -func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) { - if args.Count() < 2 { - return nil, MakeError(rt, args, "'fn' needs at least an arguments list") - } - - var params IColl - body := MakeEmptyBlock() - - arguments := args.Rest().First() - - // TODO: Add vector in here - // Or any other icoll - if arguments.GetType() == ast.List { - params = arguments.(IColl) - } - - if args.Count() > 1 { - body.SetContent(args.Rest().Rest().(*List).ToSlice()) - } - - return MakeFunction(MakeNodeFromExpr(args.First()), scope, params, body), nil -} - -func NSForm(rt *Runtime, scope IScope, args *List) (IExpr, IError) { - if args.Count() == 1 { - return nil, MakeError(rt, args, "namespace's name is missing") - } - - name := args.Rest().First() - - if name.GetType() != ast.Symbol { - return nil, MakeError(rt, name, "the first argument to the 'ns' has to be a symbol") - } - nsName := name.(*Symbol).GetName() - - if nsName != rt.CurrentNS().GetName() { - return nil, MakeError( - rt, - args, - fmt.Sprintf("the namespace '%s' doesn't match the file name.", nsName), - ) - } - ns, ok := rt.GetNS(nsName) - - if !ok { - return nil, MakeError(rt, name, fmt.Sprintf("can't find the namespace '%s'. Is it the same as the file name?", nsName)) - } - - return ns, nil - // TODO: Handle the params like `require` and `meta` - // params := args.Rest().Rest() -} diff --git a/bootstrap/pkg/core/string.go b/bootstrap/pkg/core/string.go deleted file mode 100644 index d65c211..0000000 --- a/bootstrap/pkg/core/string.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - "strings" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type String struct { - Node - ExecutionScope - content string -} - -func (s *String) GetType() ast.NodeType { - return ast.String -} - -func (s *String) String() string { - return "\"" + s.Escape() + "\"" -} - -func (s *String) PrintToString() string { - return s.content -} - -func (s *String) ToDebugStr() string { - return fmt.Sprintf("<%s at %p>", s.content, s) -} - -func (s *String) Hash() uint32 { - bytes := []byte(s.content) - return hash.Of(append([]byte{byte(ast.String)}, bytes...)) -} - -func (s *String) Escape() string { - replacer := strings.NewReplacer( - "\n", "\\n", - "\t", "\\t", - "\r", "\\r", - "\\", "\\\\", - "\"", "\\\"", - ) - return replacer.Replace(s.content) -} - -func MakeString(n Node, s string) *String { - return &String{ - Node: n, - content: s, - } -} diff --git a/bootstrap/pkg/core/symbol.go b/bootstrap/pkg/core/symbol.go deleted file mode 100644 index 8ae2480..0000000 --- a/bootstrap/pkg/core/symbol.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "strings" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -type Symbol struct { - Node - ExecutionScope - name string - nsPart string -} - -func (s *Symbol) GetType() ast.NodeType { - return ast.Symbol -} - -func (s *Symbol) String() string { - if s.IsNSQualified() { - return s.nsPart + "/" + s.name - } - - return s.name -} - -func (s *Symbol) GetName() string { - return s.name -} - -func (s *Symbol) GetNSPart() string { - return s.nsPart -} - -func (s *Symbol) ToDebugStr() string { - return s.String() -} - -func (s *Symbol) Hash() uint32 { - // TODO: Return a combined hash of nsPart and name - return hash.Of([]byte(s.nsPart + "/" + s.name)) -} - -func (s *Symbol) IsRestable() bool { - // Weird name ? I know :D - return strings.HasPrefix(s.name, "&") -} - -func (s *Symbol) IsNSQualified() bool { - return s.nsPart != "" -} - -func MakeSymbol(n Node, s string) (*Symbol, IError) { - parts := strings.Split(s, "/") - var ( - name string - nsPart string - ) - - switch len(parts) { - case 1: - name = parts[0] - nsPart = "" - case 2: - name = parts[1] - nsPart = parts[0] - default: - return nil, MakeSyntaxErrorf(n, "can't create a symbol from '%s'. More that on '/' is illegal.", s) - } - - return &Symbol{ - Node: n, - name: name, - nsPart: nsPart, - }, nil -} diff --git a/bootstrap/pkg/core/types.go b/bootstrap/pkg/core/types.go deleted file mode 100644 index 6c7fcfa..0000000 --- a/bootstrap/pkg/core/types.go +++ /dev/null @@ -1,173 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package core - -import ( - "fmt" - - "serene-lang.org/bootstrap/pkg/ast" - "serene-lang.org/bootstrap/pkg/hash" -) - -// IRepresentable is the interface which any value that wants to have a string -// representation has to implement. Serene will use this string where ever -// it needs to present a value as string. -type IRepresentable interface { - fmt.Stringer -} - -// IPrintable is the interface which any value that wants to have a string -// representation for printing has to implement. The `print` family functions will use -// this interface to convert forms to string first and if the value doesn't -// implement this interface they will resort to `IRepresentable` -type IPrintable interface { - IRepresentable - PrintToString() string -} - -// IScopable is the interface describing how to get the execution scope of -// the value. During the evaluation of the forms in Serene we might rewrite -// the execution tree to eliminate tail calls. In order to do that we should -// be able to attach the execution scope to any form that we need to rewrite. -type IScopable interface { - - // GetExecutionScope returns an attached execution scope if there's - // any, nil otherwise. - GetExecutionScope() IScope - - // SetExecutionScope sets the given scope as the execution scope of - // the current implementor - SetExecutionScope(scope IScope) -} - -// IDebuggable is the interface designed for converting forms to a string -// form which are meant to be used as debug data -type IDebuggable interface { - ToDebugStr() string -} - -// IExpr is the most important interface in Serene which basically represents -// a VALUE in Serene. All the forms (beside special formss) has to implement -// this interface. -type IExpr interface { - ast.ILocatable - ast.ITypable - hash.IHashable - IRepresentable - IDebuggable - IScopable -} - -// TODO: Add helper functions to reach methods on Node.location. For example -// Node.location.DecStart() has to have a helper on the Node like: -// Node.DecStartLocation - -// Node struct is simply representing a Node in the AST which provides the -// functionalities required to trace the code based on the location. -type Node struct { - location ast.Location -} - -// GetLocation returns the location of the Node in the source input -func (n Node) GetLocation() *ast.Location { - return &n.location -} - -type ExecutionScope struct { - scope IScope -} - -func (e *ExecutionScope) GetExecutionScope() IScope { - return e.scope -} - -func (e *ExecutionScope) SetExecutionScope(scope IScope) { - e.scope = scope -} - -// Helper functions =========================================================== - -// changeExecutionScope sets the execution scope of all the expressions in `es` -// to the given `scope` -func changeExecutionScope(es []IExpr, scope IScope) { - for _, x := range es { - x.SetExecutionScope(scope) - } -} - -// toRepresentables converts the given collection of IExprs to an array of -// IRepresentable. Since golangs type system is weird ( if A is an interface -// that embeds interface B you []A should be usable as []B but that's not the -// case in Golang), we need this convertor helper -func toRepresentables(forms IColl) []IRepresentable { - var params []IRepresentable - - for _, x := range forms.ToSlice() { - params = append(params, x) - } - - return params -} - -// TODO: I don't like the current interface and signatures of these -// 'Make*Node*' functions. Refactor them to have the same interface -// For instance if we need to return a pointer to a Node all of them -// has to return a pointer not just one of them. The return type -// should be predictable - -// MakeNodeFromLocation creates a new Node for the given Location `loc` -func MakeNodeFromLocation(loc *ast.Location) Node { - return Node{ - location: *loc, - } -} - -// MakeNodeFromExpr creates a new Node from the given `ILocatable`. -// We use the Node to pass it to other IExpr constructors to -// keep the reference to the original form in the input string -func MakeNodeFromExpr(e ast.ILocatable) Node { - return MakeNodeFromLocation(e.GetLocation()) -} - -// MakeNodeFromExprs creates a new Node from the given slice of `IExpr`s. -// We use the Node to pass it to other IExpr constructors to -// keep the reference to the original form in the input string -func MakeNodeFromExprs(es []IExpr) *Node { - if len(es) == 0 { - return nil - } - - firstLoc := es[0].GetLocation() - endLoc := es[len(es)-1].GetLocation() - loc := ast.MakeLocation(firstLoc.GetSource(), firstLoc.GetStart(), endLoc.GetEnd()) - n := MakeNodeFromLocation(loc) - return &n -} - -// MakeNode creates a new Node in the the given `input` that points to a -// range of characters starting from the `start` till the `end`. -func MakeNode(input *ast.Source, start, end int) Node { - return MakeNodeFromLocation(ast.MakeLocation(input, start, end)) -} - -// MakeSinglePointNode creates a not the points to a single char in the -// input -func MakeSinglePointNode(input *ast.Source, point int) Node { - return MakeNode(input, point, point) -} diff --git a/bootstrap/pkg/dl/dl.go b/bootstrap/pkg/dl/dl.go deleted file mode 100644 index 24d185c..0000000 --- a/bootstrap/pkg/dl/dl.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -// Package dl provides the necessary interface to interact with share -// libraries -package dl - -/* -#cgo linux LDFLAGS: -ldl -#cgo pkg-config: libffi -#include -#include -#include -#include - -#include - -static uintptr_t openShareLib(const char* path, char** err) { - void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL); - if (h == NULL) { - *err = (char*)dlerror(); - } - return (uintptr_t)h; -} - -static void* shareLibLookup(uintptr_t h, const char* name, char** err) { - void* r = dlsym((void*)h, name); - if (r == NULL) { - *err = (char*)dlerror(); - } - return r; -} -*/ - -// import "C" -// import ( -// "fmt" -// "unsafe" -// ) - -// type SharedLib struct{} - -// func Open(libPath string) (*SharedLib, error) { -// cPath := make([]byte, C.PATH_MAX+1) -// cRelName := make([]byte, len(libPath)+1) -// copy(cRelName, libPath) - -// // If the given libPath exists, fill the cPath with the absolute path -// // to the file (it follows symlinks). -// if C.realpath( -// (*C.char)(unsafe.Pointer(&cRelName[0])), -// (*C.char)(unsafe.Pointer(&cPath[0]))) == nil { -// return nil, fmt.Errorf("can't find the shared library '%s'", libPath) -// } - -// var cErr *C.char -// h := C.openShareLib((*C.char)(unsafe.Pointer(&cPath[0])), &cErr) - -// if h == 0 { -// // lock.Unlock() -// return nil, fmt.Errorf("failed to open shared library: '%s' due to: '%s'", libPath, C.GoString(cErr)) -// } - -// initTask := C.shareLibLookup(h, C.CString("bar"), &cErr) - -// fmt.Printf(">> %p \n", initTask) - -// return &SharedLib{}, nil -// } diff --git a/bootstrap/pkg/errors/errors.go b/bootstrap/pkg/errors/errors.go deleted file mode 100644 index 61c5786..0000000 --- a/bootstrap/pkg/errors/errors.go +++ /dev/null @@ -1,71 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -// Package errors is dedicated to hold the error numbers and description of -// each knowen error in Serene -package errors - -type Errno uint - -const ( - E0000 Errno = iota // THE DESCRIPTION IS NOT SET - E0001 - E0002 - E0003 -) - -func (e Errno) String() string { - return [...]string{ - "E0000", - "E0001", - "E0002", - "E0003", - }[e] -} - -var ErrorsDescription map[Errno]string = map[Errno]string{ - E0000: `Can't find any description for this error.`, - E0001: ` -Namespaces are fundamental units in Serene. Each file has to start with -a namespace declaration with a name that matches the path of the file. - -For example imagine haivng a file with the following path -'/home/user/xyz/src/example/abc.srn' and '/home/user/xyz/src' is -in the load path. The namespace path to the file would be 'example.abc' so -that file has to contain a 'ns' form as the first expression with -'example.abc' as the name just like: - -(ns example.abc) -...rest of the file... - -Since comments are not expressions it's ok to start a file by comments -followed by the 'ns' form`, - - E0002: ` -Functions expect a certain number of argument. The number of arguments -that you're passing to the function doesn't match with it's signature. -To fix the problem double check the function signature and make sure -that you're passing the correct number of arguments to it`, - E0003: ` -Do you have a typo ? Or did you forget to define a symbol that you're trying -to evaluate ? - -This error happens when the symbol that you're trying to evaluate is not -associated with any value in the current scope tree. -`, -} diff --git a/bootstrap/pkg/hash/hash.go b/bootstrap/pkg/hash/hash.go deleted file mode 100644 index 840da7b..0000000 --- a/bootstrap/pkg/hash/hash.go +++ /dev/null @@ -1,48 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -// Package hash provides the hashing functionality -package hash - -import "hash/crc32" - -var hashTable *crc32.Table = crc32.MakeTable(crc32.Castagnoli) - -// IHashable is the interface types which allows expressions to have a hash -// value that doesn't change through out their life time. The origin -// of each expression can be checked by comparing their hashes. Basically -// two expressions with the same hash consider to be the same. -type IHashable interface { - // Returns a 32 bit hash of the the entity which implements it. - // The hash should be constant to the life time of the implementor. - Hash() uint32 -} - -func Of(in []byte) uint32 { - return crc32.Checksum(in, hashTable) -} - -// CombineHashes combines two hashes and return a new one -func CombineHashes(hash1, hash2 uint32) uint32 { - // This way of composing hashes is used in libboost and almost everyone - // is using it. The 0x9e3779b9 is the integral part of the Golden Ratio's - // fractional part 0.61803398875… (sqrt(5)-1)/2, multiplied by 2^32. - // For more info: https://lkml.org/lkml/2016/4/29/838 - hash1 ^= hash2 + 0x9e3779b9 + (hash1 << 6) + (hash2 >> 2) - return hash1 -} diff --git a/bootstrap/serene.go b/bootstrap/serene.go deleted file mode 100644 index 3824be0..0000000 --- a/bootstrap/serene.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - Serene --- Yet an other Lisp - -Copyright (c) 2020 Sameer Rahmani - -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. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -package main - -import "serene-lang.org/bootstrap/cmd" - -func main() { - cmd.Execute() -} diff --git a/examples/hello_world/hello_world.srn b/examples/hello_world/hello_world.srn deleted file mode 100644 index ed81ff9..0000000 --- a/examples/hello_world/hello_world.srn +++ /dev/null @@ -1,4 +0,0 @@ -(print 4) -(print -43) -(print 3.4) -(println asd) diff --git a/site/archetypes/default.md b/resources/site/archetypes/default.md similarity index 100% rename from site/archetypes/default.md rename to resources/site/archetypes/default.md diff --git a/site/config.toml b/resources/site/config.toml similarity index 100% rename from site/config.toml rename to resources/site/config.toml diff --git a/site/content/posts/my-first-post.md b/resources/site/content/posts/my-first-post.md similarity index 100% rename from site/content/posts/my-first-post.md rename to resources/site/content/posts/my-first-post.md diff --git a/site/scripts/deploy.sh b/resources/site/scripts/deploy.sh similarity index 100% rename from site/scripts/deploy.sh rename to resources/site/scripts/deploy.sh diff --git a/site/themes/anybodyhome/LICENSE.md b/resources/site/themes/anybodyhome/LICENSE.md similarity index 100% rename from site/themes/anybodyhome/LICENSE.md rename to resources/site/themes/anybodyhome/LICENSE.md diff --git a/site/themes/anybodyhome/README.md b/resources/site/themes/anybodyhome/README.md similarity index 100% rename from site/themes/anybodyhome/README.md rename to resources/site/themes/anybodyhome/README.md diff --git a/site/themes/anybodyhome/archetypes/default.md b/resources/site/themes/anybodyhome/archetypes/default.md similarity index 100% rename from site/themes/anybodyhome/archetypes/default.md rename to resources/site/themes/anybodyhome/archetypes/default.md diff --git a/site/themes/anybodyhome/layouts/404.html b/resources/site/themes/anybodyhome/layouts/404.html similarity index 100% rename from site/themes/anybodyhome/layouts/404.html rename to resources/site/themes/anybodyhome/layouts/404.html diff --git a/site/themes/anybodyhome/layouts/_default/paginator.html b/resources/site/themes/anybodyhome/layouts/_default/paginator.html similarity index 100% rename from site/themes/anybodyhome/layouts/_default/paginator.html rename to resources/site/themes/anybodyhome/layouts/_default/paginator.html diff --git a/site/themes/anybodyhome/layouts/_default/single.html b/resources/site/themes/anybodyhome/layouts/_default/single.html similarity index 100% rename from site/themes/anybodyhome/layouts/_default/single.html rename to resources/site/themes/anybodyhome/layouts/_default/single.html diff --git a/site/themes/anybodyhome/layouts/_default/summary.html b/resources/site/themes/anybodyhome/layouts/_default/summary.html similarity index 100% rename from site/themes/anybodyhome/layouts/_default/summary.html rename to resources/site/themes/anybodyhome/layouts/_default/summary.html diff --git a/site/themes/anybodyhome/layouts/index.html b/resources/site/themes/anybodyhome/layouts/index.html similarity index 100% rename from site/themes/anybodyhome/layouts/index.html rename to resources/site/themes/anybodyhome/layouts/index.html diff --git a/site/themes/anybodyhome/layouts/partials/footer.html b/resources/site/themes/anybodyhome/layouts/partials/footer.html similarity index 100% rename from site/themes/anybodyhome/layouts/partials/footer.html rename to resources/site/themes/anybodyhome/layouts/partials/footer.html diff --git a/site/themes/anybodyhome/layouts/partials/head.html b/resources/site/themes/anybodyhome/layouts/partials/head.html similarity index 100% rename from site/themes/anybodyhome/layouts/partials/head.html rename to resources/site/themes/anybodyhome/layouts/partials/head.html diff --git a/site/themes/anybodyhome/layouts/partials/header.html b/resources/site/themes/anybodyhome/layouts/partials/header.html similarity index 100% rename from site/themes/anybodyhome/layouts/partials/header.html rename to resources/site/themes/anybodyhome/layouts/partials/header.html diff --git a/site/themes/anybodyhome/layouts/partials/paginator.html b/resources/site/themes/anybodyhome/layouts/partials/paginator.html similarity index 100% rename from site/themes/anybodyhome/layouts/partials/paginator.html rename to resources/site/themes/anybodyhome/layouts/partials/paginator.html diff --git a/site/themes/anybodyhome/layouts/summary.html b/resources/site/themes/anybodyhome/layouts/summary.html similarity index 100% rename from site/themes/anybodyhome/layouts/summary.html rename to resources/site/themes/anybodyhome/layouts/summary.html diff --git a/site/themes/anybodyhome/static/css/styles.css b/resources/site/themes/anybodyhome/static/css/styles.css similarity index 100% rename from site/themes/anybodyhome/static/css/styles.css rename to resources/site/themes/anybodyhome/static/css/styles.css diff --git a/site/themes/anybodyhome/theme.toml b/resources/site/themes/anybodyhome/theme.toml similarity index 100% rename from site/themes/anybodyhome/theme.toml rename to resources/site/themes/anybodyhome/theme.toml diff --git a/cmake/cotire.cmake b/scripts/cmake/cotire.cmake similarity index 100% rename from cmake/cotire.cmake rename to scripts/cmake/cotire.cmake