Repository: AvicennaJr/Nuru Branch: main Commit: 78076bbdc2b5 Files: 166 Total size: 399.8 KB Directory structure: gitextract_yf7p31zd/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yml │ │ └── feature-request.yml │ ├── PULL_REQUEST_TEMPLATE │ └── workflows/ │ ├── build.yml │ └── tests.yml ├── .gitignore ├── .goreleaser.yaml ├── ABOUT.md ├── LICENSE ├── Makefile ├── README.md ├── ast/ │ ├── ast.go │ └── ast_test.go ├── evaluator/ │ ├── assign.go │ ├── assignEqual.go │ ├── at.go │ ├── bang.go │ ├── block.go │ ├── builtins.go │ ├── builtins_common.go │ ├── builtins_wasm.go │ ├── call.go │ ├── dict.go │ ├── error_handler.go │ ├── evaluator.go │ ├── evaluator_test.go │ ├── forin.go │ ├── function.go │ ├── identifier.go │ ├── if.go │ ├── import.go │ ├── in.go │ ├── index.go │ ├── infix.go │ ├── method.go │ ├── package.go │ ├── postfix.go │ ├── prefix.go │ ├── property.go │ ├── switch.go │ ├── type.go │ └── while.go ├── examples/ │ ├── Astart.nr │ ├── example.nr │ ├── perceptron.nr │ ├── reduce.nr │ ├── sarufi.nr │ ├── sorting_algorithm.nr │ └── sudoku_solver.nr ├── extensions/ │ ├── README.md │ ├── vim/ │ │ └── syntax/ │ │ └── nuru.vim │ └── vscode/ │ ├── CHANGELOG.md │ └── README.md ├── go.mod ├── go.sum ├── gotest ├── lexer/ │ ├── lexer.go │ └── lexer_test.go ├── main.go ├── main_wasm.go ├── module/ │ ├── hisabati.go │ ├── json.go │ ├── module.go │ ├── net.go │ ├── os.go │ └── time.go ├── object/ │ ├── array.go │ ├── at.go │ ├── bool.go │ ├── break.go │ ├── builtin.go │ ├── byte.go │ ├── continue.go │ ├── dict.go │ ├── environment.go │ ├── error.go │ ├── error_wasm.go │ ├── file.go │ ├── float.go │ ├── function.go │ ├── instance.go │ ├── integer.go │ ├── module.go │ ├── null.go │ ├── object.go │ ├── object_test.go │ ├── package.go │ ├── return.go │ ├── strings.go │ └── time.go ├── parser/ │ ├── arrays.go │ ├── assignEqual.go │ ├── assignment.go │ ├── at.go │ ├── boolean.go │ ├── break.go │ ├── continue.go │ ├── dict.go │ ├── dot.go │ ├── float.go │ ├── for.go │ ├── function.go │ ├── identifier.go │ ├── if.go │ ├── import.go │ ├── index.go │ ├── integer.go │ ├── null.go │ ├── package.go │ ├── parser.go │ ├── parser_test.go │ ├── statements.go │ ├── string.go │ ├── switch.go │ └── while.go ├── repl/ │ ├── docs/ │ │ ├── en/ │ │ │ ├── README.md │ │ │ ├── arrays.md │ │ │ ├── bool.md │ │ │ ├── builtins.md │ │ │ ├── comments.md │ │ │ ├── dictionaries.md │ │ │ ├── files.md │ │ │ ├── for.md │ │ │ ├── function.md │ │ │ ├── hisabati.md │ │ │ ├── identifiers.md │ │ │ ├── ifStatements.md │ │ │ ├── json.md │ │ │ ├── keywords.md │ │ │ ├── net.md │ │ │ ├── null.md │ │ │ ├── numbers.md │ │ │ ├── operators.md │ │ │ ├── packages.md │ │ │ ├── range.md │ │ │ ├── strings.md │ │ │ ├── switch.md │ │ │ ├── time.md │ │ │ └── while.md │ │ └── sw/ │ │ ├── README.md │ │ ├── arrays.md │ │ ├── bools.md │ │ ├── builtins.md │ │ ├── dictionaries.md │ │ ├── for.md │ │ ├── functions.md │ │ ├── identifiers.md │ │ ├── if.md │ │ ├── keywords.md │ │ ├── maoni.md │ │ ├── null.md │ │ ├── numbers.md │ │ ├── operators.md │ │ ├── range.md │ │ ├── strings.md │ │ ├── switch.md │ │ └── while.md │ ├── docs.go │ └── repl.go ├── sh/ │ └── install.sh ├── styles/ │ └── styles.go ├── third_party/ │ └── math/ │ ├── README.md │ ├── hesabu.nr │ └── test.nr ├── token/ │ └── token.go └── upx ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.yml ================================================ name: 🐛 Bug Report description: Report a bug title: (bug report summary) labels: Bug body: - type: textarea id: description attributes: label: Describe the bug description: What is the problem? A clear and concise description of the bug. validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: What did you expect to happen? validations: required: true - type: textarea id: current attributes: label: Current Behavior description: | What actually happened? Please include full errors, uncaught exceptions, stack traces, and relevant logs. If service/functions responses are relevant, please include wire logs. validations: required: true - type: textarea id: reproduction attributes: label: Reproduction Steps description: | Provide a self-contained, concise snippet of code that can be used to reproduce the issue. For more complex issues provide a repo with the smallest sample that reproduces the bug. Avoid including business logic or unrelated code, it makes diagnosis more difficult. The code sample should be an SSCCE. See http://sscce.org/ for details. In short, please provide a code sample that we can copy/paste, run and reproduce. validations: required: true - type: textarea id: solution attributes: label: Possible Solution description: Suggest a fix/reason for the bug validations: required: false - type: textarea id: context attributes: label: Additional Information/Context description: | Anything else that might be relevant for troubleshooting this bug. Providing context helps us come up with a solution that is most useful in the real world. validations: required: false - type: input id: version attributes: label: Nuru version description: | Please make sure to use the latest version of Nuru before reporting any issues as it may have already been fixed. validations: required: true - type: textarea id: environment attributes: label: Environment details (OS name and version, etc.) description: Your operating system (Windows, Linux, Android or MacOS) validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.yml ================================================ name: 🚀 Feature Request description: Suggest an idea for this project title: (feature request summary) labels: Feature Request body: - type: textarea id: description attributes: label: Describe the feature description: A clear and concise description of the feature you are proposing. validations: required: true - type: textarea id: use-case attributes: label: Use Case description: | Why do you need this feature? For example: "I'm always frustrated when..." validations: required: true - type: textarea id: solution attributes: label: Proposed Solution description: Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. validations: required: false - type: textarea id: other attributes: label: Other Information description: Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. validations: required: false - type: checkboxes id: ack attributes: label: Acknowledgements options: - label: I may be able to implement this feature request required: false - label: This feature might incur a breaking change required: false - type: input id: version attributes: label: Version used description: Please provide the version of the repository or tool you are using. validations: required: true - type: textarea id: environment attributes: label: Environment details (OS name and version, etc.) description: Your operating system (Linux, Windows, Android or Mac) validations: required: true ================================================ FILE: .github/PULL_REQUEST_TEMPLATE ================================================ ================================================ FILE: .github/workflows/build.yml ================================================ name: Go on: push: tags: - "*" jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v3 with: go-version: 1.21 id: go - name: Test run: go mod tidy && make test - name: Run GoReleaser uses: goreleaser/goreleaser-action@v5 with: distribution: goreleaser version: latest args: release --clean env: GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }} ================================================ FILE: .github/workflows/tests.yml ================================================ name: Go on: push: branches: [ main, dev ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v3 with: go-version: 1.21 - name: Test run: go mod tidy && make test ================================================ FILE: .gitignore ================================================ .DS_Store *.[56789ao] *.a[56789o] *.so *.pyc ._* .nfs.* [56789a].out *~ *.orig *.rej *.exe .*.swp core *.cgo*.go *.cgo*.c _cgo_* _obj _test _testmain.go /VERSION.cache /bin/ /build.out /doc/articles/wiki/*.bin /goinstall.log /last-change /misc/cgo/life/run.out /misc/cgo/stdio/run.out /misc/cgo/testso/main /pkg/ /src/*.*/ /src/cmd/cgo/zdefaultcc.go /src/cmd/dist/dist /src/cmd/go/internal/cfg/zdefaultcc.go /src/cmd/go/internal/cfg/zosarch.go /src/cmd/internal/objabi/zbootstrap.go /src/go/build/zcgo.go /src/go/doc/headscan /src/runtime/internal/sys/zversion.go /src/unicode/maketables /test.out /test/garbage/*.out /test/pass.out /test/run.out /test/times.out #Personal testbinaries/ tests_random/ nuru Notes.md tutorials/en/* config.json *local* # For Nuru executables /nuru /Nuru dist/ *.wasm ================================================ FILE: .goreleaser.yaml ================================================ project_name: nuru before: hooks: - go mod tidy - go generate ./... builds: - env: - CGO_ENABLED=0 goos: - linux - windows - darwin - android ldflags: - "-s -w" ignore: - goos: android goarch: 386 archives: - format: tar.gz name_template: >- nuru_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}amd64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} format_overrides: - goos: windows format: zip changelog: sort: asc filters: exclude: - "^docs:" - "^test:" nfpms: - maintainer: "AvicennaJr" homepage: "https://nuruprogramming.org" description: "Nuru is a programming language built from the ground up" formats: - deb file_name_template: "{{ .ProjectName }}.{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" ================================================ FILE: ABOUT.md ================================================ # NURU PROGRAMMING LANGUAGE This page intends to show the origins of Nuru, its purpose, what it can be used for, what it should not be used for and the potential future of the programming language. ## Background This language is the direct child of a programming language called "Monkey Language" made by Thorston Ball. He wrote a book titled "Writing An Interpreter In Go" and the core of the language is based on his book. With the knowledge I gained from his book, I saw an opportunity to write a Swahili Programming Language, which would not just be a translation of an already existing one, but an actual standalone interpreted language that can be built from the ground up, where everything can be customized from the syntax, its abilities, its standard library and more. Now one may wonder, why a new programming language when there are many other much better programming languages in existence. The answer is, it is a Swahili programming language. And this is significant for two reasons: - Many are unable to learn programming due to the language barrier. Almost all programming languages in existence are in English and thus a non English speaker would have to first learn English before they can learn a programming language. This makes the effort twice as difficult and Nuru, a native Swahili programming language, intends to make the process of learning programming languages a bit easier. - Secondly, even if no one does use this programming language, then at least we can say "We do have a fully functional Swahili programming language", and as a person who grew up in Tanzania, this does give me pride. ## Purpose Nuru does not intend to replace any existing programming language. In fact, it does not intend to be used in production at all. Nuru intends to be an educational programming language, a programming language that will make it easy for anyone to get into the world of programming without knowing English. It intends to be simple enough to be taught to kids in primary and highschool and allow them to build interesting tools with it. Nuru also hopes to be used by hobbyists and experienced programmers, where by they will find it easy to write scripts in Nuru that will help solve their various tasks or build interesting projects with it. As a matter of fact, someone already made a sudoku solver in Nuru. While being simple it also intends to be fully functional. Other than having all the core features required by a programming language, Nuru also has an extensive standard library that will make performing common tasks much easier. Thus, it intends to bring the best of both worlds, simple to use with a lot of features. ## Philosophy Nuru's philosophy is to keep things simple. Everything in Nuru should be consistent and intuitive, from its syntax to the keywords used. On the matter of keywords, Nuru intends to provide keywords that are simple and intuitive that can easily explain what the function or library is for. A more detailed guide on the Nuru's syntax and the proper way of writing Nuru will be provided in the near future. Nuru is also community driven. We listen to our community and do our best to implement on the feedback we get from them. ## Where Not To Use Nuru Nuru's performance is worse than python. It has been authored by someone with very limited knowledge in programming. Thus, it is advised to never use Nuruin production code where by any kind of mistakes are critical. Nuru is still very immature and should only be used for educational and hobby projects. ## Challenges The main challenge we have in Nuru is in naming keywords. Since this is something new, there are lack of words that fully describe common programming words. However, we do intend to try our best to select the best keywords, and we often consult with our community when choosing a word. ## Future Of Nuru It is still too early to know how Nuru will evolve, or the way in which the community will use this language. However, what is certain is the core developers will do their best to provide a language that will be enjoyable to learn and code in. We listen to our community and hopefully we will soon have a large number of developers contributing to the language. We also hope to see games and GUI applications written in Nuru in the near future... God willing. ## Final Words I am very grateful to the reception of this project, we now have over 150+ downloads and its barely been a month. We hope to fulfill all your expectations and provide something that you will all enjoy to use. We also ask you to bear with us when we make mistakes and correct and advise us on areas where we can do better. I am also grateful to Thorston for writing such an amazing book, and I would recommend anyone who'd want to learn how programming languages work to read his book. And finally, I thank Allah for granting us the ability to learn and giving me the ability to make such a project. ================================================ FILE: LICENSE ================================================ 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. ================================================ FILE: Makefile ================================================ VERSION=0.5.1 build_linux: @echo 'building linux binary...' env GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o nuru @echo 'shrinking binary...' ./upx --brute nuru @echo 'zipping build....' tar -zcvf nuru_linux_amd64_v${VERSION}.tar.gz nuru @echo 'cleaning up...' rm nuru build_windows: @echo 'building windows executable...' env GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o nuru_windows_amd64_v${VERSION}.exe @echo 'shrinking build...' ./upx --brute nuru_windows_amd64_v${VERSION}.exe build_mac: @echo 'building mac binary...' env GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o nuru @echo 'shrinking binary...' ./upx --brute nuru @echo 'zipping build...' tar -zcvf nuru_mac_amd64_v${VERSION}.tar.gz nuru @echo 'cleaning up...' rm nuru build_android: @echo 'building android binary' env GOOS=android GOARCH=arm64 go build -ldflags="-s -w" -o nuru @echo 'zipping build...' tar -zcvf nuru_android_arm64_v${VERSION}.tar.gz nuru @echo 'cleaning up...' rm nuru build_wasm: @echo 'building wasm binary' GOOS=js GOARCH=wasm go build -o nuru.wasm @echo 'zipping build...' tar -zcvf nuru_wasm_v${VERSION}.tar.gz nuru.wasm @echo 'cleaning up...' rm nuru.wasm build_test: go build -ldflags="-s -w" -o nuru dependencies: @echo 'checking dependencies...' go mod tidy test: @echo -e '\nTesting Lexer...' @./gotest --format testname ./lexer/ @echo -e '\nTesting Parser...' @./gotest --format testname ./parser/ @echo -e '\nTesting AST...' @./gotest --format testname ./ast/ @echo -e '\nTesting Object...' @./gotest --format testname ./object/ @echo -e '\nTesting Evaluator...' @./gotest --format testname ./evaluator/ clean: go clean ================================================ FILE: README.md ================================================

NURU🔥PROGRAMMING🔥LANGUAGE

Nuru Programming Language Nuru Programming Language Nuru Programming Language
Nuru Programming Language Nuru Programming Language Nuru Programming Language
Nuru Programming Language

A Swahili Programming Language of its kind built from the ground up. ## Installation To get started download the executables from the release page or follow the instructions for your device below: ### Linux - Download the binary: ``` curl -O -L https://github.com/NuruProgramming/Nuru/releases/download/v0.5.18/nuru_Linux_amd64.tar.gz ``` - Extract the file to make global available: ``` sudo tar -C /usr/local/bin -xzvf nuru_Linux_amd64.tar.gz ``` - Confirm installation with: ``` nuru -v ``` ### MacOs ( Apple silicon Mac ) - Download the binary: - For apple silicon mac use: ``` curl -O -L https://github.com/NuruProgramming/Nuru/releases/download/v0.5.18/nuru_Darwin_arm64.tar.gz ``` - For apple intel mac use: ``` curl -O -L https://github.com/NuruProgramming/Nuru/releases/download/v0.5.18/nuru_Darwin_amd64.tar.gz ``` - Extract the file to make global available: - For apple silicon mac use: ``` sudo tar -C /usr/local/bin -xzvf nuru_Darwin_arm64.tar.gz ``` - For apple intel mac use: ``` sudo tar -C /usr/local/bin -xzvf nuru_Darwin_amd64.tar.gz ``` - Confirm installation with: ``` nuru -v ``` ### Android (Termux) To install Nuru on your Android device using Termux, follow these steps: 1. **Ensure Termux is installed**: - You can download and install [Termux](https://f-droid.org/en/packages/com.termux/). 2. **Create the target directory**: ```bash mkdir -p /data/data/com.termux/files/usr/share/nuru ``` 3. **Download the Nuru package**: ```bash curl -O -L https://github.com/NuruProgramming/Nuru/releases/download/v0.5.18/nuru_Android_arm64.tar.gz ``` 4. **Extract the files to the target directory**: ```bash tar -xzvf nuru_Android_arm64.tar.gz -C /data/data/com.termux/files/usr/share/nuru ``` 5. **Set up an alias for easy access**: ```bash echo "alias nuru='/data/data/com.termux/files/usr/share/nuru/nuru'" >> ~/.bashrc ``` 6. **Reload the .bashrc file to apply the alias**: ```bash source ~/.bashrc ``` 7. **Verify the installation**: ```bash nuru -v ``` For a more streamlined installation, you can use the following one-liner: ```bash curl -O -L https://github.com/NuruProgramming/Nuru/releases/download/v0.5.18/nuru_Android_arm64.tar.gz && mkdir -p /data/data/com.termux/files/usr/share/nuru && tar -xzvf nuru_Android_arm64.tar.gz -C /data/data/com.termux/files/usr/share/nuru && echo "alias nuru='/data/data/com.termux/files/usr/share/nuru/nuru'" >> ~/.bashrc && source ~/.bashrc && echo "Installation complete.." ``` ### Windows - Executable: - Download the Nuru zip file [Here](https://github.com/NuruProgramming/Nuru/releases/download/v0.5.18/nuru_Windows_amd64.zip) - Unzip to get the executable - Double click the executable - Nuru Installer > Coming Soon ### Building From Source - Make sure you have golang installed (atleast 1.19.0 and above) - Run the following command: ``` go build -o nuru . ``` - Copy nuru binary to path destination ~/go/bin ``` cp nuru ~/go/bin ``` - Confirm installtion with: ``` nuru -v ``` ## Syntax At A Glance **NOTE** > There is a more detailed documentation of the language [here](https://nuruprogramming.org). Nuru, although still in its early stage, intends to be a fully functional programming language, and thus it has been baked with many features. ### Defining A Variable You can define variables like this: ``` x = 2; y = 3; andika(x*y) // output is 6 ``` You can also use the `fanya` keyword to define a variabe: ``` fanya x = 3 ``` **Note that `fanya` keyword is OPTIONAL** ### Comments Nuru supports both single line and multiple line comments as shown below: ``` // Single line comment /* Multiple Line Comment */ ``` ### Arithmetic Operations For now Nuru supports `+`, `-`, `/`, `*` and `%`. Nuru also provides precedence of operations using the BODMAS rule: ``` 2 + 2 * 3 // output = 8 2 * (2 + 3) // output = 10 ``` ### Types Nuru has the following types: | Type | Syntax | Comments | | ------ | ---------------------------------- | ------------------------------------------------------- | | BOOL | `kweli sikweli` | kweli == true, sikweli == false | | INT | `1, 100, 342, -4` | These are signed 64 bit integers | | FLOAT | `2.3, 4.5. 100.8094` | Signed 64 bit floats | | STRING | `"" "mambo" "habari yako"` | They can be in double `"` or single `'` quotes | | ARRAY | `[] [1, 2, 3] [1, "moja", kweli]` | Arrays can hold any types | | DICT | `{} {"a": 3, 1: "moja", kweli: 2}` | Keys can be int, string or bool. Values can be anything | | NULL | `tupu` | These are nil objects | ### Functions This is how you define a function in Nuru: ``` jumlisha = unda(x, y) { rudisha x + y } andika(jumlisha(3,4)) ``` Nuru also supports recursion: ``` fibo = unda(x) { kama (x == 0) { rudisha 0; } au kama (x == 1) { rudisha 1; } sivyo { rudisha fibo(x - 1) + fibo(x - 2); } } ``` ### If Statements Nuru supports if, elif and else statements with keywords `kama`, `au kama` and `sivyo` respectively: ``` kama (2<1) { andika("Mbili ni ndogo kuliko moja") } au kama (3 < 1) { andika ("Tatu ni ndogo kuliko moja") } sivyo { andika("Moja ni ndogo") } ``` ### While Loops Nuru's while loop syntax is as follows: ``` i = 10 wakati (i > 0) { andika(i) i-- } ``` ### Arrays This is how you initiliaze and perform other array operations in Nuru: ``` arr = [] // To add elements sukuma(arr, 2) andika(arr) // output = [2] // Add two Arrays arr2 = [1,2,3,4] arr3 = arr1 + arr2 andika(arr3) // output = [2,1,2,3,4] // reassign value arr3[0] = 0 andika[arr3] // output = [0,1,2,3,4] // get specific item andika(arr[3]) // output = 3 ``` ### Dictionaries Nuru also supports dictionaries and you can do a lot with them as follows: ``` mtu = {"jina": "Mojo", "kabila": "Mnyakusa"} // get value from key andika(mtu["jina"]) // output = Mojo andika(mtu["kabila"]); // output = Mnyakusa // You can reassign values mtu["jina"] = "Avicenna" andika(mtu["jina"]) // output = Avicenna // You can also add new values like this: mtu["anapoishi"] = "Dar Es Salaam" andika(mtu) // output = {"jina": "Avicenna", "kabila": "Mnyakusa", "anapoishi": "Dar Es Salaam"} // You can also add two Dictionaries kazi = {"kazi": "jambazi"} mtu = mtu + kazi andika(mtu) // output = {"jina": "Avicenna", "kabila": "Mnyakusa", "anapoishi": "Dar Es Salaam", "kazi": "jambazi"} ``` ### For Loops These can iterate over strings, arrays and dictionaries: ``` kwa i ktk "habari" { andika(i) } /* //output h a b a r i */ ``` ### Getting Input From User In Nuru you can get input from users using the `jaza()` keyword as follows: ``` jina = jaza("Unaitwa nani? ") // will prompt for input andika("Habari yako " + jina) ``` ## How To Run ### Using The Intepreter: You can enter the intepreter by simply running the `nuru` command: ``` nuru >>> andika("karibu") karibu >>> 2 + 2 4 ``` Kindly Note that everything should be placed in a single line. Here's an example: ``` >>> kama (x > y) {andika("X ni kubwa")} sivyo {andika("Y ni kubwa")} ``` ### Running From File To run a Nuru script, write the `nuru` command followed by the name of the file with a `.nr` or `.sw` extension: ``` nuru myFile.nr ``` ## Issues Kindly open an [Issue](https://github.com/NuruProgramming/Nuru/issues) to make suggestions and anything else. ## Contributions ### Documentation There are documentations for two languages, English and Kiswahili, which are both under the `docs` folder. All files are written in markdown. Feel free to contribute by making a pull request. ### Code Clone the repo, hack it, make sure all tests are passing then submit a pull request. > Make sure ALL tests are passing before making a pull request. You can confirm with running `make test` ## Community Nuru has a passionate community, join us on [Telegram](https://t.me/NuruProgrammingChat) ## License [MIT](http://opensource.org/licenses/MIT) ## Authors Nuru Programming Language has been authored and being actively maintained by [Avicenna](https://github.com/AvicennaJr) ================================================ FILE: ast/ast.go ================================================ package ast import ( "bytes" "strings" "github.com/NuruProgramming/Nuru/token" ) type Node interface { TokenLiteral() string String() string } type Statement interface { Node statementNode() } type Expression interface { Node expressionNode() } type Program struct { Statements []Statement } func (p *Program) TokenLiteral() string { if len(p.Statements) > 0 { return p.Statements[0].TokenLiteral() } else { return "" } } func (p *Program) String() string { var out bytes.Buffer for _, s := range p.Statements { out.WriteString(s.String()) } return out.String() } type LetStatement struct { Token token.Token Name *Identifier Value Expression } func (ls *LetStatement) statementNode() {} func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal } func (ls *LetStatement) String() string { var out bytes.Buffer out.WriteString(ls.TokenLiteral() + " ") out.WriteString(ls.Name.String()) out.WriteString(" = ") if ls.Value != nil { out.WriteString(ls.Value.String()) } out.WriteString(";") return out.String() } type Identifier struct { Token token.Token Value string } func (i *Identifier) expressionNode() {} func (i *Identifier) TokenLiteral() string { return i.Token.Literal } func (i *Identifier) String() string { return i.Value } type ReturnStatement struct { Token token.Token ReturnValue Expression } func (rs *ReturnStatement) statementNode() {} func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Literal } func (rs *ReturnStatement) String() string { var out bytes.Buffer out.WriteString(rs.TokenLiteral() + " ") if rs.ReturnValue != nil { out.WriteString(rs.ReturnValue.String()) } out.WriteString(";") return out.String() } type ExpressionStatement struct { Token token.Token Expression Expression } func (es *ExpressionStatement) statementNode() {} func (es *ExpressionStatement) TokenLiteral() string { return es.Token.Literal } func (es *ExpressionStatement) String() string { if es.Expression != nil { return es.Expression.String() } return "" } type IntegerLiteral struct { Token token.Token Value int64 } func (il *IntegerLiteral) expressionNode() {} func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal } func (il *IntegerLiteral) String() string { return il.Token.Literal } type PrefixExpression struct { Token token.Token Operator string Right Expression } func (pe *PrefixExpression) expressionNode() {} func (pe *PrefixExpression) TokenLiteral() string { return pe.Token.Literal } func (pe *PrefixExpression) String() string { var out bytes.Buffer out.WriteString("(") out.WriteString(pe.Operator) out.WriteString(pe.Right.String()) out.WriteString(")") return out.String() } type InfixExpression struct { Token token.Token Left Expression Operator string Right Expression } func (oe *InfixExpression) expressionNode() {} func (oe *InfixExpression) TokenLiteral() string { return oe.Token.Literal } func (oe *InfixExpression) String() string { var out bytes.Buffer out.WriteString("(") out.WriteString(oe.Left.String()) out.WriteString(" " + oe.Operator + " ") out.WriteString(oe.Right.String()) out.WriteString(")") return out.String() } type Boolean struct { Token token.Token Value bool } func (b *Boolean) expressionNode() {} func (b *Boolean) TokenLiteral() string { return b.Token.Literal } func (b *Boolean) String() string { return b.Token.Literal } type IfExpression struct { Token token.Token Condition Expression Consequence *BlockStatement Alternative *BlockStatement } func (ie *IfExpression) expressionNode() {} func (ie *IfExpression) TokenLiteral() string { return ie.Token.Literal } func (ie *IfExpression) String() string { var out bytes.Buffer out.WriteString("kama") out.WriteString(ie.Condition.String()) out.WriteString(" ") out.WriteString(ie.Consequence.String()) if ie.Alternative != nil { out.WriteString("sivyo") out.WriteString(ie.Alternative.String()) } return out.String() } type BlockStatement struct { Token token.Token Statements []Statement } func (bs *BlockStatement) statementNode() {} func (bs *BlockStatement) TokenLiteral() string { return bs.Token.Literal } func (bs *BlockStatement) String() string { var out bytes.Buffer for _, s := range bs.Statements { out.WriteString(s.String()) } return out.String() } type FunctionLiteral struct { Token token.Token Name string Parameters []*Identifier Defaults map[string]Expression Body *BlockStatement } func (fl *FunctionLiteral) expressionNode() {} func (fl *FunctionLiteral) TokenLiteral() string { return fl.Token.Literal } func (fl *FunctionLiteral) String() string { var out bytes.Buffer params := []string{} for _, p := range fl.Parameters { params = append(params, p.String()) } out.WriteString(fl.TokenLiteral()) out.WriteString("(") out.WriteString(strings.Join(params, ", ")) out.WriteString(") ") out.WriteString(fl.Body.String()) return out.String() } type CallExpression struct { Token token.Token Function Expression // can be Identifier or FunctionLiteral Arguments []Expression } func (ce *CallExpression) expressionNode() {} func (ce *CallExpression) TokenLiteral() string { return ce.Token.Literal } func (ce *CallExpression) String() string { var out bytes.Buffer args := []string{} for _, a := range ce.Arguments { args = append(args, a.String()) } out.WriteString(ce.Function.String()) out.WriteString("(") out.WriteString(strings.Join(args, ", ")) out.WriteString(")") return out.String() } type StringLiteral struct { Token token.Token Value string } func (sl *StringLiteral) expressionNode() {} func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal } func (sl *StringLiteral) String() string { return sl.Token.Literal } type ArrayLiteral struct { Token token.Token Elements []Expression } func (al *ArrayLiteral) expressionNode() {} func (al *ArrayLiteral) TokenLiteral() string { return al.Token.Literal } func (al *ArrayLiteral) String() string { var out bytes.Buffer elements := []string{} for _, el := range al.Elements { elements = append(elements, el.String()) } out.WriteString("[") out.WriteString(strings.Join(elements, ", ")) out.WriteString("]") return out.String() } type IndexExpression struct { Token token.Token Left Expression Index Expression } func (ie *IndexExpression) expressionNode() {} func (ie *IndexExpression) TokenLiteral() string { return ie.Token.Literal } func (ie *IndexExpression) String() string { var out bytes.Buffer out.WriteString("(") out.WriteString(ie.Left.String()) out.WriteString("[") out.WriteString(ie.Index.String()) out.WriteString("])") return out.String() } type DictLiteral struct { Token token.Token Pairs map[Expression]Expression } func (dl *DictLiteral) expressionNode() {} func (dl *DictLiteral) TokenLiteral() string { return dl.Token.Literal } func (dl *DictLiteral) String() string { var out bytes.Buffer pairs := []string{} for key, value := range dl.Pairs { pairs = append(pairs, key.String()+":"+value.String()) } out.WriteString("(") out.WriteString(strings.Join(pairs, ", ")) out.WriteString("}") return out.String() } type Assign struct { Token token.Token Name *Identifier Value Expression } func (ae *Assign) expressionNode() {} func (ae *Assign) TokenLiteral() string { return ae.Token.Literal } func (ae *Assign) String() string { var out bytes.Buffer out.WriteString(ae.Name.String()) out.WriteString(ae.TokenLiteral()) out.WriteString(ae.Value.String()) return out.String() } type AssignEqual struct { Token token.Token Left *Identifier Value Expression } func (ae *AssignEqual) expressionNode() {} func (ae *AssignEqual) TokenLiteral() string { return ae.Token.Literal } func (ae *AssignEqual) String() string { var out bytes.Buffer out.WriteString(ae.Left.String()) out.WriteString(ae.TokenLiteral()) out.WriteString(ae.Value.String()) return out.String() } type AssignmentExpression struct { Token token.Token Left Expression Value Expression } func (ae *AssignmentExpression) expressionNode() {} func (ae *AssignmentExpression) TokenLiteral() string { return ae.Token.Literal } func (ae *AssignmentExpression) String() string { var out bytes.Buffer out.WriteString(ae.Left.String()) out.WriteString(ae.TokenLiteral()) out.WriteString(ae.Value.String()) return out.String() } type WhileExpression struct { Token token.Token Condition Expression Consequence *BlockStatement } func (we *WhileExpression) expressionNode() {} func (we *WhileExpression) TokenLiteral() string { return we.Token.Literal } func (we *WhileExpression) String() string { var out bytes.Buffer out.WriteString("wakati") out.WriteString(we.Condition.String()) out.WriteString(" ") out.WriteString(we.Consequence.String()) return out.String() } type Null struct { Token token.Token } func (n *Null) expressionNode() {} func (n *Null) TokenLiteral() string { return n.Token.Literal } func (n *Null) String() string { return n.Token.Literal } type Break struct { Statement Token token.Token // the 'break' token } func (b *Break) expressionNode() {} func (b *Break) TokenLiteral() string { return b.Token.Literal } func (b *Break) String() string { return b.Token.Literal } type Continue struct { Statement Token token.Token // the 'continue' token } func (c *Continue) expressionNode() {} func (c *Continue) TokenLiteral() string { return c.Token.Literal } func (c *Continue) String() string { return c.Token.Literal } type PostfixExpression struct { Token token.Token Operator string } func (pe *PostfixExpression) expressionNode() {} func (pe *PostfixExpression) TokenLiteral() string { return pe.Token.Literal } func (pe *PostfixExpression) String() string { var out bytes.Buffer out.WriteString("(") out.WriteString(pe.Token.Literal) out.WriteString(pe.Operator) out.WriteString(")") return out.String() } type FloatLiteral struct { Token token.Token Value float64 } func (fl *FloatLiteral) expressionNode() {} func (fl *FloatLiteral) TokenLiteral() string { return fl.Token.Literal } func (fl *FloatLiteral) String() string { return fl.Token.Literal } type For struct { Token token.Token Identifier string // "i" StarterName *Identifier // i = 0 StarterValue Expression Closer Expression // i++ Condition Expression // i < 1 Block *BlockStatement } type ForIn struct { Token token.Token Key string Value string Iterable Expression Block *BlockStatement } func (fi *ForIn) expressionNode() {} func (fi *ForIn) TokenLiteral() string { return fi.Token.Literal } func (fi *ForIn) String() string { var out bytes.Buffer out.WriteString("kwa ") if fi.Key != "" { out.WriteString(fi.Key + ", ") } out.WriteString(fi.Value + " ") out.WriteString("ktk ") out.WriteString(fi.Iterable.String() + " {\n") out.WriteString("\t" + fi.Block.String()) out.WriteString("\n}") return out.String() } type CaseExpression struct { Token token.Token Default bool Expr []Expression Block *BlockStatement } func (ce *CaseExpression) expressionNode() {} func (ce *CaseExpression) TokenLiteral() string { return ce.Token.Literal } func (ce *CaseExpression) String() string { var out bytes.Buffer if ce.Default { out.WriteString("kawaida ") } else { out.WriteString("ikiwa ") tmp := []string{} for _, exp := range ce.Expr { tmp = append(tmp, exp.String()) } out.WriteString(strings.Join(tmp, ",")) } out.WriteString(ce.Block.String()) return out.String() } type SwitchExpression struct { Token token.Token Value Expression Choices []*CaseExpression } func (se *SwitchExpression) expressionNode() {} func (se *SwitchExpression) TokenLiteral() string { return se.Token.Literal } func (se *SwitchExpression) String() string { var out bytes.Buffer out.WriteString("\nbadili (") out.WriteString(se.Value.String()) out.WriteString(")\n{\n") for _, tmp := range se.Choices { if tmp != nil { out.WriteString(tmp.String()) } } out.WriteString("}\n") return out.String() } type MethodExpression struct { Token token.Token Object Expression Method Expression Arguments []Expression Defaults map[string]Expression } func (me *MethodExpression) expressionNode() {} func (me *MethodExpression) TokenLiteral() string { return me.Token.Literal } func (me *MethodExpression) String() string { var out bytes.Buffer out.WriteString(me.Object.String()) out.WriteString(".") out.WriteString(me.Method.String()) return out.String() } type Import struct { Token token.Token Identifiers map[string]*Identifier } func (i *Import) expressionNode() {} func (i *Import) TokenLiteral() string { return i.Token.Literal } func (i *Import) String() string { var out bytes.Buffer out.WriteString("tumia ") for k := range i.Identifiers { out.WriteString(k + " ") } return out.String() } type PackageBlock struct { Token token.Token Statements []Statement } func (pb *PackageBlock) statementNode() {} func (pb *PackageBlock) TokenLiteral() string { return pb.Token.Literal } func (pb *PackageBlock) String() string { var out bytes.Buffer for _, s := range pb.Statements { out.WriteString(s.String()) } return out.String() } type Package struct { Token token.Token Name *Identifier Block *BlockStatement } func (p *Package) expressionNode() {} func (p *Package) TokenLiteral() string { return p.Token.Literal } func (p *Package) String() string { var out bytes.Buffer out.WriteString("pakeji " + p.Name.Value + "\n") out.WriteString("::\n") for _, s := range p.Block.Statements { out.WriteString(s.String()) } out.WriteString("\n::") return out.String() } type At struct { Token token.Token } func (a *At) expressionNode() {} func (a *At) TokenLiteral() string { return a.Token.Literal } func (a *At) String() string { return "@" } type PropertyAssignment struct { Token token.Token // the '=' token Name *PropertyExpression Value Expression } func (pa *PropertyAssignment) expressionNode() {} func (pa *PropertyAssignment) TokenLiteral() string { return pa.Token.Literal } func (pa *PropertyAssignment) String() string { return "Ngl I'm tired" } type PropertyExpression struct { Expression Token token.Token // The . token Object Expression Property Expression } func (pe *PropertyExpression) expressionNode() {} func (pe *PropertyExpression) TokenLiteral() string { return pe.Token.Literal } func (pe *PropertyExpression) String() string { return "Ngl I'm tired part two" } ================================================ FILE: ast/ast_test.go ================================================ package ast import ( "testing" "github.com/NuruProgramming/Nuru/token" ) func TestString(t *testing.T) { program := &Program{ Statements: []Statement{ &LetStatement{ Token: token.Token{Type: token.LET, Literal: "fanya"}, Name: &Identifier{ Token: token.Token{Type: token.IDENT, Literal: "myVar"}, Value: "myVar", }, Value: &Identifier{ Token: token.Token{Type: token.IDENT, Literal: "anotherVar"}, Value: "anotherVar", }, }, }, } if program.String() != "fanya myVar = anotherVar;" { t.Errorf("program.String() wrong. got=%q", program.String()) } } ================================================ FILE: evaluator/assign.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalAssign(node *ast.Assign, env *object.Environment) object.Object { val := Eval(node.Value, env) if isError(val) { return val } obj := env.Set(node.Name.Value, val) return obj } ================================================ FILE: evaluator/assignEqual.go ================================================ package evaluator import ( "strings" "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalAssignEqual(node *ast.AssignEqual, env *object.Environment) object.Object { left := Eval(node.Left, env) if isError(left) { return left } value := Eval(node.Value, env) if isError(value) { return value } switch node.Token.Literal { case "+=": switch arg := left.(type) { case *object.Integer: switch val := value.(type) { case *object.Integer: v := arg.Value + val.Value return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) case *object.Float: v := float64(arg.Value) + val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '+=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } case *object.Float: switch val := value.(type) { case *object.Integer: v := arg.Value + float64(val.Value) return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) case *object.Float: v := arg.Value + val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '+=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } case *object.String: switch val := value.(type) { case *object.String: v := arg.Value + val.Value return env.Set(node.Left.Token.Literal, &object.String{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '+=' kwa %v na %v", node.Token.Line, arg.Type(), val.Type()) } default: return newError("Mstari %d: Huwezi kutumia '+=' na %v", node.Token.Line, arg.Type()) } case "-=": switch arg := left.(type) { case *object.Integer: switch val := value.(type) { case *object.Integer: v := arg.Value - val.Value return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) case *object.Float: v := float64(arg.Value) - val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '-=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } case *object.Float: switch val := value.(type) { case *object.Integer: v := arg.Value - float64(val.Value) return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) case *object.Float: v := arg.Value - val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '-=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } default: return newError("Mstari %d: Huwezi kutumia '-=' na %v", node.Token.Line, arg.Type()) } case "*=": switch arg := left.(type) { case *object.Integer: switch val := value.(type) { case *object.Integer: v := arg.Value * val.Value return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) case *object.Float: v := float64(arg.Value) * val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) case *object.String: v := strings.Repeat(val.Value, int(arg.Value)) return env.Set(node.Left.Token.Literal, &object.String{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '*=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } case *object.Float: switch val := value.(type) { case *object.Integer: v := arg.Value * float64(val.Value) return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) case *object.Float: v := arg.Value * val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '*=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } case *object.String: switch val := value.(type) { case *object.Integer: v := strings.Repeat(arg.Value, int(val.Value)) return env.Set(node.Left.Token.Literal, &object.String{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '+=' kwa %v na %v", node.Token.Line, arg.Type(), val.Type()) } default: return newError("Mstari %d: Huwezi kutumia '*=' na %v", node.Token.Line, arg.Type()) } case "/=": switch arg := left.(type) { case *object.Integer: switch val := value.(type) { case *object.Integer: v := arg.Value / val.Value return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) case *object.Float: v := float64(arg.Value) / val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '/=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } case *object.Float: switch val := value.(type) { case *object.Integer: v := arg.Value / float64(val.Value) return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) case *object.Float: v := arg.Value / val.Value return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: Huwezi kutumia '/=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) } default: return newError("Mstari %d: Huwezi kutumia '/=' na %v", node.Token.Line, arg.Type()) } default: return newError("Mstari %d: Operesheni Haifahamiki %s", node.Token.Line, node.Token.Literal) } } ================================================ FILE: evaluator/at.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalAt(node *ast.At, env *object.Environment) object.Object { if at, ok := env.Get("@"); ok { return at } return newError("Iko nje ya scope") } ================================================ FILE: evaluator/bang.go ================================================ package evaluator import "github.com/NuruProgramming/Nuru/object" func evalBangOperatorExpression(right object.Object) object.Object { switch right { case TRUE: return FALSE case FALSE: return TRUE case NULL: return TRUE default: return FALSE } } ================================================ FILE: evaluator/block.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object { var result object.Object for _, statement := range block.Statements { result = Eval(statement, env) if result != nil { rt := result.Type() if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ || rt == object.CONTINUE_OBJ || rt == object.BREAK_OBJ { return result } } } return result } ================================================ FILE: evaluator/builtins.go ================================================ //go:build !js || !wasm package evaluator import ( "bufio" "fmt" "io" "os" "strings" "github.com/NuruProgramming/Nuru/object" ) var builtins = map[string]*object.Builtin{ "jaza": { Fn: func(args ...object.Object) object.Object { if len(args) > 1 { return newError("Samahani, kiendesha hiki kinapokea hoja 0 au 1, wewe umeweka %d", len(args)) } if len(args) > 0 && args[0].Type() != object.STRING_OBJ { return newError(fmt.Sprintf(`Tafadhali tumia alama ya nukuu: "%s"`, args[0].Inspect())) } if len(args) == 1 { prompt := args[0].(*object.String).Value fmt.Fprint(os.Stdout, prompt) } buffer := bufio.NewReader(os.Stdin) line, _, err := buffer.ReadLine() if err != nil && err != io.EOF { return newError("Nimeshindwa kusoma uliyo yajaza") } return &object.String{Value: string(line)} }, }, "andika": { Fn: func(args ...object.Object) object.Object { if len(args) == 0 { fmt.Println("") } else { var arr []string for _, arg := range args { if arg == nil { return newError("Hauwezi kufanya operesheni hii") } arr = append(arr, arg.Inspect()) } str := strings.Join(arr, " ") fmt.Println(str) } return nil }, }, "_andika": { Fn: func(args ...object.Object) object.Object { if len(args) == 0 { return &object.String{Value: "\n"} } else { var arr []string for _, arg := range args { if arg == nil { return newError("Hauwezi kufanya operesheni hii") } arr = append(arr, arg.Inspect()) } str := strings.Join(arr, " ") return &object.String{Value: str} } }, }, } func init(){ for name, builtin := range commonBuiltins{ builtins[name]=builtin } } ================================================ FILE: evaluator/builtins_common.go ================================================ package evaluator import ( "fmt" "os" "strings" "github.com/NuruProgramming/Nuru/object" ) var commonBuiltins = map[string]*object.Builtin{ "_andika": { Fn: func(args ...object.Object) object.Object { if len(args) == 0 { return &object.String{Value: "\n"} } else { var arr []string for _, arg := range args { if arg == nil { return newError("Hauwezi kufanya operesheni hii") } arr = append(arr, arg.Inspect()) } str := strings.Join(arr, " ") return &object.String{Value: str} } }, }, "aina": { Fn: func(args ...object.Object) object.Object { if len(args) != 1 { return newError("Samahani, tunahitaji hoja 1, wewe umeweka %d", len(args)) } return &object.String{Value: string(args[0].Type())} }, }, "fungua": { Fn: func(args ...object.Object) object.Object { if len(args) != 1 { return newError("Samahani, tunahitaji hoja 1, wewe umeweka %d", len(args)) } filename := args[0].(*object.String).Value file, err := os.ReadFile(filename) if err != nil { return &object.Error{Message: "Tumeshindwa kusoma faili au faili halipo"} } return &object.File{Filename: filename, Content: string(file)} }, }, "mfululizo": { Fn: func(args ...object.Object) object.Object { if len(args) < 1 || len(args) > 3 { return newError("Samahani, mfululizo inahitaji hoja 1 hadi 3, wewe umeweka %d", len(args)) } var start, end, step int64 var err error switch len(args) { case 1: end, err = getIntValue(args[0]) if err != nil { return newError("Hoja lazima iwe nambari nzima") } start, step = 0, 1 case 2: start, err = getIntValue(args[0]) if err != nil { return newError("Hoja ya kwanza lazima iwe nambari nzima") } end, err = getIntValue(args[1]) if err != nil { return newError("Hoja ya pili lazima iwe nambari nzima") } step = 1 case 3: start, err = getIntValue(args[0]) if err != nil { return newError("Hoja ya kwanza lazima iwe nambari nzima") } end, err = getIntValue(args[1]) if err != nil { return newError("Hoja ya pili lazima iwe nambari nzima") } step, err = getIntValue(args[2]) if err != nil { return newError("Hoja ya tatu lazima iwe nambari nzima") } if step == 0 { return newError("Hatua haiwezi kuwa sifuri") } } elements := []object.Object{} for i := start; (step > 0 && i < end) || (step < 0 && i > end); i += step { elements = append(elements, &object.Integer{Value: i}) } return &object.Array{Elements: elements} }, }, "badilisha": { Fn: func(args ...object.Object) object.Object { if len(args) != 2 { return newError("Samahani, badili inahitaji hoja 2, wewe umeweka %d", len(args)) } value := args[0] targetType := args[1] if targetType.Type() != object.STRING_OBJ { return newError("Aina ya lengo lazima iwe neno") } targetTypeStr := targetType.(*object.String).Value switch targetTypeStr { case "NAMBA": return convertToInteger(value) case "DESIMALI": return convertToFloat(value) case "NENO": return convertToString(value) case "BOOLEAN": return convertToBoolean(value) default: return newError("Aina isiyojulikana: %s", targetTypeStr) } }, }, "namba": { Fn: func(args ...object.Object) object.Object { if len(args) != 1 { return newError("Samahani, namba inahitaji hoja 1, wewe umeweka %d", len(args)) } value := args[0] return convertToInteger(value) }, }, "tungo": { Fn: func(args ...object.Object) object.Object { if len(args) != 1 { return newError("Samahani, tungo inahitaji hoja 1, wewe umeweka %d", len(args)) } value := args[0] return convertToString(value) }, }, // "jumla": { // Fn: func(args ...object.Object) object.Object { // if len(args) != 1 { // return newError("Hoja hazilingani, tunahitaji=1, tumepewa=%d", len(args)) // } // switch arg := args[0].(type) { // case *object.Array: // var sums float64 // for _, num := range arg.Elements { // if num.Type() != object.INTEGER_OBJ && num.Type() != object.FLOAT_OBJ { // return newError("Samahani namba tu zinahitajika") // } else { // if num.Type() == object.INTEGER_OBJ { // no, _ := strconv.Atoi(num.Inspect()) // floatnum := float64(no) // sums += floatnum // } else if num.Type() == object.FLOAT_OBJ { // no, _ := strconv.ParseFloat(num.Inspect(), 64) // sums += no // } // } // } // if math.Mod(sums, 1) == 0 { // return &object.Integer{Value: int64(sums)} // } // return &object.Float{Value: float64(sums)} // default: // return newError("Samahani, hii function haitumiki na %s", args[0].Type()) // } // }, // }, } func getIntValue(obj object.Object) (int64, error) { switch obj := obj.(type) { case *object.Integer: return obj.Value, nil default: return 0, fmt.Errorf("expected integer, got %T", obj) } } ================================================ FILE: evaluator/builtins_wasm.go ================================================ //go:build wasm && js // Modified version with of the builtins.go file with browser friendly versions of functions. package evaluator import ( "fmt" "strings" "github.com/NuruProgramming/Nuru/object" "syscall/js" ) var builtins = map[string]*object.Builtin{ "jaza": { Fn: func(args ...object.Object) object.Object { if len(args) > 1 { return newError("Samahani, kiendesha hiki kinapokea hoja 0 au 1, wewe umeweka %d", len(args)) } if len(args) == 1 && args[0].Type() != object.STRING_OBJ { return newError(fmt.Sprintf(`Tafadhali tumia alama ya nukuu: "%s"`, args[0].Inspect())) } // Get the window.prompt function jsPromptFunction := js.Global().Get("prompt") if jsPromptFunction.Type() != js.TypeFunction { return newError("prompt function not found") } // invoke it!! var result js.Value if len(args) == 0 { result = jsPromptFunction.Invoke() } else { result = jsPromptFunction.Invoke(args[0].Inspect()) } if result.String() == ""|| result.String() == "null" { return newError("Nimeshindwa kusoma uliyo yajaza") } return &object.String{Value: string(result.String())} }, }, "andika": { Fn: func(args ...object.Object) object.Object { jsOutputReceiverFunction := js.Global().Get("nuruOutputReceiver") if len(args) == 0 { jsOutputReceiverFunction.Invoke("") } else { var arr []string for _, arg := range args { if arg == nil { return newError("Hauwezi kufanya operesheni hii") } arr = append(arr, arg.Inspect()) } str := strings.Join(arr, " ") jsOutputReceiverFunction.Invoke(str) // pipe output to js land } return nil }, }, } func init(){ for name, builtin := range commonBuiltins{ builtins[name]=builtin } } ================================================ FILE: evaluator/call.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalCall(node *ast.CallExpression, env *object.Environment) object.Object { function := Eval(node.Function, env) if isError(function) { return function } var args []object.Object switch fn := function.(type) { case *object.Function: args = evalArgsExpressions(node, fn, env) case *object.Package: obj, ok := fn.Scope.Get("andaa") if !ok { return newError("Pakeji haina 'andaa'") } args = evalArgsExpressions(node, obj.(*object.Function), env) default: args = evalExpressions(node.Arguments, env) } if len(args) == 1 && isError(args[0]) { return args[0] } return applyFunction(function, args, node.Token.Line) } func evalArgsExpressions(node *ast.CallExpression, fn *object.Function, env *object.Environment) []object.Object { argsList := &object.Array{} argsHash := &object.Dict{} argsHash.Pairs = make(map[object.HashKey]object.DictPair) for _, exprr := range node.Arguments { switch exp := exprr.(type) { case *ast.Assign: val := Eval(exp.Value, env) if isError(val) { return []object.Object{val} } var keyHash object.HashKey key := &object.String{Value: exp.Name.Value} keyHash = key.HashKey() pair := object.DictPair{Key: key, Value: val} argsHash.Pairs[keyHash] = pair default: evaluated := Eval(exp, env) if isError(evaluated) { return []object.Object{evaluated} } argsList.Elements = append(argsList.Elements, evaluated) } } var result []object.Object var params = map[string]bool{} for _, exp := range fn.Parameters { params[exp.Value] = true if len(argsList.Elements) > 0 { result = append(result, argsList.Elements[0]) argsList.Elements = argsList.Elements[1:] } else { keyParam := &object.String{Value: exp.Value} keyParamHash := keyParam.HashKey() if valParam, ok := argsHash.Pairs[keyParamHash]; ok { result = append(result, valParam.Value) delete(argsHash.Pairs, keyParamHash) } else { if _e, _ok := fn.Defaults[exp.Value]; _ok { evaluated := Eval(_e, env) if isError(evaluated) { return []object.Object{evaluated} } result = append(result, evaluated) } else { return []object.Object{&object.Error{Message: "Tumekosa Hoja"}} } } } } for _, pair := range argsHash.Pairs { if _, ok := params[pair.Key.(*object.String).Value]; ok { return []object.Object{&object.Error{Message: "Tumepewa hoja nyingi kwa parameter moja"}} } } return result } ================================================ FILE: evaluator/dict.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalDictLiteral(node *ast.DictLiteral, env *object.Environment) object.Object { pairs := make(map[object.HashKey]object.DictPair) for keyNode, valueNode := range node.Pairs { key := Eval(keyNode, env) if isError(key) { return key } hashKey, ok := key.(object.Hashable) if !ok { return newError("Mstari %d: Hashing imeshindikana: %s", node.Token.Line, key.Type()) } value := Eval(valueNode, env) if isError(value) { return value } hashed := hashKey.HashKey() pairs[hashed] = object.DictPair{Key: key, Value: value} } return &object.Dict{Pairs: pairs} } ================================================ FILE: evaluator/error_handler.go ================================================ package evaluator import ( "fmt" "github.com/NuruProgramming/Nuru/object" ) func newError(format string, a ...interface{}) *object.Error { return &object.Error{Message: fmt.Sprintf(format, a...)} } ================================================ FILE: evaluator/evaluator.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) var ( NULL = &object.Null{} TRUE = &object.Boolean{Value: true} FALSE = &object.Boolean{Value: false} BREAK = &object.Break{} CONTINUE = &object.Continue{} ) func Eval(node ast.Node, env *object.Environment) object.Object { switch node := node.(type) { case *ast.Program: return evalProgram(node, env) case *ast.ExpressionStatement: return Eval(node.Expression, env) case *ast.IntegerLiteral: return &object.Integer{Value: node.Value} case *ast.FloatLiteral: return &object.Float{Value: node.Value} case *ast.Boolean: return nativeBoolToBooleanObject(node.Value) case *ast.PrefixExpression: right := Eval(node.Right, env) if isError(right) { return right } return evalPrefixExpression(node.Operator, right, node.Token.Line) case *ast.InfixExpression: left := Eval(node.Left, env) if isError(left) { return left } right := Eval(node.Right, env) if isError(right) && right != nil { return right } return evalInfixExpression(node.Operator, left, right, node.Token.Line) case *ast.PostfixExpression: return evalPostfixExpression(env, node.Operator, node) case *ast.BlockStatement: return evalBlockStatement(node, env) case *ast.IfExpression: return evalIfExpression(node, env) case *ast.ReturnStatement: val := Eval(node.ReturnValue, env) if isError(val) { return val } return &object.ReturnValue{Value: val} case *ast.LetStatement: val := Eval(node.Value, env) if isError(val) { return val } env.Set(node.Name.Value, val) case *ast.Identifier: return evalIdentifier(node, env) case *ast.FunctionLiteral: return evalFunction(node, env) case *ast.MethodExpression: return evalMethodExpression(node, env) case *ast.Import: return evalImport(node, env) case *ast.CallExpression: return evalCall(node, env) case *ast.StringLiteral: return &object.String{Value: node.Value} case *ast.At: return evalAt(node, env) case *ast.ArrayLiteral: elements := evalExpressions(node.Elements, env) if len(elements) == 1 && isError(elements[0]) { return elements[0] } return &object.Array{Elements: elements} case *ast.IndexExpression: left := Eval(node.Left, env) if isError(left) { return left } index := Eval(node.Index, env) if isError(index) { return index } return evalIndexExpression(left, index, node.Token.Line) case *ast.DictLiteral: return evalDictLiteral(node, env) case *ast.WhileExpression: return evalWhileExpression(node, env) case *ast.Break: return evalBreak(node) case *ast.Continue: return evalContinue(node) case *ast.SwitchExpression: return evalSwitchStatement(node, env) case *ast.Null: return NULL // case *ast.For: // return evalForExpression(node, env) case *ast.ForIn: return evalForInExpression(node, env, node.Token.Line) case *ast.Package: return evalPackage(node, env) case *ast.PropertyExpression: return evalPropertyExpression(node, env) case *ast.PropertyAssignment: val := Eval(node.Value, env) if isError(val) { return val } return evalPropertyAssignment(node.Name, val, env) case *ast.Assign: return evalAssign(node, env) case *ast.AssignEqual: return evalAssignEqual(node, env) case *ast.AssignmentExpression: left := Eval(node.Left, env) if isError(left) { return left } value := Eval(node.Value, env) if isError(value) { return value } // This is an easy way to assign operators like +=, -= etc // for index expressions (arrays and dicts) where applicable op := node.Token.Literal if len(op) >= 2 { op = op[:len(op)-1] value = evalInfixExpression(op, left, value, node.Token.Line) if isError(value) { return value } } if ident, ok := node.Left.(*ast.Identifier); ok { env.Set(ident.Value, value) } else if ie, ok := node.Left.(*ast.IndexExpression); ok { obj := Eval(ie.Left, env) if isError(obj) { return obj } if array, ok := obj.(*object.Array); ok { index := Eval(ie.Index, env) if isError(index) { return index } if idx, ok := index.(*object.Integer); ok { if int(idx.Value) >= len(array.Elements) { return newError("Index imezidi idadi ya elements") } array.Elements[idx.Value] = value } else { return newError("Hauwezi kufanya operesheni hii na %#v", index) } } else if hash, ok := obj.(*object.Dict); ok { key := Eval(ie.Index, env) if isError(key) { return key } if hashKey, ok := key.(object.Hashable); ok { hashed := hashKey.HashKey() hash.Pairs[hashed] = object.DictPair{Key: key, Value: value} } else { return newError("Hauwezi kufanya operesheni hii na %T", key) } } else { return newError("%T haifanyi operesheni hii", obj) } } else { return newError("Tumia neno kama kibadala, sio %T", left) } } return nil } func evalProgram(program *ast.Program, env *object.Environment) object.Object { var result object.Object for _, statement := range program.Statements { result = Eval(statement, env) switch result := result.(type) { case *object.ReturnValue: return result.Value case *object.Error: return result } } return result } func nativeBoolToBooleanObject(input bool) *object.Boolean { if input { return TRUE } return FALSE } func isTruthy(obj object.Object) bool { switch obj { case NULL: return false case TRUE: return true case FALSE: return false default: return true } } func isError(obj object.Object) bool { if obj != nil { return obj.Type() == object.ERROR_OBJ } return false } func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object { var result []object.Object for _, e := range exps { evaluated := Eval(e, env) if isError(evaluated) { return []object.Object{evaluated} } result = append(result, evaluated) } return result } func applyFunction(fn object.Object, args []object.Object, line int) object.Object { switch fn := fn.(type) { case *object.Function: extendedEnv := extendedFunctionEnv(fn, args) evaluated := Eval(fn.Body, extendedEnv) return unwrapReturnValue(evaluated) case *object.Builtin: if result := fn.Fn(args...); result != nil { return result } return NULL case *object.Package: obj := &object.Instance{ Package: fn, Env: object.NewEnclosedEnvironment(fn.Env), } obj.Env.Set("@", obj) node, ok := fn.Scope.Get("andaa") if !ok { return newError("Hamna andaa kiendesha") } node.(*object.Function).Env.Set("@", obj) applyFunction(node, args, fn.Name.Token.Line) node.(*object.Function).Env.Del("@") return obj default: if fn != nil { return newError("Mstari %d: Hiki sio kiendesha: %s", line, fn.Type()) } else { return newError("Bro how did you even get here??? Contact language maker asap!") } } } func extendedFunctionEnv(fn *object.Function, args []object.Object) *object.Environment { env := object.NewEnclosedEnvironment(fn.Env) for paramIdx, param := range fn.Parameters { if paramIdx < len(args) { env.Set(param.Value, args[paramIdx]) } } return env } func unwrapReturnValue(obj object.Object) object.Object { if returnValue, ok := obj.(*object.ReturnValue); ok { return returnValue.Value } return obj } func evalBreak(node *ast.Break) object.Object { return BREAK } func evalContinue(node *ast.Continue) object.Object { return CONTINUE } // func evalForExpression(fe *ast.For, env *object.Environment) object.Object { // obj, ok := env.Get(fe.Identifier) // defer func() { // stay safe and not reassign an existing variable // if ok { // env.Set(fe.Identifier, obj) // } // }() // val := Eval(fe.StarterValue, env) // if isError(val) { // return val // } // env.Set(fe.StarterName.Value, val) // // err := Eval(fe.Starter, env) // // if isError(err) { // // return err // // } // for { // evaluated := Eval(fe.Condition, env) // if isError(evaluated) { // return evaluated // } // if !isTruthy(evaluated) { // break // } // res := Eval(fe.Block, env) // if isError(res) { // return res // } // if res.Type() == object.BREAK_OBJ { // break // } // if res.Type() == object.CONTINUE_OBJ { // err := Eval(fe.Closer, env) // if isError(err) { // return err // } // continue // } // if res.Type() == object.RETURN_VALUE_OBJ { // return res // } // err := Eval(fe.Closer, env) // if isError(err) { // return err // } // } // return NULL // } func loopIterable(next func() (object.Object, object.Object), env *object.Environment, fi *ast.ForIn) object.Object { k, v := next() for k != nil && v != nil { env.Set(fi.Key, k) env.Set(fi.Value, v) res := Eval(fi.Block, env) if isError(res) { return res } if res != nil { if res.Type() == object.BREAK_OBJ { break } if res.Type() == object.CONTINUE_OBJ { k, v = next() continue } if res.Type() == object.RETURN_VALUE_OBJ { return res } } k, v = next() } return NULL } ================================================ FILE: evaluator/evaluator_test.go ================================================ package evaluator import ( "fmt" "testing" "time" "github.com/NuruProgramming/Nuru/lexer" "github.com/NuruProgramming/Nuru/object" "github.com/NuruProgramming/Nuru/parser" ) func TestEvalIntegerExpression(t *testing.T) { tests := []struct { input string expected int64 }{ {"5", 5}, {"10", 10}, {"-5", -5}, {"-10", -10}, {"5 + 5 + 5 + 5 - 10", 10}, {"2 * 2 * 2 * 2", 16}, {"2 / 2 + 1", 2}, } for _, tt := range tests { evaluated := testEval(tt.input) testIntegerObject(t, evaluated, tt.expected) } } func TestEvalFloatExpression(t *testing.T) { tests := []struct { input string expected float64 }{ {"2**3", 8.0}, } for _, tt := range tests { evaluated := testEval(tt.input) testFloatObject(t, evaluated, tt.expected) } } func TestEvalBooleanExpression(t *testing.T) { tests := []struct { input string expected bool }{ {"kweli", true}, {"sikweli", false}, {"1 < 2", true}, {"1 > 2", false}, {"1 > 1", false}, {"1 < 1", false}, {"1 == 1", true}, {"1 != 1", false}, {"1 == 2", false}, {"1 != 2", true}, {"kweli == kweli", true}, {"sikweli == sikweli", true}, {"kweli == sikweli", false}, {"kweli != sikweli", true}, {"sikweli != kweli", true}, {"(1 < 2) == kweli", true}, {"!kweli", false}, {"!sikweli", true}, {"!tupu", true}, {"!'kitu'", false}, {"2 > 1 && 1 < 4", true}, {"2 > 1 && 1 > 4", false}, {"2 < 1 && 1 < 4", false}, {"2 < 1 && 1 > 4", false}, {"5 < 2 || 3 > 2", true}, {"5 == 5 || 4 == 4", true}, {"5 > 2 || 3 < 2", true}, {"5 < 2 || 3 < 2", false}, {"5 >= 2", true}, {"5 <= 2", false}, } for _, tt := range tests { evaluated := testEval(tt.input) testBooleanObject(t, evaluated, tt.expected) } } func TestBangOperator(t *testing.T) { tests := []struct { input string expected bool }{ {"!kweli", false}, {"!sikweli", true}, {"!5", false}, {"!!kweli", true}, {"!!sikweli", false}, {"!!5", true}, } for _, tt := range tests { evaluated := testEval(tt.input) testBooleanObject(t, evaluated, tt.expected) } } func testEval(input string) object.Object { l := lexer.New(input) p := parser.New(l) program := p.ParseProgram() env := object.NewEnvironment() return Eval(program, env) } func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool { result, ok := obj.(*object.Integer) if !ok { t.Errorf("Object is not Integer, got=%T(%+v)", obj, obj) return false } if result.Value != expected { t.Errorf("object has wrong value. got=%d, want=%d", result.Value, expected) return false } return true } func testFloatObject(t *testing.T, obj object.Object, expected float64) bool { result, ok := obj.(*object.Float) if !ok { t.Errorf("Object is not Float, got=%T(%+v)", obj, obj) return false } if result.Value != expected { t.Errorf("object has wrong value. got=%f, want=%f", result.Value, expected) return false } return true } func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool { result, ok := obj.(*object.Boolean) if !ok { t.Errorf("object is not Boolean, got=%T(%+v)", obj, obj) return false } if result.Value != expected { t.Errorf("object has wrong value, got=%t, want=%t", result.Value, expected) return false } return true } func TestIfElseExpressions(t *testing.T) { tests := []struct { input string expected interface{} }{ {"kama (kweli) {10}", 10}, {"kama (sikweli) {10}", nil}, {"kama (1) {10}", 10}, {"kama (1 < 2) {10}", 10}, {"kama (1 > 2) {10}", nil}, {"kama (1 > 2) {10} sivyo {20}", 20}, {"kama (1 < 2) {10} sivyo {20}", 10}, } for _, tt := range tests { evaluated := testEval(tt.input) integer, ok := tt.expected.(int) if ok { testIntegerObject(t, evaluated, int64(integer)) } else { testNullObject(t, evaluated) } } } func testNullObject(t *testing.T, obj object.Object) bool { if obj != NULL { t.Errorf("object is not null, got=%T(+%v)", obj, obj) return false } return true } func TestReturnStatements(t *testing.T) { tests := []struct { input string expected int64 }{ {"rudisha 10", 10}, {"rudisha 10; 9;", 10}, {"rudisha 2 * 5; 9;", 10}, {"9; rudisha 2 * 5; 9;", 10}, } for _, tt := range tests { evaluated := testEval(tt.input) testIntegerObject(t, evaluated, tt.expected) } } func TestErrorHandling(t *testing.T) { tests := []struct { input string expectedMessage string }{ { "5 + kweli", "Mstari 1: Aina Hazilingani: NAMBA + BOOLEAN", }, { "5 + kweli; 5;", "Mstari 1: Aina Hazilingani: NAMBA + BOOLEAN", }, { "-kweli", "Mstari 1: Operesheni Haieleweki: -BOOLEAN", }, { "kweli + sikweli", "Mstari 1: Operesheni Haieleweki: BOOLEAN + BOOLEAN", }, { "5; kweli + sikweli; 5", "Mstari 1: Operesheni Haieleweki: BOOLEAN + BOOLEAN", }, { "kama (10 > 1) { kweli + sikweli;}", "Mstari 1: Operesheni Haieleweki: BOOLEAN + BOOLEAN", }, { ` kama (10 > 1) { kama (10 > 1) { rudisha kweli + kweli; } rudisha 1; } `, "Mstari 4: Operesheni Haieleweki: BOOLEAN + BOOLEAN", }, { "bangi", "Mstari 1: Neno Halifahamiki: bangi", }, { `"Habari" - "Habari"`, "Mstari 1: Operesheni Haieleweki: NENO - NENO", }, { `{"jina": "Avi"}[unda(x) {x}];`, "Mstari 1: Samahani, UNDO (FUNCTION) haitumiki kama ufunguo", }, } for _, tt := range tests { evaluated := testEval(tt.input) errObj, ok := evaluated.(*object.Error) if !ok { t.Errorf("no error object return, got=%T(%+v)", evaluated, evaluated) continue } if errObj.Message != fmt.Sprintf(tt.expectedMessage) { t.Errorf("wrong error message, expected=%q, got=%q", fmt.Sprintf(tt.expectedMessage), errObj.Message) } } } func TestLetStatement(t *testing.T) { tests := []struct { input string expected int64 }{ {"fanya a = 5; a;", 5}, {"fanya a = 5 * 5; a;", 25}, {"fanya a = 5; fanya b = a; b;", 5}, {"fanya a = 5; fanya b = a; fanya c = a + b + 5; c;", 15}, } for _, tt := range tests { testIntegerObject(t, testEval(tt.input), tt.expected) } } func TestFunctionObject(t *testing.T) { input := "unda(x) { x + 2 ;};" evaluated := testEval(input) unda, ok := evaluated.(*object.Function) if !ok { t.Fatalf("object is not a Function, got=%T(%+v)", evaluated, evaluated) } if len(unda.Parameters) != 1 { t.Fatalf("function has wrong parameters,Parameters=%+v", unda.Parameters) } if unda.Parameters[0].String() != "x" { t.Fatalf("parameter is not x, got=%q", unda.Parameters[0]) } expectedBody := "(x + 2)" if unda.Body.String() != expectedBody { t.Fatalf("body is not %q, got=%q", expectedBody, unda.Body.String()) } } func TestFunctionApplication(t *testing.T) { tests := []struct { input string expected int64 }{ {"fanya mfano = unda(x) {x;}; mfano(5);", 5}, {"fanya mfano = unda(x) {rudisha x;}; mfano(5);", 5}, {"fanya double = unda(x) { x * 2;}; double(5);", 10}, {"fanya add = unda(x, y) {x + y;}; add(5,5);", 10}, {"fanya add = unda(x, y) {x + y;}; add(5 + 5, add(5, 5));", 20}, {"unda(x) {x;}(5)", 5}, } for _, tt := range tests { testIntegerObject(t, testEval(tt.input), tt.expected) } } func TestClosures(t *testing.T) { input := ` fanya newAdder = unda(x) { unda(y) { x + y}; }; fanya addTwo = newAdder(2); addTwo(2); ` testIntegerObject(t, testEval(input), 4) } func TestStringLiteral(t *testing.T) { input := `"Habari yako!"` evaluated := testEval(input) str, ok := evaluated.(*object.String) if !ok { t.Fatalf("Object is not string, got=%T(%+v)", evaluated, evaluated) } if str.Value != "Habari yako!" { t.Errorf("String has wrong value, got=%q", str.Value) } } func TestStringconcatenation(t *testing.T) { input := `"Mambo" + " " + "Vipi" + "?"` evaluated := testEval(input) str, ok := evaluated.(*object.String) if !ok { t.Fatalf("object is not a string, got=%T(%+v)", evaluated, evaluated) } if str.Value != "Mambo Vipi?" { t.Errorf("String has wrong value, got=%q", str.Value) } } func TestStringMultiplyInteger(t *testing.T) { input := `"Mambo" * 4` evaluated := testEval(input) str, ok := evaluated.(*object.String) if !ok { t.Fatalf("object is not a string, got=%T(%+v)", evaluated, evaluated) } if str.Value != "MamboMamboMamboMambo" { t.Errorf("String has wrong value, got=%q", str.Value) } } // func TestBuiltinFunctions(t *testing.T) { // tests := []struct { // input string // expected interface{} // }{ // {`jumla()`, "Hoja hazilingani, tunahitaji=1, tumepewa=0"}, // {`jumla("")`, "Samahani, hii function haitumiki na NENO"}, // {`jumla(1)`, "Samahani, hii function haitumiki na NAMBA"}, // {`jumla([1,2,3])`, 6}, // {`jumla([1,2,3.4])`, 6.4}, // {`jumla([1.1,2.5,3.4])`, 7}, // {`jumla([1.1,2.5,"q"])`, "Samahani namba tu zinahitajika"}, // } // for _, tt := range tests { // evaluated := testEval(tt.input) // switch expected := tt.expected.(type) { // case int: // testIntegerObject(t, evaluated, int64(expected)) // case float64: // testFloatObject(t, evaluated, float64(expected)) // case string: // errObj, ok := evaluated.(*object.Error) // if !ok { // t.Errorf("Object is not Error, got=%T(%+v)", evaluated, evaluated) // continue // } // if errObj.Message != fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, expected) { // t.Errorf("Wrong eror message, expected=%q, got=%q", expected, errObj.Message) // } // } // } // } func TestArrayLiterals(t *testing.T) { input := "[1, 2 * 2, 3 + 3]" evaluated := testEval(input) result, ok := evaluated.(*object.Array) if !ok { t.Fatalf("Object is not an Array, got=%T(%+v)", evaluated, evaluated) } if len(result.Elements) != 3 { t.Fatalf("Array has wrong number of elements, got=%d", len(result.Elements)) } testIntegerObject(t, result.Elements[0], 1) testIntegerObject(t, result.Elements[1], 4) testIntegerObject(t, result.Elements[2], 6) } func TestArrayIndexExpressions(t *testing.T) { tests := []struct { input string expected interface{} }{ { "[1, 2, 3][0]", 1, }, { "[1, 2, 3][1]", 2, }, { "[1, 2, 3][2]", 3, }, { "fanya i = 0; [1][i];", 1, }, { "fanya myArr = [1, 2, 3]; myArr[2];", 3, }, { "[1, 2, 3][3]", nil, }, { "[1, 2, 3][-1]", nil, }, } for _, tt := range tests { evaluated := testEval(tt.input) integer, ok := tt.expected.(int) if ok { testIntegerObject(t, evaluated, int64(integer)) } else { testNullObject(t, evaluated) } } } func TestDictLiterals(t *testing.T) { input := `fanya two = "two"; { "one": 10 - 9, two: 1 +1, "thr" + "ee": 6 / 2, 4: 4, kweli: 5, sikweli: 6 }` evaluated := testEval(input) result, ok := evaluated.(*object.Dict) if !ok { t.Fatalf("Eval didn't return a dict, got=%T(%+v)", evaluated, evaluated) } expected := map[object.HashKey]int64{ (&object.String{Value: "one"}).HashKey(): 1, (&object.String{Value: "two"}).HashKey(): 2, (&object.String{Value: "three"}).HashKey(): 3, (&object.Integer{Value: 4}).HashKey(): 4, TRUE.HashKey(): 5, FALSE.HashKey(): 6, } if len(result.Pairs) != len(expected) { t.Fatalf("Dict has wrong number of pairs, got=%d", len(result.Pairs)) } for expectedKey, expectedValue := range expected { pair, ok := result.Pairs[expectedKey] if !ok { t.Errorf("No pair for give key") } testIntegerObject(t, pair.Value, expectedValue) } } func TestDictIndexExpression(t *testing.T) { tests := []struct { input string expected interface{} }{ { `{"foo": 5}["foo"]`, 5, }, { `{"foo": 5}["bar"]`, nil, }, { `fanya key = "foo"; {"foo": 5}[key]`, 5, }, { `{}["foo"]`, nil, }, { `{5: 5}[5]`, 5, }, { `{kweli: 5}[kweli]`, 5, }, { `{sikweli: 5}[sikweli]`, 5, }, } for _, tt := range tests { evaluated := testEval(tt.input) integer, ok := tt.expected.(int) if ok { testIntegerObject(t, evaluated, int64(integer)) } else { testNullObject(t, evaluated) } } } func TestPrefixInteger(t *testing.T) { tests := []struct { input string expected interface{} }{ { "-4", -4, }, { "+5", 5, }, } for _, tt := range tests { evaluated := testEval(tt.input) integer, ok := tt.expected.(int) if !ok { t.Errorf("Object is not an integer") } testIntegerObject(t, evaluated, int64(integer)) } } func TestPrefixFloat(t *testing.T) { tests := []struct { input string expected interface{} }{ { "-4.4", -4.4, }, { "+5.5", 5.5, }, } for _, tt := range tests { evaluated := testEval(tt.input) float, ok := tt.expected.(float64) if !ok { t.Errorf("Object is not a float") } testFloatObject(t, evaluated, float) } } func TestInExpression(t *testing.T) { tests := []struct { input string expected bool }{ { "'a' ktk 'habari'", true, }, { "'c' ktk 'habari'", false, }, { "1 ktk [1, 2, 3]", true, }, { "4 ktk [1, 2, 3]", false, }, { "'a' ktk {'a': 'apple', 'b': 'banana'}", true, }, { "'apple' ktk {'a': 'apple', 'b': 'banana'}", false, }, { "'c' ktk {'a': 'apple', 'b': 'banana'}", false, }, } for _, tt := range tests { evaluated := testEval(tt.input) testBooleanObject(t, evaluated, tt.expected) } } func TestArrayConcatenation(t *testing.T) { tests := []struct { input string expected string }{ { "['a', 'b', 'c'] + [1, 2, 3]", "[a, b, c, 1, 2, 3]", }, { "[1, 2, 3] * 4", "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]", }, { "4 * [1, 2, 3]", "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]", }, } for _, tt := range tests { evaluated := testEval(tt.input) arr, ok := evaluated.(*object.Array) if !ok { t.Fatalf("Object is not an array, got=%T(%+v)", evaluated, evaluated) } if arr.Inspect() != tt.expected { t.Errorf("Array has wrong values, got=%s want=%s", arr.Inspect(), tt.expected) } } } func TestDictConcatenation(t *testing.T) { tests := []struct { input string expected map[string]string }{ { input: "{'a': 'apple', 'b': 'banana'} + {'c': 'cat'}", expected: map[string]string{"a": "apple", "b": "banana", "c": "cat"}, }, { input: "{'a':'bbb'} + {'a':'ccc'}", expected: map[string]string{"a": "ccc"}, }, } for _, tt := range tests { evaluated := testEval(tt.input) dict, ok := evaluated.(*object.Dict) if !ok { t.Fatalf("Object is not an dict, got=%T(%+v)", evaluated, evaluated) } if len(dict.Pairs) != len(tt.expected) { t.Errorf("Dictionary has wrong number of pairs, got=%d want=%d", len(dict.Pairs), len(tt.expected)) } } } func TestPostfixExpression(t *testing.T) { inttests := []struct { input string expected int64 }{ { "a=5; a++", 6, }, { "a=5; a--", 4, }, } for _, tt := range inttests { evaluated := testEval(tt.input) integer, ok := evaluated.(*object.Integer) if !ok { t.Fatalf("Object is not an integer, got=%T(%+v)", evaluated, evaluated) } testIntegerObject(t, integer, tt.expected) } floattests := []struct { input string expected float64 }{ { "a=5.5; a++", 6.5, }, { "a=5.5; a--", 4.5, }, } for _, tt := range floattests { evaluated := testEval(tt.input) float, ok := evaluated.(*object.Float) if !ok { t.Fatalf("Object is not an float, got=%T(%+v)", evaluated, evaluated) } testFloatObject(t, float, tt.expected) } } func TestWhileLoop(t *testing.T) { input := ` i = 10 wakati (i > 0){ i-- } i ` evaluated := testEval(input) i, ok := evaluated.(*object.Integer) if !ok { t.Fatalf("Object is not an integer, got=%T(+%v)", evaluated, evaluated) } if i.Value != 0 { t.Errorf("Incorrect value, want=0 got=%d", i.Value) } } func TestForLoop(t *testing.T) { input := ` output = "" kwa i ktk "mojo" { output += i } output ` evaluated := testEval(input) i, ok := evaluated.(*object.String) if !ok { t.Fatalf("Object is not a string, got=%T(+%v)", evaluated, evaluated) } if i.Value != "mojo" { t.Errorf("Wrong value: want=%s got=%s", "mojo", i.Value) } } func TestBreakLoop(t *testing.T) { input := ` i = 0 wakati (i < 10) { kama (i == 5) { vunja } i++ } i ` evaluated := testEval(input) i, ok := evaluated.(*object.Integer) if !ok { t.Fatalf("Object is not an integer, got=%T(+%v)", evaluated, evaluated) } if i.Value != 5 { t.Errorf("Wrong value: want=5, got=%d", i.Value) } input = ` output = "" kwa i ktk "mojo" { output += i kama (i == 'o') { vunja } } output ` evaluatedFor := testEval(input) j, ok := evaluatedFor.(*object.String) if !ok { t.Fatalf("Object is not a string, got=%T", evaluated) } if j.Value != "mo" { t.Errorf("Wrong value: want=%s, got=%s", "mo", j.Value) } } func TestContinueLoop(t *testing.T) { input := ` i = 0 wakati (i < 10) { i++ kama (i == 5) { endelea } i++ } i ` evaluated := testEval(input) i, ok := evaluated.(*object.Integer) if !ok { t.Fatalf("Object is not an integer, got=%T(+%v)", evaluated, evaluated) } if i.Value != 11 { t.Errorf("Wrong value: want=11, got=%d", i.Value) } input = ` output = "" kwa i ktk "mojo" { kama (i == 'o') { endelea } output += i } output ` evaluatedFor := testEval(input) j, ok := evaluatedFor.(*object.String) if !ok { t.Fatalf("Object is not a string, got=%T", evaluated) } if j.Value != "mj" { t.Errorf("Wrong value: want=%s, got=%s", "mj", j.Value) } } func TestSwitchStatement(t *testing.T) { tests := []struct { input string expected interface{} }{ { ` i = 5 badili (i) { ikiwa 2 { output = 2 } ikiwa 5 { output = 5 } kawaida { output = "haijulikani" } } output `, 5, }, { ` i = 5 badili (i) { ikiwa 2 { output = 2 } kawaida { output = "haijulikani" } } output `, "haijulikani", }, { ` i = 5 badili (i) { ikiwa 5 { output = 5 } ikiwa 2 { output = 2 } kawaida { output = "haijulikani" } } output `, 5, }, } for _, tt := range tests { evaluated := testEval(tt.input) switch expected := tt.expected.(type) { case int: testIntegerObject(t, evaluated, int64(expected)) case string: s, ok := evaluated.(*object.String) if !ok { t.Fatalf("Object is not a string, got=%T", evaluated) } if s.Value != tt.expected { t.Errorf("Wrong Value, want='haijulikani', got=%s", s.Value) } } } } func TestAssignEqual(t *testing.T) { tests := []struct { input string expected interface{} }{ {"a = 5; a += 5", 10, }, { "a = 5; a -= 5", 0, }, { "a = 5; a *= 10", 50, }, { "a = 100; a /= 4", 25, }, { ` a = [1, 2, 3] a[0] += 500 a[0] `, 501, }, { ` a = "mambo" a += " vipi" `, "mambo vipi", }, { "a = 5.5; a += 4.5", 10.0, }, { "a = 11.3; a -= 0.8", 10.5, }, { "a = 0.4; a /= 2", 0.2, }, { "a = 0.1; a *= 10", 1.0, }, } for _, tt := range tests { evaluated := testEval(tt.input) switch expected := tt.expected.(type) { case int: testIntegerObject(t, evaluated, int64(expected)) case float64: testFloatObject(t, evaluated, float64(expected)) case string: s, ok := evaluated.(*object.String) if !ok { t.Fatalf("Object not a string, got=%T", evaluated) } if s.Value != tt.expected { t.Errorf("Wrong value, want=%s, got=%s", tt.expected, s.Value) } } } } func TestStringMethods(t *testing.T) { tests := []struct { input string expected interface{} }{ { "'mambo'.idadi()", 5, }, { "'mambo'.herufikubwa()", "MAMBO", }, { "'MaMbO'.herufindogo()", "mambo", }, { "'habari'.gawa('a')", "[h, b, ri]", }, } for _, tt := range tests { evaluated := testEval(tt.input) switch expected := tt.expected.(type) { case int: testIntegerObject(t, evaluated, int64(expected)) case string: switch eval := evaluated.(type) { case *object.String: s, ok := evaluated.(*object.String) if !ok { t.Fatalf("Object not of type string, got=%T", eval) } if s.Value != tt.expected { t.Errorf("Wrong value: want=%s, got=%s", tt.expected, s.Value) } case *object.Array: arr, ok := evaluated.(*object.Array) if !ok { t.Fatalf("Object not of type array, got=%T", eval) } if arr.Inspect() != tt.expected { t.Errorf("Wrong value: want=%s, got=%s", tt.expected, arr.Inspect()) } } } } } func TestTimeModule(t *testing.T) { input := ` tumia muda muda.hasahivi() ` evaluated := testEval(input) muda, ok := evaluated.(*object.Time) if !ok { t.Fatalf("Object is not a time object, got=%T", evaluated) } _, err := time.Parse("15:04:05 02-01-2006", muda.TimeValue) if err != nil { t.Errorf("Wrong time value: got=%v", err) } } ================================================ FILE: evaluator/forin.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalForInExpression(fie *ast.ForIn, env *object.Environment, line int) object.Object { iterable := Eval(fie.Iterable, env) existingKeyIdentifier, okk := env.Get(fie.Key) // again, stay safe existingValueIdentifier, okv := env.Get(fie.Value) defer func() { // restore them later on if okk { env.Set(fie.Key, existingKeyIdentifier) } if okv { env.Set(fie.Value, existingValueIdentifier) } }() switch i := iterable.(type) { case object.Iterable: defer func() { i.Reset() }() return loopIterable(i.Next, env, fie) default: return newError("Mstari %d: Huwezi kufanya operesheni hii na %s", line, i.Type()) } } ================================================ FILE: evaluator/function.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalFunction(node *ast.FunctionLiteral, env *object.Environment) object.Object { function := &object.Function{ Name: node.Name, Parameters: node.Parameters, Defaults: node.Defaults, Body: node.Body, Env: env, } return function } ================================================ FILE: evaluator/identifier.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object { if val, ok := env.Get(node.Value); ok { return val } if builtin, ok := builtins[node.Value]; ok { return builtin } return newError("Mstari %d: Neno Halifahamiki: %s", node.Token.Line, node.Value) } ================================================ FILE: evaluator/if.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object { condition := Eval(ie.Condition, env) if isError(condition) { return condition } if isTruthy(condition) { return Eval(ie.Consequence, env) } else if ie.Alternative != nil { return Eval(ie.Alternative, env) } else { return NULL } } ================================================ FILE: evaluator/import.go ================================================ package evaluator import ( "fmt" "os" "path/filepath" "strings" "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/lexer" "github.com/NuruProgramming/Nuru/module" "github.com/NuruProgramming/Nuru/object" "github.com/NuruProgramming/Nuru/parser" ) var searchPaths []string func evalImport(node *ast.Import, env *object.Environment) object.Object { for k, v := range node.Identifiers { if mod, ok := module.Mapper[v.Value]; ok { env.Set(k, mod) } else { return evalImportFile(k, v, env) } } return NULL } func evalImportFile(name string, ident *ast.Identifier, env *object.Environment) object.Object { addSearchPath("") filename := findFile(name) if filename == "" { return newError("Moduli %s haipo", name) } var scope *object.Environment scope, err := evaluateFile(filename, env) if err != nil { return err } return importFile(name, ident, env, scope) } func addSearchPath(path string) { searchPaths = append(searchPaths, path) } func findFile(name string) string { basename := fmt.Sprintf("%s.nr", name) for _, path := range searchPaths { file := filepath.Join(path, basename) if fileExists(file) { return file } } return "" } func fileExists(file string) bool { _, err := os.Stat(file) return err == nil } func evaluateFile(file string, env *object.Environment) (*object.Environment, object.Object) { source, err := os.ReadFile(file) if err != nil { return nil, &object.Error{Message: fmt.Sprintf("Tumeshindwa kufungua pakeji: %s", file)} } l := lexer.New(string(source)) p := parser.New(l) program := p.ParseProgram() if len(p.Errors()) != 0 { return nil, &object.Error{Message: fmt.Sprintf("Pakeji %s ina makosa yafuatayo:\n%s", file, strings.Join(p.Errors(), "\n"))} } scope := object.NewEnvironment() result := Eval(program, scope) if isError(result) { return nil, result } return scope, nil } func importFile(name string, ident *ast.Identifier, env *object.Environment, scope *object.Environment) object.Object { value, ok := scope.Get(ident.Value) if !ok { return newError("%s sio pakeji", name) } env.Set(name, value) return NULL } ================================================ FILE: evaluator/in.go ================================================ package evaluator import ( "strings" "github.com/NuruProgramming/Nuru/object" ) func evalInExpression(left, right object.Object, line int) object.Object { switch right.(type) { case *object.String: return evalInStringExpression(left, right) case *object.Array: return evalInArrayExpression(left, right) case *object.Dict: return evalInDictExpression(left, right, line) default: return FALSE } } func evalInStringExpression(left, right object.Object) object.Object { if left.Type() != object.STRING_OBJ { return FALSE } leftVal := left.(*object.String) rightVal := right.(*object.String) found := strings.Contains(rightVal.Value, leftVal.Value) return nativeBoolToBooleanObject(found) } func evalInDictExpression(left, right object.Object, line int) object.Object { leftVal, ok := left.(object.Hashable) if !ok { return newError("Mstari %d: Huwezi kutumia kama 'key': %s", line, left.Type()) } key := leftVal.HashKey() rightVal := right.(*object.Dict).Pairs _, ok = rightVal[key] return nativeBoolToBooleanObject(ok) } func evalInArrayExpression(left, right object.Object) object.Object { rightVal := right.(*object.Array) switch leftVal := left.(type) { case *object.Null: for _, v := range rightVal.Elements { if v.Type() == object.NULL_OBJ { return TRUE } } case *object.String: for _, v := range rightVal.Elements { if v.Type() == object.STRING_OBJ { elem := v.(*object.String) if elem.Value == leftVal.Value { return TRUE } } } case *object.Integer: for _, v := range rightVal.Elements { if v.Type() == object.INTEGER_OBJ { elem := v.(*object.Integer) if elem.Value == leftVal.Value { return TRUE } } } case *object.Float: for _, v := range rightVal.Elements { if v.Type() == object.FLOAT_OBJ { elem := v.(*object.Float) if elem.Value == leftVal.Value { return TRUE } } } } return FALSE } ================================================ FILE: evaluator/index.go ================================================ package evaluator import "github.com/NuruProgramming/Nuru/object" func evalIndexExpression(left, index object.Object, line int) object.Object { switch { case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ: return evalArrayIndexExpression(left, index) case left.Type() == object.ARRAY_OBJ && index.Type() != object.INTEGER_OBJ: return newError("Mstari %d: Tafadhali tumia number, sio: %s", line, index.Type()) case left.Type() == object.DICT_OBJ: return evalDictIndexExpression(left, index, line) default: return newError("Mstari %d: Operesheni hii haiwezekani kwa: %s", line, left.Type()) } } func evalArrayIndexExpression(array, index object.Object) object.Object { arrayObject := array.(*object.Array) idx := index.(*object.Integer).Value max := int64(len(arrayObject.Elements) - 1) if idx < 0 || idx > max { return NULL } return arrayObject.Elements[idx] } func evalDictIndexExpression(dict, index object.Object, line int) object.Object { dictObject := dict.(*object.Dict) key, ok := index.(object.Hashable) if !ok { return newError("Mstari %d: Samahani, %s haitumiki kama ufunguo", line, index.Type()) } pair, ok := dictObject.Pairs[key.HashKey()] if !ok { return NULL } return pair.Value } ================================================ FILE: evaluator/infix.go ================================================ package evaluator import ( "math" "strings" "github.com/NuruProgramming/Nuru/object" ) func evalInfixExpression(operator string, left, right object.Object, line int) object.Object { if right == nil { return newError("Mstari %d: Umekosea hapa", line) } if left == nil { return newError("Mstari %d: Umekosea hapa", line) } switch { case operator == "ktk": return evalInExpression(left, right, line) case left.Type() == object.STRING_OBJ && right.Type() == object.STRING_OBJ: return evalStringInfixExpression(operator, left, right, line) case operator == "+" && left.Type() == object.DICT_OBJ && right.Type() == object.DICT_OBJ: leftVal := left.(*object.Dict).Pairs rightVal := right.(*object.Dict).Pairs pairs := make(map[object.HashKey]object.DictPair) for k, v := range leftVal { pairs[k] = v } for k, v := range rightVal { pairs[k] = v } return &object.Dict{Pairs: pairs} case operator == "+" && left.Type() == object.ARRAY_OBJ && right.Type() == object.ARRAY_OBJ: leftVal := left.(*object.Array).Elements rightVal := right.(*object.Array).Elements elements := append(leftVal, rightVal...) return &object.Array{Elements: elements} case operator == "*" && left.Type() == object.ARRAY_OBJ && right.Type() == object.INTEGER_OBJ: leftVal := left.(*object.Array).Elements rightVal := int(right.(*object.Integer).Value) elements := leftVal for i := rightVal; i > 1; i-- { elements = append(elements, leftVal...) } return &object.Array{Elements: elements} case operator == "*" && left.Type() == object.INTEGER_OBJ && right.Type() == object.ARRAY_OBJ: leftVal := int(left.(*object.Integer).Value) rightVal := right.(*object.Array).Elements elements := rightVal for i := leftVal; i > 1; i-- { elements = append(elements, rightVal...) } return &object.Array{Elements: elements} case operator == "*" && left.Type() == object.STRING_OBJ && right.Type() == object.INTEGER_OBJ: leftVal := left.(*object.String).Value rightVal := right.(*object.Integer).Value return &object.String{Value: strings.Repeat(leftVal, int(rightVal))} case operator == "*" && left.Type() == object.INTEGER_OBJ && right.Type() == object.STRING_OBJ: leftVal := left.(*object.Integer).Value rightVal := right.(*object.String).Value return &object.String{Value: strings.Repeat(rightVal, int(leftVal))} case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ: return evalIntegerInfixExpression(operator, left, right, line) case left.Type() == object.FLOAT_OBJ && right.Type() == object.FLOAT_OBJ: return evalFloatInfixExpression(operator, left, right, line) case left.Type() == object.INTEGER_OBJ && right.Type() == object.FLOAT_OBJ: return evalFloatIntegerInfixExpression(operator, left, right, line) case left.Type() == object.FLOAT_OBJ && right.Type() == object.INTEGER_OBJ: return evalFloatIntegerInfixExpression(operator, left, right, line) case operator == "==": return nativeBoolToBooleanObject(left == right) case operator == "!=": return nativeBoolToBooleanObject(left != right) case left.Type() == object.BOOLEAN_OBJ && right.Type() == object.BOOLEAN_OBJ: return evalBooleanInfixExpression(operator, left, right, line) case left.Type() != right.Type(): return newError("Mstari %d: Aina Hazilingani: %s %s %s", line, left.Type(), operator, right.Type()) default: return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", line, left.Type(), operator, right.Type()) } } func evalFloatIntegerInfixExpression(operator string, left, right object.Object, line int) object.Object { var leftVal, rightVal float64 if left.Type() == object.FLOAT_OBJ { leftVal = left.(*object.Float).Value rightVal = float64(right.(*object.Integer).Value) } else { leftVal = float64(left.(*object.Integer).Value) rightVal = right.(*object.Float).Value } var val float64 switch operator { case "+": val = leftVal + rightVal case "-": val = leftVal - rightVal case "*": val = leftVal * rightVal case "**": val = math.Pow(float64(leftVal), float64(rightVal)) case "/": val = leftVal / rightVal case "%": val = math.Mod(leftVal, rightVal) case "<": return nativeBoolToBooleanObject(leftVal < rightVal) case "<=": return nativeBoolToBooleanObject(leftVal <= rightVal) case ">": return nativeBoolToBooleanObject(leftVal > rightVal) case ">=": return nativeBoolToBooleanObject(leftVal >= rightVal) case "==": return nativeBoolToBooleanObject(leftVal == rightVal) case "!=": return nativeBoolToBooleanObject(leftVal != rightVal) default: return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", line, left.Type(), operator, right.Type()) } if math.Mod(val, 1) == 0 { return &object.Integer{Value: int64(val)} } else { return &object.Float{Value: val} } } func evalStringInfixExpression(operator string, left, right object.Object, line int) object.Object { leftVal := left.(*object.String).Value rightVal := right.(*object.String).Value switch operator { case "+": return &object.String{Value: leftVal + rightVal} case "==": return nativeBoolToBooleanObject(leftVal == rightVal) case "!=": return nativeBoolToBooleanObject(leftVal != rightVal) default: return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", line, left.Type(), operator, right.Type()) } } func evalBooleanInfixExpression(operator string, left, right object.Object, line int) object.Object { leftVal := left.(*object.Boolean).Value rightVal := right.(*object.Boolean).Value switch operator { case "&&": return nativeBoolToBooleanObject(leftVal && rightVal) case "||": return nativeBoolToBooleanObject(leftVal || rightVal) default: return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", line, left.Type(), operator, right.Type()) } } func evalFloatInfixExpression(operator string, left, right object.Object, line int) object.Object { leftVal := left.(*object.Float).Value rightVal := right.(*object.Float).Value switch operator { case "+": return &object.Float{Value: leftVal + rightVal} case "-": return &object.Float{Value: leftVal - rightVal} case "*": return &object.Float{Value: leftVal * rightVal} case "**": return &object.Float{Value: math.Pow(float64(leftVal), float64(rightVal))} case "/": return &object.Float{Value: leftVal / rightVal} case "<": return nativeBoolToBooleanObject(leftVal < rightVal) case "<=": return nativeBoolToBooleanObject(leftVal <= rightVal) case ">": return nativeBoolToBooleanObject(leftVal > rightVal) case ">=": return nativeBoolToBooleanObject(leftVal >= rightVal) case "==": return nativeBoolToBooleanObject(leftVal == rightVal) case "!=": return nativeBoolToBooleanObject(leftVal != rightVal) default: return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", line, left.Type(), operator, right.Type()) } } func evalIntegerInfixExpression(operator string, left, right object.Object, line int) object.Object { leftVal := left.(*object.Integer).Value rightVal := right.(*object.Integer).Value switch operator { case "+": return &object.Integer{Value: leftVal + rightVal} case "-": return &object.Integer{Value: leftVal - rightVal} case "*": return &object.Integer{Value: leftVal * rightVal} case "**": return &object.Float{Value: float64(math.Pow(float64(leftVal), float64(rightVal)))} case "/": x := float64(leftVal) / float64(rightVal) if math.Mod(x, 1) == 0 { return &object.Integer{Value: int64(x)} } else { return &object.Float{Value: x} } case "%": return &object.Integer{Value: leftVal % rightVal} case "<": return nativeBoolToBooleanObject(leftVal < rightVal) case "<=": return nativeBoolToBooleanObject(leftVal <= rightVal) case ">": return nativeBoolToBooleanObject(leftVal > rightVal) case ">=": return nativeBoolToBooleanObject(leftVal >= rightVal) case "==": return nativeBoolToBooleanObject(leftVal == rightVal) case "!=": return nativeBoolToBooleanObject(leftVal != rightVal) default: return newError("Mstari %d: Operesheni Haieleweki: %s %s %s", line, left.Type(), operator, right.Type()) } } ================================================ FILE: evaluator/method.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalMethodExpression(node *ast.MethodExpression, env *object.Environment) object.Object { obj := Eval(node.Object, env) if isError(obj) { return obj } args := evalExpressions(node.Arguments, env) if len(args) == 1 && isError(args[0]) { return args[0] } defs := make(map[string]object.Object) for k, v := range node.Defaults { defs[k] = Eval(v, env) } return applyMethod(obj, node.Method, args, defs, node.Token.Line) } func applyMethod(obj object.Object, method ast.Expression, args []object.Object, defs map[string]object.Object, l int) object.Object { switch obj := obj.(type) { case *object.String: return obj.Method(method.(*ast.Identifier).Value, args) case *object.File: return obj.Method(method.(*ast.Identifier).Value, args) case *object.Time: return obj.Method(method.(*ast.Identifier).Value, args, defs) case *object.Array: switch method.(*ast.Identifier).Value { case "map": return maap(obj, args) case "chuja": return filter(obj, args) default: return obj.Method(method.(*ast.Identifier).Value, args) } case *object.Module: if fn, ok := obj.Functions[method.(*ast.Identifier).Value]; ok { return fn(args, defs) } case *object.Instance: if fn, ok := obj.Package.Scope.Get(method.(*ast.Identifier).Value); ok { fn.(*object.Function).Env.Set("@", obj) ret := applyFunction(fn, args, l) fn.(*object.Function).Env.Del("@") return ret } case *object.Package: if fn, ok := obj.Scope.Get(method.(*ast.Identifier).Value); ok { fn.(*object.Function).Env.Set("@", obj) ret := applyFunction(fn, args, l) fn.(*object.Function).Env.Del("@") return ret } } return newError("Samahani, %s haina function '%s()'", obj.Inspect(), method.(*ast.Identifier).Value) } // /////////////////////////////////////////////////////////////// // //////// Some methods here because of loop dependency //////// // ///////////////////////////////////////////////////////////// func maap(a *object.Array, args []object.Object) object.Object { if len(args) != 1 && args[0].Type() != object.FUNCTION_OBJ { return newError("Samahani, hoja sii sahihi") } fn, ok := args[0].(*object.Function) if !ok { return newError("Samahani, hoja sii sahihi") } env := object.NewEnvironment() newArr := object.Array{Elements: []object.Object{}} for _, obj := range a.Elements { env.Set(fn.Parameters[0].Value, obj) r := Eval(fn.Body, env) if o, ok := r.(*object.ReturnValue); ok { r = o.Value } newArr.Elements = append(newArr.Elements, r) } return &newArr } func filter(a *object.Array, args []object.Object) object.Object { if len(args) != 1 && args[0].Type() != object.FUNCTION_OBJ { return newError("Samahani, hoja sii sahihi") } fn, ok := args[0].(*object.Function) if !ok { return newError("Samahani, hoja sii sahihi") } env := object.NewEnvironment() newArr := object.Array{Elements: []object.Object{}} for _, obj := range a.Elements { env.Set(fn.Parameters[0].Value, obj) cond := Eval(fn.Body, env) if cond.Inspect() == "kweli" { newArr.Elements = append(newArr.Elements, obj) } } return &newArr } ================================================ FILE: evaluator/package.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalPackage(node *ast.Package, env *object.Environment) object.Object { pakeji := &object.Package{ Name: node.Name, Env: env, Scope: object.NewEnclosedEnvironment(env), } Eval(node.Block, pakeji.Scope) env.Set(node.Name.Value, pakeji) return pakeji } ================================================ FILE: evaluator/postfix.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalPostfixExpression(env *object.Environment, operator string, node *ast.PostfixExpression) object.Object { val, ok := env.Get(node.Token.Literal) if !ok { return newError("Tumia KITAMBULISHI CHA NAMBA AU DESIMALI, sio %s", node.Token.Type) } switch operator { case "++": switch arg := val.(type) { case *object.Integer: v := arg.Value + 1 return env.Set(node.Token.Literal, &object.Integer{Value: v}) case *object.Float: v := arg.Value + 1 return env.Set(node.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: %s sio kitambulishi cha namba. Tumia '++' na kitambulishi cha namba au desimali.\nMfano:\tfanya i = 2; i++", node.Token.Line, node.Token.Literal) } case "--": switch arg := val.(type) { case *object.Integer: v := arg.Value - 1 return env.Set(node.Token.Literal, &object.Integer{Value: v}) case *object.Float: v := arg.Value - 1 return env.Set(node.Token.Literal, &object.Float{Value: v}) default: return newError("Mstari %d: %s sio kitambulishi cha namba. Tumia '--' na kitambulishi cha namba au desimali.\nMfano:\tfanya i = 2; i++", node.Token.Line, node.Token.Literal) } default: return newError("Haifahamiki: %s", operator) } } ================================================ FILE: evaluator/prefix.go ================================================ package evaluator import "github.com/NuruProgramming/Nuru/object" func evalMinusPrefixOperatorExpression(right object.Object, line int) object.Object { switch obj := right.(type) { case *object.Integer: return &object.Integer{Value: -obj.Value} case *object.Float: return &object.Float{Value: -obj.Value} default: return newError("Mstari %d: Operesheni Haieleweki: -%s", line, right.Type()) } } func evalPlusPrefixOperatorExpression(right object.Object, line int) object.Object { switch obj := right.(type) { case *object.Integer: return &object.Integer{Value: obj.Value} case *object.Float: return &object.Float{Value: obj.Value} default: return newError("Mstari %d: Operesheni Haieleweki: +%s", line, right.Type()) } } func evalPrefixExpression(operator string, right object.Object, line int) object.Object { switch operator { case "!": return evalBangOperatorExpression(right) case "-": return evalMinusPrefixOperatorExpression(right, line) case "+": return evalPlusPrefixOperatorExpression(right, line) default: return newError("Mstari %d: Operesheni Haieleweki: %s%s", line, operator, right.Type()) } } ================================================ FILE: evaluator/property.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalPropertyExpression(node *ast.PropertyExpression, env *object.Environment) object.Object { left := Eval(node.Object, env) if isError(left) { return left } switch left.(type) { case *object.Instance: obj := left.(*object.Instance) prop := node.Property.(*ast.Identifier).Value if val, ok := obj.Env.Get(prop); ok { return val } case *object.Package: obj := left.(*object.Package) prop := node.Property.(*ast.Identifier).Value if val, ok := obj.Env.Get(prop); ok { return val } // case *object.Module: // mod := left.(*object.Module) // prop := node.Property.(*ast.Identifier).Value // if val, ok := mod.Properties[prop]; ok { // return val() // } } return newError("Value %s sii sahihi kwenye %s", node.Property.(*ast.Identifier).Value, left.Inspect()) } func evalPropertyAssignment(name *ast.PropertyExpression, val object.Object, env *object.Environment) object.Object { left := Eval(name.Object, env) if isError(left) { return left } switch left.(type) { case *object.Instance: obj := left.(*object.Instance) prop := name.Property.(*ast.Identifier).Value if _, ok := obj.Env.Get(prop); ok { obj.Env.Set(prop, val) return NULL } obj.Env.Set(prop, val) return NULL case *object.Package: obj := left.(*object.Package) prop := name.Property.(*ast.Identifier).Value if _, ok := obj.Env.Get(prop); ok { obj.Env.Set(prop, val) return NULL } obj.Env.Set(prop, val) return NULL default: return newError("Imeshindikana kuweka kwenye pakiti %s", left.Type()) } } ================================================ FILE: evaluator/switch.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) func evalSwitchStatement(se *ast.SwitchExpression, env *object.Environment) object.Object { obj := Eval(se.Value, env) for _, opt := range se.Choices { if opt.Default { continue } for _, val := range opt.Expr { out := Eval(val, env) if obj.Type() == out.Type() && obj.Inspect() == out.Inspect() { blockOut := evalBlockStatement(opt.Block, env) return blockOut } } } for _, opt := range se.Choices { if opt.Default { out := evalBlockStatement(opt.Block, env) return out } } return nil } ================================================ FILE: evaluator/type.go ================================================ package evaluator import ( "strconv" "github.com/NuruProgramming/Nuru/object" ) func convertToInteger(obj object.Object) object.Object { switch obj := obj.(type) { case *object.Integer: return obj case *object.Float: return &object.Integer{Value: int64(obj.Value)} case *object.String: i, err := strconv.ParseInt(obj.Value, 10, 64) if err != nil { return newError("Haiwezi kubadilisha '%s' kuwa NAMBA", obj.Value) } return &object.Integer{Value: i} case *object.Boolean: if obj.Value { return &object.Integer{Value: 1} } return &object.Integer{Value: 0} default: return newError("Haiwezi kubadilisha %s kuwa NAMBA", obj.Type()) } } func convertToFloat(obj object.Object) object.Object { switch obj := obj.(type) { case *object.Float: return obj case *object.Integer: return &object.Float{Value: float64(obj.Value)} case *object.String: f, err := strconv.ParseFloat(obj.Value, 64) if err != nil { return newError("Haiwezi kubadilisha '%s' kuwa DESIMALI", obj.Value) } return &object.Float{Value: f} case *object.Boolean: if obj.Value { return &object.Float{Value: 1.0} } return &object.Float{Value: 0.0} default: return newError("Haiwezi kubadilisha %s kuwa DESIMALI", obj.Type()) } } func convertToString(obj object.Object) object.Object { return &object.String{Value: obj.Inspect()} } func convertToBoolean(obj object.Object) object.Object { switch obj := obj.(type) { case *object.Boolean: return obj case *object.Integer: return &object.Boolean{Value: obj.Value != 0} case *object.Float: return &object.Boolean{Value: obj.Value != 0} case *object.String: return &object.Boolean{Value: len(obj.Value) > 0} case *object.Null: return &object.Boolean{Value: false} default: return &object.Boolean{Value: true} } } ================================================ FILE: evaluator/while.go ================================================ package evaluator import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/object" ) const MAX_ITERATIONS = 1_000_000 func evalWhileExpression(we *ast.WhileExpression, env *object.Environment) object.Object { var evaluated object.Object iterations := 0 for { iterations++ if iterations > MAX_ITERATIONS { return newError("mzunguko usio na mwisho umegunduliwa") } condition := Eval(we.Condition, env) if isError(condition) { return condition } if !isTruthy(condition) { break } evaluated = Eval(we.Consequence, env) if isError(evaluated) { return evaluated } if evaluated != nil && evaluated.Type() == object.BREAK_OBJ { return evaluated } } return evaluated } ================================================ FILE: examples/Astart.nr ================================================ /*############ A*(A-star) Algorithm ############## By @VictorKariuki https://github.com/VictorKariuki ################################################*/ // create a list of numbers fanya list = unda(first,last,interval){ fanya list = [first]; fanya i = first + interval; wakati(i < last){ list.sukuma(i) i+=interval; } rudisha list; } // Maths functions // find the absolute value of a number fanya abs_namba = unda(namba){ kama(namba < 0){ rudisha -1 * namba; } rudisha namba; } // square a number fanya square = unda(n, i, j){ fanya kati = (i+j)/2; fanya mul = kati * kati; fanya abs_diff = abs_namba(mul-n); kama (mul == n || abs_diff < 0.00001){ rudisha kati; }au kama(mul < n){ rudisha square(n,kati,j) }au{ rudisha square(n,i,kati) } } // find the square root of a number fanya sqrt = unda(namba){ kwa i ktk list(0,namba,1) { kama((i*i )== namba){ rudisha i; }au kama ((i*i )> namba){ rudisha square(namba,i-1,i) } } } // Main function fanya aStar = unda(start, goal) { // Initialize the open and closed lists fanya openList = [start]; fanya closedList = []; fanya reconstructPath = unda(node) { fanya path = [node]; wakati (node["parent"]) { path = [node["parent"]] + path; node = node["parent"]; } rudisha path; } fanya heuristic = unda(node1, node2) { // Calculate the Euclidean distance between the nodes' positions fanya dx = node1["x"] - node2["x"]; fanya dy = node1["y"] - node2["y"]; rudisha sqrt(dx * dx + dy * dy); } fanya findMinNode = unda(openList) { fanya i = 1; fanya minNode = openList[0]; wakati (i < openList.idadi()) { fanya node = openList[i]; kama (node["f"] < minNode["f"]) { minNode = node; } i++ } rudisha minNode; } fanya removeNodeFromArray = unda(array, node) { fanya newArray = []; fanya i = 1; wakati (i < array.idadi()) { kama (array[i] != node) { newArray.sukuma(array[i]); } i++; } rudisha newArray; } fanya urefu = unda(node1, node2) { // Assume all edges have a cost of 1 rudisha 1; } // Initialize the g and f scores of the starting node start["g"] = 0; start["f"] = start["g"] + heuristic(start, goal); // Start the search loop wakati (openList.idadi() > 0) { // Find the node with the lowest f score in the open list fanya current = findMinNode(openList); // Check kama the goal node has been reached kama (current == goal) { rudisha reconstructPath(current); } // Move the current node from the open to the closed list openList = removeNodeFromArray(openList, current); closedList.sukuma(current); // Explore the neighbors of the current node kwa neighbor ktk current["neighbors"] { // Skip neighbors that are in the closed list kama (neighbor ktk closedList) { endelea } // Calculate the tentative g score of the neighbor fanya tentativeG = start["g"] + urefu(current, neighbor); // Check kama the neighbor is in the open list fanya tentativeIsBetter = sikweli; kama (!(neighbor ktk openList)) { openList.sukuma(neighbor); tentativeIsBetter = kweli; } au kama (tentativeG < neighbor["g"]) { tentativeIsBetter = kweli; } // Update the neighbor's g score kama the tentative score is better kama (tentativeIsBetter) { neighbor["g"] = tentativeG; neighbor["f"] = neighbor["g"] + heuristic(neighbor, goal); neighbor["parent"] = current; } } } // kama the open list is empty, no path was found rudisha tupu; } // Define the nodes of the graph fanya nodeA = { "x": 0, "y": 0, "neighbors": [] }; fanya nodeB = { "x": 1, "y": 2, "neighbors": [] }; fanya nodeC = { "x": 3, "y": 1, "neighbors": [] }; fanya nodeD = { "x": 4, "y": 3, "neighbors": [] }; // Define the edges between the nodes nodeA["neighbors"] = [nodeB]; nodeB["neighbors"] = [nodeA, nodeC]; nodeC["neighbors"] = [nodeB, nodeD]; nodeD["neighbors"] = [nodeC]; // Call the A* function with the start and goal nodes and the heuristic and distance functions //fanya path = aStar(nodeA, nodeC); andika(nodeA); ================================================ FILE: examples/example.nr ================================================ // basics jina = "Nuru" andika(jina) // Nuru // lists orodha = [1, "pili", kweli] namba = [10, 20, 30] jina = namba[1] // jina is 20 namba[1] = 25 a = [1, 2, 3] b = [4, 5, 6] c = a + b // c is now [1, 2, 3, 4, 5, 6] namba = [10, 20, 30] andika(20 ktk namba) // will print kweli namba = [1, 2, 3, 4, 5] kwa thamani ktk namba { andika(thamani) } majina = ["Juma", "Asha", "Haruna"] kwa idx, jina ktk majina { andika(idx, "-", jina) } a = [1, 2, 3] urefu = a.idadi() andika(urefu) // will print 3 a = [1, 2, 3] a.sukuma("s", "g") andika(a) // will print [1, 2, 3, "s", "g"] a = [1, 2, 3] mwisho = a.yamwisho() andika(mwisho) // will print 3 b = [] mwisho = b.yamwisho() andika(mwisho) // will print tupu andika(1 > 2) // Output: `sikweli` andika(1 + 3 < 10) // Output: `kweli` a = 5 b = 10 c = 15 result = (a < b) && (b < c) kama (result) { andika("Both conditions are true") } sivyo { andika("At least one condition is false") } // Output: "Both conditions are true" andika(kweli && kweli) // Output: `kweli` andika(kweli && sikweli) // Output: `sikweli` andika(kweli || sikweli) // Output: `kweli` andika(sikweli || sikweli) // Output: `sikweli` andika(!kweli) // Output: `sikweli` andika(!sikweli) // Output: `kweli` namba = [1, 2, 3, 4, 5] kwa thamani ktk namba { kama (thamani % 2 == 0) { andika(thamani, "is even") } sivyo { andika(thamani, "is odd") } } // Output: // 1 is odd // 2 is even // 3 is odd // 4 is even // 5 is odd salamu = unda() { jina = jaza("Unaitwa nani? ") andika("Mambo vipi", jina) } salamu() aina(2) // Output: "NAMBA" aina("Nuru") // Output: "NENO" orodha = {"jina": "Juma", "umri": 25} k = { "jina": "Juma", "umri": 25, kweli: "kweli", "salimu": unda(x) { andika("habari", x) }, "sina value": tupu } andika(k[kweli]) // kweli andika(k["salimu"]("Juma")) // habari Juma k['umri'] = 30 andika(k['umri']) // 30 k["lugha"] = "Kiswahili" andika(k["lugha"]) // Kiswahili matunda = {"a": "apple", "b": "banana"} mboga = {"c": "carrot", "d": "daikon"} vyakula = matunda + mboga andika(vyakula) // {"a": "apple", "b": "banana", "c": "carrot", "d": "daikon"} "umri" ktk k // kweli "urefu" ktk k // sikweli hobby = {"a": "asili", "b": "baiskeli", "c": "chakula"} kwa i, v ktk hobby { andika(i, "=>", v) } /* a => asili b => baiskeli c => chakula */ kwa v ktk hobby { andika(v) } /* asili baiskeli chakula */ jina = "lugano" kwa i ktk jina { andika(i) } kamusi = {"a": "andaa", "b": "baba"} kwa v ktk kamusi { andika(v) } kwa k, v ktk kamusi { andika(k + " ni " + v) } kwa v ktk "mojo" { andika(v) } kwa i, v ktk "mojo" { andika(i, "->", v) } majina = ["juma", "asha", "haruna"] kwa v ktk majina { andika(v) } kwa i, v ktk majina { andika(i, "-", v) } kwa i, v ktk "mojo" { kama (i == 2) { andika("nimevunja") vunja } andika(v) } kwa i, v ktk "mojo" { kama (i == 2) { andika("nimeruka") endelea } andika(v) } jum = unda(x, y) { rudisha x + y } jum(2, 3) // 5 salamu = unda() { andika("Habari yako") } salamu() salamu = unda(jina) { andika("Habari yako", jina) } salamu("asha") // Habari yako asha salimu = unda(salamu="Habari") { andika(salamu) } salimu() // Habari salimu("Mambo") // Mambo mfano = unda(x) { rudisha "nimerudi" andika(x) } mfano("x") // nimerudi fib = unda(n) { kama (n <= 1) { rudisha n } sivyo { rudisha fib(n-1) + fib(n-2) } } andika(fib(10)) // 55 jum = unda(x) { rudisha unda(y) { rudisha x + y } } jum_x = jum(5) andika(jum_x(3)) // 8 2 + 3 * 5 // 17 a = 2.5 b = 3/5 a + b // 2.8 i = 2.4 i++ // 3.4 i = 2 i *= 3 // 6 i /= 2 // 3 i += 100 // 103 i -= 10 // 93 i %= 90 // 3 i = -10 wakati (i < 0) { andika(i) i++ } andika("mambo") // mambo a = 'niaje' andika("mambo", a) // mambo niaje a = "habari" + " " + "yako" andika(a) // habari yako b = "habari" b += " yako" // habari yako andika("mambo " * 4) // mambo mambo mambo mambo a = "habari" a *= 4 // habarihabarihabarihabari jina = "avicenna" kwa i ktk jina {andika(i)} a = "nuru" andika(a == "nuru") // kweli andika(a == "mambo") // sikweli a = "mambo" a.idadi() // 5 a = "NURU" a.herufindogo() // nuru a = "nuru mambo habari" b = a.gawa() andika(b) // ["nuru", "mambo", "habari"] a = "nuru,mambo,habari" b = a.gawa(",") andika(b) // ["nuru", "mambo", "habari"] a = 2 badili (a){ ikiwa 3 { andika("a ni tatu") } ikiwa 2 { andika ("a ni mbili") } } badili (a) { ikiwa 1,2,3 { andika("a ni kati ya 1, 2 au 3") } ikiwa 4 { andika("a ni 4") } } z = 20 badili(z) { ikiwa 10 { andika("kumi") } ikiwa 30 { andika("thelathini") } kawaida { andika("ishirini") } } i = 1 wakati (i <= 5) { andika(i) i++ } i = 1 wakati (i < 5) { kama (i == 3) { andika("nimevunja") vunja } andika(i) i++ } i = 0 wakati (i < 5) { i++ kama (i == 3) { andika("nimeruka") endelea } andika(i) } // using time: tumia muda andika(muda.hasahivi()) andika("Tunalala") muda.lala(2) andika("Tumeamka") s = muda.hasahivi() muda.lala(2) andika(muda.tangu(s)) andika(s.ongeza(sekunde=3, miaka=10, dakika=4)) // using file: fail = fungua("sarufi.nr") andika(fail.soma()) // using net and json: tumia mtandao, jsoni url = "https://v2.jokeapi.dev/joke/Any?type=single" resp = mtandao.peruzi(url) resp = jsoni.dikodi(resp) andika(resp["joke"]) // os module tumia os andika(os.kimbiza("pwd")) os.toka(0) ================================================ FILE: examples/perceptron.nr ================================================ tumia hisabati // Mbinu za Kupanga //orodhesha(kwanza, mwisho, umbali), huunda orodha ya nambari na umbali uliowekwa kati yao. fanya orodhesha = unda(kwanza, mwisho, umbali){ fanya orodha = [kwanza]; fanya i = kwanza + umbali; wakati(i < mwisho){ orodha.sukuma(i); i += umbali; } rudisha orodha; } // Kuanzisha uzani bila mpangilio fanya mizani = [ hisabati.random() * 2 - 1, hisabati.random() * 2 - 1, hisabati.random() * 2 - 1 ]; // Undo la uanzishaji wa Sigmoid fanya sigmoid = unda(vekta) { fanya tokeo = []; kwa v ktk vekta { tokeo.sukuma(1 / (1 + hisabati.exp(-1 * v))); } rudisha tokeo; } // Derivative ya undo la sigmoid fanya sigmoidDerivative = unda(vekta) { andika("vekta: ",vekta) fanya tokeo = []; kwa v ktk vekta { tokeo.sukuma(v * (1 - v)); } rudisha tokeo; } fanya kuzidishaMatrikiVekta = unda(matriki, vekta) { fanya tokeo = []; kwa row ktk matriki { fanya jamii = 0; kwa j, kipengee ktk row { jamii += kipengee * vekta[j]; } tokeo.sukuma(jamii); } rudisha tokeo; } fanya zidishaKwaNukta = unda(safu1, safu2) { // Angalia ikiwa safu zina urefu sawa kama (safu1.idadi() != safu2.idadi()) { andika("Safu lazima ziwe na urefu sawa kwa kuzidisha kwa busara ya kipengele."); } // Perform element-wise multiplication fanya tokeo = []; kwa i, kipengee ktk safu1 { tokeo.sukuma(kipengee * safu2[i]); } rudisha tokeo; } // Songa mbele kupitia mtandao wa neva fanya waza = unda(pembejeo, mizani) { fanya jumlaYaUzani = sigmoid(kuzidishaMatrikiVekta(pembejeo, mizani)); rudisha jumlaYaUzani; } fanya badiliMatriki = unda(matrix) { // Pata idadi ya safu mlalo na safu wima katika matrix asili fanya nambari_ya_safu_mlalo = matrix.idadi(); fanya nambari_ya_safu_wima = matrix[0].idadi(); // Unda matrix mpya na safu mlalo na safu wima zilizobadilishwa fanya matrikiIliyobadili = []; // Pita ndani ya safu wima kwa safu_wima_ya, safuW ktk orodhesha(0, nambari_ya_safu_wima, 1){ // Anzisha safu mlalo mpya kwa matriki iliyopitishwa fanya transposed_safu_mlalo = []; // Pita ndani ya safu mlalo kwa safu_mlalo_ya, safu ktk orodhesha(0, nambari_ya_safu_mlalo, 1){ // Sukuma kipengele kwenye safu wima ya sasa na safu mlalo hadi safu iliyopitishwa transposed_safu_mlalo.sukuma(matrix[safu_mlalo_ya][safu_wima_ya]); } // Sukuma safu mlalo iliyopitishwa kwenye matriki lililopitishwa matrikiIliyobadili.sukuma(transposed_safu_mlalo); } rudisha matrikiIliyobadili; } // Funza mtandao wa neva fanya funza = unda(mizani, mafunzoPembejeo, matokeoYaMafunzo, marudioYaMafunzo) { fanya kurudia = 0 andika('\nmafunzoPembejeo: '); andika(mafunzoPembejeo); andika('\nmatokeoYaMafunzo: '); andika(matokeoYaMafunzo); fanya orodha = orodhesha(0, marudioYaMafunzo, 1) // andika("orodha: ",orodha) kwa i ktk orodha{ andika('\n\nkurudia: '); andika(i); // Pitisha mafunzo yaliyowekwa kupitia mtandao wa neva fanya pato = waza(mafunzoPembejeo, mizani); andika('\npato: '); andika(pato); // Kuhesabu kiwango cha upungufu fanya upungufu = []; kwa i, kipengee ktk matokeoYaMafunzo { upungufu.sukuma(kipengee - pato[i]); } andika('\nupungufu: '); andika(upungufu); fanya sigmoidDerivative_ya_pato = sigmoidDerivative(pato) andika('\nsigmoidDerivative tokeo: '); andika(sigmoidDerivative_ya_pato); fanya zidishaKwaNukta_tokeo = zidishaKwaNukta(upungufu, sigmoidDerivative_ya_pato); andika('\nzidishaKwaNukta tokeo: '); andika(zidishaKwaNukta_tokeo); fanya mafunzoPembejeoYaliyobadili = badiliMatriki(mafunzoPembejeo) andika('\nmafunzo pembejeo yaliyobadili: '); andika(mafunzoPembejeoYaliyobadili); // Kuzidisha upungufu kwa pembejeo na upinde rangi ya kitendakazi cha sigmoid // Uzito mdogo wa ujasiri hurekebishwa zaidi kupitia asili ya kazi fanya marekebisho = kuzidishaMatrikiVekta(mafunzoPembejeo, zidishaKwaNukta_tokeo); andika('\nmarekebisho tokeo: '); andika(marekebisho); // Rekebisha uzani kwa i, j ktk mizani { mizani[i] = mizani[i] + marekebisho[i]; } andika('\nmizani mpya: '); andika(mizani); kurudia++ } rudisha mizani; } andika('\nMizani ya Kuanzisha isiyo na mpangilio: '); andika(mizani); // Seti ya mafunzo fanya mafunzoPembejeo = [[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]]; fanya matokeoYaMafunzo = [0, 1, 1, 0]; // Funza mtandao wa neva fanya mafunzoMizani = funza(mizani, mafunzoPembejeo, matokeoYaMafunzo, 10000); andika('\nMizani baada ya mafunzo:'); andika(mafunzoMizani); // Ingizo la mtumiaji kwa hali mpya fanya A = 0; fanya B = 0; fanya C = 1; andika('\nHali mpya: data ya pembejeo = ', A, B, C); andika('\nData ya pato:'); andika(waza([[A, B, C]], mafunzoMizani)); ================================================ FILE: examples/reduce.nr ================================================ fanya reduce = unda(iterator, callback, initialValue) { fanya accumulator = initialValue; kwa thamani ktk iterator { accumulator = callback(accumulator, thamani); } rudisha accumulator; } fanya list = [1,2,3,4,5]; fanya employees = [{"salary":120},{"salary":135},{"salary":140}] fanya sum = unda(acc,value){ rudisha acc + value; } fanya mul = unda(acc,value){ rudisha acc * value; } fanya sumSalo = unda(acc,value){ rudisha acc + value["salary"]; } fanya sumSaloWithTax = unda(acc,value){ rudisha acc + (value["salary"] * (1-0.34)); } andika(reduce(list,sum,0)) andika(reduce(list,mul,1)) andika(reduce(employees,sumSalo,0)) andika(reduce(employees,sumSaloWithTax,0)) ================================================ FILE: examples/sarufi.nr ================================================ tumia mtandao tumia jsoni pakeji sarufi { andaa = unda(file) { config = fungua(file) configString = config.soma() configDict = jsoni.dikodi(configString) clientID = configDict["client_id"] clientSecret = configDict["client_secret"] params = {"client_id": clientID, "client_secret": clientSecret} tokenString = mtandao.tuma(yuareli="https://api.sarufi.io/api/access_token", mwili=params) tokenDict = jsoni.dikodi(tokenString) @.token = tokenDict["access_token"] @.Auth = "Bearer " + @.token } tokenYangu = unda() { rudisha @.token } tengenezaChatbot = unda(data) { majibu = mtandao.tuma(yuareli="https://api.sarufi.io/chatbot", vichwa={"Authorization": @.Auth}, mwili = data) rudisha majibu } pataChatbotZote = unda() { majibu = mtandao.peruzi(yuareli="https://api.sarufi.io/chatbots", vichwa={"Authorization": @.Auth}) rudisha majibu } } ================================================ FILE: examples/sorting_algorithm.nr ================================================ /* ############ Sorting Algorithm ############## By @VictorKariuki https://github.com/VictorKariuki ############################################# */ slice = unda(arr,start, end) { result = [] wakati (start < end) { result = result + [arr[start]] start = start + 1 } rudisha result } merge = unda(left, right) { result = [] lLen = left.idadi() rLen = right.idadi() l = 0 r = 0 wakati (l < lLen && r < rLen) { kama (left[l] < right[r]) { result = result + [left[l]] l = l + 1 } sivyo { result = result + [right[r]] r = r + 1 } } andika(result) } mergeSort = unda(arr){ len = arr.idadi() andika("arr is ", arr," of length ", len) kama (len < 2) { rudisha arr } andika("len is greater than or == to 2", len > 1) mid = (len / 2) andika("arr has a mid point of ", mid) left = slice(arr, 0, mid) right = slice(arr, mid, len) andika("left slice is ", left) andika("right slice is ", right) sortedLeft = mergeSort(left) sortedRight = mergeSort(right) andika("sortedLeft is ", sortedLeft) andika("sortedRight is ", sortedRight) rudisha merge(sortedLeft, sortedRight) } arr = [6, 5, 3, 1, 8, 7, 2, 4] sortedArray = mergeSort(arr) andika(sortedArray) ================================================ FILE: examples/sudoku_solver.nr ================================================ /*########### Backtracking Algorithm ############## By @VictorKariuki https://github.com/VictorKariuki NURU program to solve Sudoku using Backtracking Algorithm The sudoku puzzle is represented as a 2D array. The empty cells are represented by 0. The algorithm works by trying out all possible numbers for an empty cell. If the number is valid, it is placed in the cell. If the number is invalid, the algorithm backtracks to the previous cell and tries another number. The algorithm terminates when all cells are filled. The algorithm is implemented in the solveSudoku function. The isValid function checks kama a number is valid in a given cell. The printSudoku function prints the sudoku puzzle. The solveSudoku function solves the sudoku puzzle. The main function initializes the sudoku puzzle and calls the solveSudoku function. #################################################*/ fanya printing = unda(sudoku) { fanya row = 0 wakati (row < 9){ andika(sudoku[row]) row++ } } fanya sudoku = [[3, 0, 6, 5, 0, 8, 4, 0, 0],[5, 2, 0, 0, 0, 0, 0, 0, 0],[0, 8, 7, 0, 0, 0, 0, 3, 1],[0, 0, 3, 0, 1, 0, 0, 8, 0],[9, 0, 0, 8, 6, 3, 0, 0, 5],[0, 5, 0, 0, 9, 0, 6, 0, 0],[1, 3, 0, 0, 0, 0, 2, 5, 0],[0, 0, 0, 0, 0, 0, 0, 7, 4],[0, 0, 5, 2, 0, 6, 3, 0, 0]] fanya isSafe = unda(grid, row, col, num) { kwa x ktk [0,1,2,3,4,5,6,7,8] { kama (grid[row][x] == num) { rudisha sikweli } } kwa x ktk [0,1,2,3,4,5,6,7,8] { kama (grid[x][col] == num) { rudisha sikweli } } fanya startRow = row - row % 3 fanya startCol = col - col % 3 kwa i ktk [0, 1, 2] { kwa j ktk [0, 1, 2] { kama (grid[i + startRow][j + startCol] == num) { rudisha sikweli } } } rudisha kweli } fanya solveSudoku = unda(grid, row, col) { kama (row == 8 && col == 9) { rudisha kweli } kama (col == 9) { row += 1 col = 0 } kama (grid[row][col] > 0) { rudisha solveSudoku(grid, row, col + 1) } kwa num ktk [1,2,3,4,5,6,7,8,9] { kama (isSafe(grid, row, col, num)) { grid[row][col] = num kama (solveSudoku(grid, row, col + 1)) { rudisha kweli } } grid[row][col] = 0 } rudisha sikweli } andika() andika("----- PUZZLE TO SOLVE -----") printing(sudoku) kama (solveSudoku(sudoku, 0, 0)){ andika() andika("--------- SOLUTION --------") printing(sudoku) andika() } sivyo { andika("imeshindikana") } ================================================ FILE: extensions/README.md ================================================ # Nuru Extensions For Various Editors ## [VSCODE](./vscode/) Nuru syntax highlighting on VSCode ## [VIM](./vim) The file contained herein has a basic syntax highlight for vim. The file should be saved in `$HOME/.vim/syntax/nuru.vim`. You should add the following line to your `.vimrc` or the appropriate location: ```vim au BufRead,BufNewFile *.nr set filetype=nuru ``` Only basic syntax highlighting is provided by the script. ================================================ FILE: extensions/vim/syntax/nuru.vim ================================================ " Sintaksia ya nuru kwenye programu ya "vim" " Lugha: Nuru " Maneno tengwa syntax keyword nuruKeyword unda pakeji rudisha vunja endelea tupu syntax keyword nuruType fanya syntax keyword nuruBool kweli sikweli syntax keyword nuruConditional kama sivyo au syntax match nuruComparision /[!\|<>]/ syntax keyword nuruLoop ktk while badili syntax keyword nuruLabel ikiwa kawaida " Nambari syntax match nuruInt '[+-]\d\+' contained display syntax match nuruFloat '[+-]\d+\.\d*' contained display " Viendeshaji syntax match nuruAssignment '=' syntax match nuruLogicalOP /[\&!|]/ " Vitendakazi syntax keyword nuruFunction andika aina jaza fungua " Tungo syntax region nuruString start=/"/ skip=/\\"/ end=/"/ syntax region nuruString start=/'/ skip=/\\'/ end=/'/ " Maoni syntax match nuruComment "//.*" syntax region nuruComment start="/\*" end="\*/" " Fafanua sintaksia let b:current_syntax = "nuru" highlight def link nuruComment Comment highlight def link nuruBool Boolean highlight def link nuruFunction Function highlight def link nuruComparision Conditional highlight def link nuruConditional Conditional highlight def link nuruKeyword Keyword highlight def link nuruString String highlight def link nuruVariable Identifier highlight def link nuruLoop Repeat highlight def link nuruInt Number highlight def link nuruFloat Float highlight def link nuruAssignment Operator highlight def link nuruLogicalOP Operator highlight def link nuruAriOP Operator highlight def link nuruType Type highlight def link nuruLabel Label ================================================ FILE: extensions/vscode/CHANGELOG.md ================================================ # Change Log All notable changes to the "nuru" extension will be documented in this file. Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. ## [Unreleased] - Initial release ================================================ FILE: extensions/vscode/README.md ================================================ # Nuru VSCode Extension This is a syntax highliting extension for Nuru on vscode. It detects `.nr` and `.sw` files. ## Screenshots

Nuru Programming Language

## How To Install ### Download From Market Place - Simply download the Nuru Extension from VSCode Market Place ### Windows - Copy the whole [nuru folder](https://github.com/NuruProgramming/Nuru/tree/main/extensions/vscode/nuru) and paste it in the VSCode extensions directory found in `%USERPROFILE%\.vscode\extensions` - Restart VSCode ### Linux and MacOS - Copy the whole [nuru folder](https://github.com/NuruProgramming/Nuru/tree/main/extensions/vscode/nuru) and paste it in the VSCode extensions directory found in `~/.vscode/extensions` - Restart VSCode ================================================ FILE: go.mod ================================================ module github.com/NuruProgramming/Nuru go 1.18 require ( github.com/AvicennaJr/GoPrompt v0.0.0-20230411215003-be2316d88e2d github.com/charmbracelet/bubbles v0.15.0 github.com/charmbracelet/bubbletea v0.23.2 github.com/charmbracelet/glamour v0.6.0 github.com/charmbracelet/lipgloss v0.7.1 github.com/lrstanley/bubblezone v0.0.0-20230303230241-08f906ff62a9 ) require ( github.com/alecthomas/chroma v0.10.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.7 // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-tty v0.0.3 // indirect github.com/microcosm-cc/bluemonday v1.0.21 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pkg/term v1.2.0-beta.2 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect github.com/yuin/goldmark v1.5.2 // indirect github.com/yuin/goldmark-emoji v1.0.1 // indirect golang.org/x/net v0.0.0-20221002022538-bcab6841153b // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.7.0 // indirect golang.org/x/term v0.7.0 // indirect golang.org/x/text v0.9.0 // indirect ) ================================================ FILE: go.sum ================================================ github.com/AvicennaJr/GoPrompt v0.0.0-20230411215003-be2316d88e2d h1:H+Y1MQQXd83x0xC2MPOw+gFFozKTXgcW69bV80+/wpY= github.com/AvicennaJr/GoPrompt v0.0.0-20230411215003-be2316d88e2d/go.mod h1:oo6I+tik505nlWIPCU2ogWMkOxoTt7A1YwQYlmlHqE8= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI= github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74= github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= github.com/charmbracelet/bubbletea v0.23.2 h1:vuUJ9HJ7b/COy4I30e8xDVQ+VRDUEFykIjryPfgsdps= github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lrstanley/bubblezone v0.0.0-20230303230241-08f906ff62a9 h1:w7W7F0EBRNTRRy+MFNLGrhJLn2Ldzl8Ms2AtHtgFtuw= github.com/lrstanley/bubblezone v0.0.0-20230303230241-08f906ff62a9/go.mod h1:v5lEwWaguF1o2MW/ucO0ZIA/IZymdBYJJ+2cMRLE7LU= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8= github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: lexer/lexer.go ================================================ // This will convert the sequence of characters into a sequence of tokens package lexer import ( "github.com/NuruProgramming/Nuru/token" ) type Lexer struct { input []rune position int readPosition int ch rune line int } func New(input string) *Lexer { l := &Lexer{input: []rune(input), line: 1} l.readChar() return l } func (l *Lexer) readChar() { if l.readPosition >= len(l.input) { l.ch = 0 } else { l.ch = l.input[l.readPosition] } l.position = l.readPosition l.readPosition += 1 } func (l *Lexer) NextToken() token.Token { var tok token.Token l.skipWhitespace() if l.ch == rune('/') && l.peekChar() == rune('/') { l.skipSingleLineComment() return l.NextToken() } if l.ch == rune('/') && l.peekChar() == rune('*') { l.skipMultiLineComment() return l.NextToken() } switch l.ch { case rune('='): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.EQ, Literal: string(ch) + string(l.ch), Line: l.line} } else { tok = newToken(token.ASSIGN, l.line, l.ch) } case rune(';'): tok = newToken(token.SEMICOLON, l.line, l.ch) case rune('('): tok = newToken(token.LPAREN, l.line, l.ch) case rune(')'): tok = newToken(token.RPAREN, l.line, l.ch) case rune('{'): tok = newToken(token.LBRACE, l.line, l.ch) case rune('}'): tok = newToken(token.RBRACE, l.line, l.ch) case rune(','): tok = newToken(token.COMMA, l.line, l.ch) case rune('+'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.PLUS_ASSIGN, Line: l.line, Literal: string(ch) + string(l.ch)} } else if l.peekChar() == rune('+') { ch := l.ch l.readChar() tok = token.Token{Type: token.PLUS_PLUS, Literal: string(ch) + string(l.ch), Line: l.line} } else { tok = newToken(token.PLUS, l.line, l.ch) } case rune('-'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.MINUS_ASSIGN, Line: l.line, Literal: string(ch) + string(l.ch)} } else if l.peekChar() == rune('-') { ch := l.ch l.readChar() tok = token.Token{Type: token.MINUS_MINUS, Literal: string(ch) + string(l.ch), Line: l.line} } else { tok = newToken(token.MINUS, l.line, l.ch) } case rune('!'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.NOT_EQ, Literal: string(ch) + string(l.ch), Line: l.line} } else { tok = newToken(token.BANG, l.line, l.ch) } case rune('/'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.SLASH_ASSIGN, Line: l.line, Literal: string(ch) + string(l.ch)} } else { tok = newToken(token.SLASH, l.line, l.ch) } case rune('*'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.ASTERISK_ASSIGN, Line: l.line, Literal: string(ch) + string(l.ch)} } else if l.peekChar() == rune('*') { ch := l.ch l.readChar() tok = token.Token{Type: token.POW, Literal: string(ch) + string(l.ch), Line: l.line} } else { tok = newToken(token.ASTERISK, l.line, l.ch) } case rune('<'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.LTE, Literal: string(ch) + string(l.ch), Line: l.line} } else { tok = newToken(token.LT, l.line, l.ch) } case rune('>'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.GTE, Literal: string(ch) + string(l.ch), Line: l.line} } else { tok = newToken(token.GT, l.line, l.ch) } case rune('"'): tok.Type = token.STRING tok.Literal = l.readString() tok.Line = l.line case rune('\''): tok = token.Token{Type: token.STRING, Literal: l.readSingleQuoteString(), Line: l.line} case rune('['): tok = newToken(token.LBRACKET, l.line, l.ch) case rune(']'): tok = newToken(token.RBRACKET, l.line, l.ch) case rune(':'): tok = newToken(token.COLON, l.line, l.ch) case rune('@'): tok = newToken(token.AT, l.line, l.ch) case rune('.'): tok = newToken(token.DOT, l.line, l.ch) case rune('&'): if l.peekChar() == rune('&') { ch := l.ch l.readChar() tok = token.Token{Type: token.AND, Literal: string(ch) + string(l.ch), Line: l.line} } case rune('|'): if l.peekChar() == rune('|') { ch := l.ch l.readChar() tok = token.Token{Type: token.OR, Literal: string(ch) + string(l.ch), Line: l.line} } case rune('%'): if l.peekChar() == rune('=') { ch := l.ch l.readChar() tok = token.Token{Type: token.MODULUS_ASSIGN, Line: l.line, Literal: string(ch) + string(l.ch)} } else { tok = newToken(token.MODULUS, l.line, l.ch) } case rune('#'): if l.peekChar() == rune('!') && l.line == 1 { l.skipSingleLineComment() return l.NextToken() } case 0: tok.Literal = "" tok.Type = token.EOF tok.Line = l.line default: if isLetter(l.ch) { tok.Literal = l.readIdentifier() tok.Type = token.LookupIdent(tok.Literal) tok.Line = l.line return tok } else if isDigit(l.ch) && isLetter(l.peekChar()) { tok.Literal = l.readIdentifier() tok.Type = token.LookupIdent(tok.Literal) tok.Line = l.line return tok } else if isDigit(l.ch) { tok = l.readDecimal() return tok } else { tok = newToken(token.ILLEGAL, l.line, l.ch) } } l.readChar() return tok } func newToken(tokenType token.TokenType, line int, ch rune) token.Token { return token.Token{Type: tokenType, Literal: string(ch), Line: line} } func (l *Lexer) readIdentifier() string { position := l.position for isLetter(l.ch) || isDigit(l.ch) { l.readChar() } return string(l.input[position:l.position]) } func isLetter(ch rune) bool { return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch == '@' } func (l *Lexer) skipWhitespace() { for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' { if l.ch == '\n' { l.line++ } l.readChar() } } func isDigit(ch rune) bool { return '0' <= ch && ch <= '9' } func (l *Lexer) readNumber() string { position := l.position for isDigit(l.ch) { l.readChar() } return string(l.input[position:l.position]) } func (l *Lexer) readDecimal() token.Token { integer := l.readNumber() if l.ch == '.' && isDigit(l.peekChar()) { l.readChar() fraction := l.readNumber() return token.Token{Type: token.FLOAT, Literal: integer + "." + fraction, Line: l.line} } return token.Token{Type: token.INT, Literal: integer, Line: l.line} } func (l *Lexer) peekChar() rune { if l.readPosition >= len(l.input) { return rune(0) } else { return l.input[l.readPosition] } } // func (l *Lexer) peekTwoChar() rune { // if l.readPosition+1 >= len(l.input) { // return rune(0) // } else { // return l.input[l.readPosition+1] // } // } func (l *Lexer) skipSingleLineComment() { for l.ch != '\n' && l.ch != 0 { l.readChar() } l.skipWhitespace() } func (l *Lexer) skipMultiLineComment() { endFound := false for !endFound { if l.ch == 0 { endFound = true } if l.ch == '*' && l.peekChar() == '/' { endFound = true l.readChar() } l.readChar() l.skipWhitespace() } } func (l *Lexer) readString() string { var str string for { l.readChar() if l.ch == '"' || l.ch == 0 { break } else if l.ch == '\\' { switch l.peekChar() { case 'n': l.readChar() l.ch = '\n' case 'r': l.readChar() l.ch = '\r' case 't': l.readChar() l.ch = '\t' case '"': l.readChar() l.ch = '"' case '\\': l.readChar() l.ch = '\\' } } str += string(l.ch) } return str } func (l *Lexer) readSingleQuoteString() string { var str string for { l.readChar() if l.ch == '\'' || l.ch == 0 { break } else if l.ch == '\\' { switch l.peekChar() { case 'n': l.readChar() l.ch = '\n' case 'r': l.readChar() l.ch = '\r' case 't': l.readChar() l.ch = '\t' case '"': l.readChar() l.ch = '"' case '\\': l.readChar() l.ch = '\\' } } str += string(l.ch) } return str } ================================================ FILE: lexer/lexer_test.go ================================================ package lexer import ( "testing" "github.com/NuruProgramming/Nuru/token" ) func TestNextToken(t *testing.T) { input := ` // Testing kama lex luther iko sawa fanya tano = 5; fanya kumi = 10; fanya jumla = unda(x, y){ x + y; }; fanya jibu = jumla(tano, kumi); !-/5; 5 < 10 > 5; kama (5 < 10) { rudisha kweli; } sivyo { rudisha sikweli; } 10 == 10; 10 != 9; // Hii ni comment // Comment nyingine /* multiline comment */ /* multiline comment number twooooooooooo */ 5 "bangi" "ba ngi" [1, 2]; {"mambo": "vipi"} . // test dot tumia muda badili (a) { ikiwa 2 { andika(2) } kawaida { andika(0) } } tupu kwa i, v ktk j` tests := []struct { expectedType token.TokenType expectedLiteral string }{ {token.LET, "fanya"}, {token.IDENT, "tano"}, {token.ASSIGN, "="}, {token.INT, "5"}, {token.SEMICOLON, ";"}, {token.LET, "fanya"}, {token.IDENT, "kumi"}, {token.ASSIGN, "="}, {token.INT, "10"}, {token.SEMICOLON, ";"}, {token.LET, "fanya"}, {token.IDENT, "jumla"}, {token.ASSIGN, "="}, {token.FUNCTION, "unda"}, {token.LPAREN, "("}, {token.IDENT, "x"}, {token.COMMA, ","}, {token.IDENT, "y"}, {token.RPAREN, ")"}, {token.LBRACE, "{"}, {token.IDENT, "x"}, {token.PLUS, "+"}, {token.IDENT, "y"}, {token.SEMICOLON, ";"}, {token.RBRACE, "}"}, {token.SEMICOLON, ";"}, {token.LET, "fanya"}, {token.IDENT, "jibu"}, {token.ASSIGN, "="}, {token.IDENT, "jumla"}, {token.LPAREN, "("}, {token.IDENT, "tano"}, {token.COMMA, ","}, {token.IDENT, "kumi"}, {token.RPAREN, ")"}, {token.SEMICOLON, ";"}, {token.BANG, "!"}, {token.MINUS, "-"}, {token.SLASH, "/"}, {token.INT, "5"}, {token.SEMICOLON, ";"}, {token.INT, "5"}, {token.LT, "<"}, {token.INT, "10"}, {token.GT, ">"}, {token.INT, "5"}, {token.SEMICOLON, ";"}, {token.IF, "kama"}, {token.LPAREN, "("}, {token.INT, "5"}, {token.LT, "<"}, {token.INT, "10"}, {token.RPAREN, ")"}, {token.LBRACE, "{"}, {token.RETURN, "rudisha"}, {token.TRUE, "kweli"}, {token.SEMICOLON, ";"}, {token.RBRACE, "}"}, {token.ELSE, "sivyo"}, {token.LBRACE, "{"}, {token.RETURN, "rudisha"}, {token.FALSE, "sikweli"}, {token.SEMICOLON, ";"}, {token.RBRACE, "}"}, {token.INT, "10"}, {token.EQ, "=="}, {token.INT, "10"}, {token.SEMICOLON, ";"}, {token.INT, "10"}, {token.NOT_EQ, "!="}, {token.INT, "9"}, {token.SEMICOLON, ";"}, {token.INT, "5"}, {token.STRING, "bangi"}, {token.STRING, "ba ngi"}, {token.LBRACKET, "["}, {token.INT, "1"}, {token.COMMA, ","}, {token.INT, "2"}, {token.RBRACKET, "]"}, {token.SEMICOLON, ";"}, {token.LBRACE, "{"}, {token.STRING, "mambo"}, {token.COLON, ":"}, {token.STRING, "vipi"}, {token.RBRACE, "}"}, {token.DOT, "."}, {token.IMPORT, "tumia"}, {token.IDENT, "muda"}, {token.SWITCH, "badili"}, {token.LPAREN, "("}, {token.IDENT, "a"}, {token.RPAREN, ")"}, {token.LBRACE, "{"}, {token.CASE, "ikiwa"}, {token.INT, "2"}, {token.LBRACE, "{"}, {token.IDENT, "andika"}, {token.LPAREN, "("}, {token.INT, "2"}, {token.RPAREN, ")"}, {token.RBRACE, "}"}, {token.DEFAULT, "kawaida"}, {token.LBRACE, "{"}, {token.IDENT, "andika"}, {token.LPAREN, "("}, {token.INT, "0"}, {token.RPAREN, ")"}, {token.RBRACE, "}"}, {token.RBRACE, "}"}, {token.NULL, "tupu"}, {token.FOR, "kwa"}, {token.IDENT, "i"}, {token.COMMA, ","}, {token.IDENT, "v"}, {token.IN, "ktk"}, {token.IDENT, "j"}, {token.EOF, ""}, } l := New(input) for i, tt := range tests { tok := l.NextToken() if tok.Type != tt.expectedType { t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q", i, tt.expectedType, tok.Type) } if tok.Literal != tt.expectedLiteral { t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q", i, tt.expectedLiteral, tok.Literal) } } } ================================================ FILE: main.go ================================================ //go:build !wasm || !js package main import ( "fmt" "os" "strings" "github.com/NuruProgramming/Nuru/repl" "github.com/NuruProgramming/Nuru/styles" "github.com/charmbracelet/lipgloss" ) var ( Title = styles.TitleStyle. Render(` █░░ █░█ █▀▀ █░█ ▄▀█   █▄█ ▄▀█   █▄░█ █░█ █▀█ █░█ █▄▄ █▄█ █▄█ █▀█ █▀█   ░█░ █▀█   █░▀█ █▄█ █▀▄ █▄█`) Version = styles.VersionStyle.Render("v0.5.18") Author = styles.AuthorStyle.Render("by Nuru Org") NewLogo = lipgloss.JoinVertical(lipgloss.Center, Title, lipgloss.JoinHorizontal(lipgloss.Center, Author, " | ", Version)) Help = styles.HelpStyle.Italic(false).Render(fmt.Sprintf(`💡 Namna ya kutumia Nuru: %s: Kuanza programu ya Nuru %s: Kuendesha faili la Nuru %s: Kusoma nyaraka za Nuru %s: Kufahamu toleo la Nuru `, styles.HelpStyle.Bold(true).Render("nuru"), styles.HelpStyle.Bold(true).Render("nuru jinaLaFile.nr"), styles.HelpStyle.Bold(true).Render("nuru --nyaraka"), styles.HelpStyle.Bold(true).Render("nuru --toleo"))) ) func main() { args := os.Args if len(args) < 2 { help := styles.HelpStyle.Render("💡 Tumia exit() au toka() kuondoka") fmt.Println(lipgloss.JoinVertical(lipgloss.Left, NewLogo, "\n", help)) repl.Start() return } if len(args) == 2 { switch args[1] { case "msaada", "-msaada", "--msaada", "help", "-help", "--help", "-h": fmt.Println(Help) case "version", "-version", "--version", "-v", "v", "--toleo", "-toleo": fmt.Println(NewLogo) case "-docs", "--docs", "-nyaraka", "--nyaraka": repl.Docs() default: file := args[1] if strings.HasSuffix(file, "nr") || strings.HasSuffix(file, ".sw") { contents, err := os.ReadFile(file) if err != nil { fmt.Println(styles.ErrorStyle.Render("Error: Nuru imeshindwa kusoma faili: ", args[1])) os.Exit(1) } repl.Read(string(contents)) } else { fmt.Println(styles.ErrorStyle.Render("'"+file+"'", "sii faili sahihi. Tumia faili la '.nr' au '.sw'")) os.Exit(1) } } } else { fmt.Println(styles.ErrorStyle.Render("Error: Operesheni imeshindikana boss.")) fmt.Println(Help) os.Exit(1) } } ================================================ FILE: main_wasm.go ================================================ //go:build wasm && js package main import ( "fmt" "github.com/NuruProgramming/Nuru/evaluator" "github.com/NuruProgramming/Nuru/lexer" "github.com/NuruProgramming/Nuru/object" "github.com/NuruProgramming/Nuru/parser" "syscall/js" ) func Read(contents string) { jsOutputReceiverFunction := js.Global().Get("nuruOutputReceiver") env := object.NewEnvironment() l := lexer.New(contents) p := parser.New(l) program := p.ParseProgram() if len(p.Errors()) != 0 { fmt.Println("Kuna makosa yafuatayo:") jsOutputReceiverFunction.Invoke("Kuna makosa yafuatayo:", true) for _, msg := range p.Errors() { // fmt.Println("\t" + msg) jsOutputReceiverFunction.Invoke("\t" + msg, true) } } evaluated := evaluator.Eval(program, env) if evaluated != nil { if evaluated.Type() != object.NULL_OBJ { jsOutputReceiverFunction.Invoke(evaluated.Inspect(), true) } } } func runCode(this js.Value, args []js.Value) interface{} { code := args[0].String() Read(code) return nil } func main() { fmt.Println("Go WASM initialized") js.Global().Set("runCode", js.FuncOf(runCode)) <-make(chan bool) } ================================================ FILE: module/hisabati.go ================================================ package module import ( "math" "math/rand" "time" "github.com/NuruProgramming/Nuru/object" ) var MathFunctions = map[string]object.ModuleFunction{ "PI": pi, "e": e, "phi": phi, "ln10": ln10, "ln2": ln2, "log10e": log10e, "log2e": log2e, "log2": log2, "sqrt1_2": sqrt1_2, "sqrt2": sqrt2, "sqrt3": sqrt3, "sqrt5": sqrt5, "EPSILON": epsilon, "abs": abs, "sign": sign, "ceil": ceil, "floor": floor, "sqrt": sqrt, "cbrt": cbrt, "root": root, "hypot": hypot, "random": random, "factorial": factorial, "round": round, "max": max, "min": min, "exp": exp, "expm1": expm1, "log": log, "log10": log10, "log1p": log1p, "cos": cos, "sin": sin, "tan": tan, "acos": acos, "asin": asin, "atan": atan, "cosh": cosh, "sinh": sinh, "tanh": tanh, "acosh": acosh, "asinh": asinh, "atanh": atanh, "atan2": atan2, } var Constants = map[string]object.Object{ "PI": &object.Float{Value: math.Pi}, "e": &object.Float{Value: math.E}, "phi": &object.Float{Value: (1 + math.Sqrt(5)) / 2}, "ln10": &object.Float{Value: math.Log10E}, "ln2": &object.Float{Value: math.Ln2}, "log10e": &object.Float{Value: math.Log10E}, "log2e": &object.Float{Value: math.Log2E}, "sqrt1_2": &object.Float{Value: 1 / math.Sqrt2}, "sqrt2": &object.Float{Value: math.Sqrt2}, "sqrt3": &object.Float{Value: math.Sqrt(3)}, "sqrt5": &object.Float{Value: math.Sqrt(5)}, "EPSILON": &object.Float{Value: 2.220446049250313e-16}, } func pi(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Pi} } func e(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.E} } func phi(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: (1 + math.Sqrt(5)) / 2} } func ln10(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Log10E} } func ln2(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Ln2} } func log10e(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Log10E} } func log2e(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Log2E} } func sqrt1_2(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: 1 / math.Sqrt2} } func sqrt2(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Sqrt2} } func sqrt3(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Sqrt(3)} } func sqrt5(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: math.Sqrt(5)} } func epsilon(args []object.Object, defs map[string]object.Object) object.Object { return &object.Float{Value: 2.220446049250313e-16} } func abs(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe namba"} } switch arg := args[0].(type) { case *object.Integer: if arg.Value < 0 { return &object.Integer{Value: -arg.Value} } return arg case *object.Float: if arg.Value < 0 { return &object.Float{Value: -arg.Value} } return arg default: return &object.Error{Message: "Hoja lazima iwe namba"} } } func sign(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } switch arg := args[0].(type) { case *object.Integer: if arg.Value == 0 { return &object.Integer{Value: 0} } else if arg.Value > 0 { return &object.Integer{Value: 1} } else { return &object.Integer{Value: -1} } case *object.Float: if arg.Value == 0 { return &object.Integer{Value: 0} } else if arg.Value > 0 { return &object.Integer{Value: 1} } else { return &object.Integer{Value: -1} } default: return &object.Error{Message: "Hoja lazima iwe namba"} } } func ceil(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe namba"} } switch arg := args[0].(type) { case *object.Integer: return &object.Integer{Value: arg.Value} case *object.Float: return &object.Integer{Value: int64(math.Ceil(arg.Value))} default: return &object.Error{Message: "Hoja lazima iwe namba"} } } func floor(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe namba"} } switch arg := args[0].(type) { case *object.Integer: return &object.Integer{Value: arg.Value} case *object.Float: return &object.Integer{Value: int64(math.Floor(arg.Value))} default: return &object.Error{Message: "Hoja lazima iwe namba"} } } func sqrt(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe namba"} } switch arg := args[0].(type) { case *object.Integer: return &object.Float{Value: math.Sqrt(float64(arg.Value))} case *object.Float: return &object.Float{Value: math.Sqrt(arg.Value)} default: return &object.Error{Message: "Hoja lazima iwe namba"} } } func cbrt(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe namba"} } switch arg := args[0].(type) { case *object.Integer: return &object.Float{Value: math.Cbrt(float64(arg.Value))} case *object.Float: return &object.Float{Value: math.Cbrt(arg.Value)} default: return &object.Error{Message: "Hoja lazima iwe namba"} } } func root(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 2 { return &object.Error{Message: "Undo hili linahitaji hoja mbili tu"} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja ya kwanza lazima iwe namba"} } if args[1].Type() != object.INTEGER_OBJ { return &object.Error{Message: "Hoja ya pili lazima iwe namba"} } base, ok := args[0].(*object.Float) if !ok { base = &object.Float{Value: float64(args[0].(*object.Integer).Value)} } exp := args[1].(*object.Integer).Value if exp == 0 { return &object.Float{Value: 1.0} } else if exp < 0 { return &object.Error{Message: "Second Hoja lazima iwe a non-negative integer"} } x := 1.0 for i := 0; i < 10; i++ { x = x - (math.Pow(x, float64(exp))-base.Value)/(float64(exp)*math.Pow(x, float64(exp-1))) } return &object.Float{Value: x} } func hypot(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) < 2 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } var sumOfSquares float64 for _, arg := range args { if arg.Type() != object.INTEGER_OBJ && arg.Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima ziwe namba"} } switch num := arg.(type) { case *object.Integer: sumOfSquares += float64(num.Value) * float64(num.Value) case *object.Float: sumOfSquares += num.Value * num.Value } } return &object.Float{Value: math.Sqrt(sumOfSquares)} } func factorial(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.INTEGER_OBJ { return &object.Error{Message: "Hoja lazima iwe namba"} } n := args[0].(*object.Integer).Value if n < 0 { return &object.Error{Message: "Hoja lazima iwe a non-negative integer"} } result := int64(1) for i := int64(2); i <= n; i++ { result *= i } return &object.Integer{Value: result} } func round(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Integer{Value: int64(num + 0.5)} } func max(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } arg, ok := args[0].(*object.Array) if !ok { return &object.Error{Message: "Hoja lazima iwe an array"} } if len(arg.Elements) == 0 { return &object.Error{Message: "Orodha haipaswi kuwa tupu"} } var maxNum float64 for _, element := range arg.Elements { if element.Type() != object.INTEGER_OBJ && element.Type() != object.FLOAT_OBJ { return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} } switch num := element.(type) { case *object.Integer: if float64(num.Value) > maxNum { maxNum = float64(num.Value) } case *object.Float: if num.Value > maxNum { maxNum = num.Value } default: return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} } } return &object.Float{Value: maxNum} } func min(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } arg, ok := args[0].(*object.Array) if !ok { return &object.Error{Message: "Hoja lazima iwe an array"} } if len(arg.Elements) == 0 { return &object.Error{Message: "Orodha haipaswi kuwa tupu"} } minNum := math.MaxFloat64 for _, element := range arg.Elements { if element.Type() != object.INTEGER_OBJ && element.Type() != object.FLOAT_OBJ { return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} } switch num := element.(type) { case *object.Integer: if float64(num.Value) < minNum { minNum = float64(num.Value) } case *object.Float: if num.Value < minNum { minNum = num.Value } default: return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} } } return &object.Float{Value: minNum} } func exp(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Exp(num)} } func expm1(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Expm1(num)} } func log(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Log(num)} } func log10(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Log10(num)} } func log2(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe namba"} } arg := extractFloatValue(args[0]) if arg <= 0 { return &object.Error{Message: "Hoja lazima iwe kubwa kuliko 0"} } return &object.Float{Value: math.Log2(arg)} } func extractFloatValue(obj object.Object) float64 { switch obj := obj.(type) { case *object.Integer: return float64(obj.Value) case *object.Float: return obj.Value default: return 0 } } func log1p(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Log1p(num)} } func cos(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Cos(num)} } func sin(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Sin(num)} } func tan(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Tan(num)} } func acos(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Acos(num)} } func asin(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Asin(num)} } func atan(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Atan(num)} } func cosh(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Cosh(num)} } func sinh(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Sinh(num)} } func tanh(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Tanh(num)} } func acosh(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Acosh(num)} } func asinh(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Asinh(num)} } func atan2(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 2 { return &object.Error{Message: "Undo hili linahitaji hoja mbili tu."} } if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima ziwe namba"} } if args[1].Type() != object.INTEGER_OBJ && args[1].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima ziwe namba"} } y := extractFloatValue(args[0]) x := extractFloatValue(args[1]) return &object.Float{Value: math.Atan2(y, x)} } func atanh(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 1 { return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} } if args[0].Type() != object.FLOAT_OBJ { return &object.Error{Message: "Hoja lazima iwe desimali"} } num := args[0].(*object.Float).Value return &object.Float{Value: math.Atanh(num)} } func random(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} } if len(args) != 0 { return &object.Error{Message: "Undo hili halipaswi kupokea hoja."} } rand.Seed(time.Now().UnixNano()) value := rand.Float64() return &object.Float{Value: value} } ================================================ FILE: module/json.go ================================================ package module import ( "encoding/json" "github.com/NuruProgramming/Nuru/object" ) var JsonFunctions = map[string]object.ModuleFunction{} func init() { JsonFunctions["dikodi"] = decode JsonFunctions["enkodi"] = encode } func decode(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Hoja hii hairuhusiwi"} } if len(args) != 1 { return &object.Error{Message: "Tunahitaji hoja moja tu"} } if args[0].Type() != object.STRING_OBJ { return &object.Error{Message: "Hoja lazima iwe neno"} } var i interface{} input := args[0].(*object.String).Value err := json.Unmarshal([]byte(input), &i) if err != nil { return &object.Error{Message: "Hii data sio jsoni"} } return convertWhateverToObject(i) } func convertWhateverToObject(i interface{}) object.Object { switch v := i.(type) { case map[string]interface{}: dict := &object.Dict{} dict.Pairs = make(map[object.HashKey]object.DictPair) for k, v := range v { pair := object.DictPair{ Key: &object.String{Value: k}, Value: convertWhateverToObject(v), } dict.Pairs[pair.Key.(object.Hashable).HashKey()] = pair } return dict case []interface{}: list := &object.Array{} for _, e := range v { list.Elements = append(list.Elements, convertWhateverToObject(e)) } return list case string: return &object.String{Value: v} case int64: return &object.Integer{Value: v} case float64: return &object.Float{Value: v} case bool: if v { return &object.Boolean{Value: true} } else { return &object.Boolean{Value: false} } } return &object.Null{} } func encode(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Hoja hii hairuhusiwi"} } input := args[0] i := convertObjectToWhatever(input) data, err := json.Marshal(i) if err != nil { return &object.Error{Message: "Siwezi kubadilisha data hii kuwa jsoni"} } return &object.String{Value: string(data)} } func convertObjectToWhatever(obj object.Object) interface{} { switch v := obj.(type) { case *object.Dict: m := make(map[string]interface{}) for _, pair := range v.Pairs { key := pair.Key.(*object.String).Value m[key] = convertObjectToWhatever(pair.Value) } return m case *object.Array: list := make([]interface{}, len(v.Elements)) for i, e := range v.Elements { list[i] = convertObjectToWhatever(e) } return list case *object.String: return v.Value case *object.Integer: return v.Value case *object.Float: return v.Value case *object.Boolean: return v.Value case *object.Null: return nil } return nil } ================================================ FILE: module/module.go ================================================ package module import "github.com/NuruProgramming/Nuru/object" var Mapper = map[string]*object.Module{} func init() { Mapper["os"] = &object.Module{Name: "os", Functions: OsFunctions} Mapper["muda"] = &object.Module{Name: "time", Functions: TimeFunctions} Mapper["mtandao"] = &object.Module{Name: "net", Functions: NetFunctions} Mapper["jsoni"] = &object.Module{Name: "json", Functions: JsonFunctions} Mapper["hisabati"] = &object.Module{Name: "hisabati", Functions: MathFunctions} } ================================================ FILE: module/net.go ================================================ package module import ( "bytes" "encoding/json" "io/ioutil" "net/http" "github.com/NuruProgramming/Nuru/object" ) var NetFunctions = map[string]object.ModuleFunction{} func init() { NetFunctions["peruzi"] = getRequest NetFunctions["tuma"] = postRequest } func getRequest(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { var url *object.String var headers, params *object.Dict for k, v := range defs { switch k { case "yuareli": strUrl, ok := v.(*object.String) if !ok { return &object.Error{Message: "Yuareli iwe neno"} } url = strUrl case "vichwa": dictHead, ok := v.(*object.Dict) if !ok { return &object.Error{Message: "Vichwa lazima viwe kamusi"} } headers = dictHead case "mwili": dictHead, ok := v.(*object.Dict) if !ok { return &object.Error{Message: "Mwili lazima iwe kamusi"} } params = dictHead default: return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} } } if url.Value == "" { return &object.Error{Message: "Yuareli ni lazima"} } var responseBody *bytes.Buffer if params != nil { booty := convertObjectToWhatever(params) jsonBody, err := json.Marshal(booty) if err != nil { return &object.Error{Message: "Huku format query yako vizuri."} } responseBody = bytes.NewBuffer(jsonBody) } var req *http.Request var err error if responseBody != nil { req, err = http.NewRequest("GET", url.Value, responseBody) } else { req, err = http.NewRequest("GET", url.Value, nil) } if err != nil { return &object.Error{Message: "Tumeshindwa kufanya request"} } if headers != nil { for _, val := range headers.Pairs { req.Header.Set(val.Key.Inspect(), val.Value.Inspect()) } } client := &http.Client{} resp, err := client.Do(req) if err != nil { return &object.Error{Message: "Tumeshindwa kutuma request."} } defer resp.Body.Close() respBody, err := ioutil.ReadAll(resp.Body) if err != nil { return &object.Error{Message: "Tumeshindwa kusoma majibu."} } return &object.String{Value: string(respBody)} } if len(args) == 1 { url, ok := args[0].(*object.String) if !ok { return &object.Error{Message: "Yuareli lazima iwe neno"} } req, err := http.NewRequest("GET", url.Value, nil) if err != nil { return &object.Error{Message: "Tumeshindwa kufanya request"} } client := &http.Client{} resp, err := client.Do(req) if err != nil { return &object.Error{Message: "Tumeshindwa kutuma request."} } defer resp.Body.Close() respBody, err := ioutil.ReadAll(resp.Body) if err != nil { return &object.Error{Message: "Tumeshindwa kusoma majibu."} } return &object.String{Value: string(respBody)} } return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} } func postRequest(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { var url *object.String var headers, params *object.Dict for k, v := range defs { switch k { case "yuareli": strUrl, ok := v.(*object.String) if !ok { return &object.Error{Message: "Yuareli iwe neno"} } url = strUrl case "vichwa": dictHead, ok := v.(*object.Dict) if !ok { return &object.Error{Message: "Vichwa lazima viwe kamusi"} } headers = dictHead case "mwili": dictHead, ok := v.(*object.Dict) if !ok { return &object.Error{Message: "Mwili lazima iwe kamusi"} } params = dictHead default: return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} } } if url.Value == "" { return &object.Error{Message: "Yuareli ni lazima"} } var responseBody *bytes.Buffer if params != nil { booty := convertObjectToWhatever(params) jsonBody, err := json.Marshal(booty) if err != nil { return &object.Error{Message: "Huku format query yako vizuri."} } responseBody = bytes.NewBuffer(jsonBody) } var req *http.Request var err error if responseBody != nil { req, err = http.NewRequest("POST", url.Value, responseBody) } else { req, err = http.NewRequest("POST", url.Value, nil) } if err != nil { return &object.Error{Message: "Tumeshindwa kufanya request"} } if headers != nil { for _, val := range headers.Pairs { req.Header.Set(val.Key.Inspect(), val.Value.Inspect()) } } req.Header.Add("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { return &object.Error{Message: "Tumeshindwa kutuma request."} } defer resp.Body.Close() respBody, err := ioutil.ReadAll(resp.Body) if err != nil { return &object.Error{Message: "Tumeshindwa kusoma majibu."} } return &object.String{Value: string(respBody)} } return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} } ================================================ FILE: module/os.go ================================================ package module import ( "os" "os/exec" "strings" "github.com/NuruProgramming/Nuru/object" ) var OsFunctions = map[string]object.ModuleFunction{} func init() { OsFunctions["toka"] = exit OsFunctions["kimbiza"] = run } func exit(args []object.Object, defs map[string]object.Object) object.Object { if len(args) > 1 { return &object.Error{Message: "Hoja sii sahihi"} } if len(args) == 1 { status, ok := args[0].(*object.Integer) if !ok { return &object.Error{Message: "Hoja sii namba"} } os.Exit(int(status.Value)) return nil } os.Exit(0) return nil } func run(args []object.Object, defs map[string]object.Object) object.Object { if len(args) != 1 { return &object.Error{Message: "Idadi ya hoja sii sahihi"} } cmd, ok := args[0].(*object.String) if !ok { return &object.Error{Message: "Hoja lazima iwe neno"} } cmdMain := cmd.Value cmdArgs := strings.Split(cmdMain, " ") cmdArgs = cmdArgs[1:] out, err := exec.Command(cmdMain, cmdArgs...).Output() if err != nil { return &object.Error{Message: "Tumeshindwa kukimbiza komandi"} } return &object.String{Value: string(out)} } ================================================ FILE: module/time.go ================================================ package module import ( "fmt" "strconv" "time" "github.com/NuruProgramming/Nuru/object" ) var TimeFunctions = map[string]object.ModuleFunction{} func init() { TimeFunctions["hasahivi"] = now TimeFunctions["lala"] = sleep TimeFunctions["tangu"] = since TimeFunctions["leo"] = today TimeFunctions["baada_ya"] = after TimeFunctions["tofauti"] = diff TimeFunctions["ongeza"] = addTime } func now(args []object.Object, defs map[string]object.Object) object.Object { if len(args) != 0 || len(defs) != 0 { return &object.Error{Message: "hatuhitaji hoja kwenye hasahivi"} } tn := time.Now() time_string := tn.Format("15:04:05 02-01-2006") return &object.Time{TimeValue: time_string} } func sleep(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Hoja hii hairuhusiwi"} } if len(args) != 1 { return &object.Error{Message: "tunahitaji hoja moja tu"} } objvalue := args[0].Inspect() inttime, err := strconv.Atoi(objvalue) if err != nil { return &object.Error{Message: "namba tu zinaruhusiwa kwenye hoja"} } time.Sleep(time.Duration(inttime) * time.Second) return nil } func since(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 { return &object.Error{Message: "Hoja hii hairuhusiwi"} } if len(args) != 1 { return &object.Error{Message: "tunahitaji hoja moja tu"} } var ( t time.Time err error ) switch m := args[0].(type) { case *object.Time: t, _ = time.Parse("15:04:05 02-01-2006", m.TimeValue) case *object.String: t, err = time.Parse("15:04:05 02-01-2006", m.Value) if err != nil { return &object.Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} } default: return &object.Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} } current_time := time.Now().Format("15:04:05 02-01-2006") ct, _ := time.Parse("15:04:05 02-01-2006", current_time) diff := ct.Sub(t) durationInSeconds := diff.Seconds() return &object.Integer{Value: int64(durationInSeconds)} } func today(args []object.Object, defs map[string]object.Object) object.Object { if len(args) != 0 || len(defs) != 0 { return &object.Error{Message: "hatuhitaji hoja kwenye leo"} } dateStr := time.Now().Format("02-01-2006") return &object.String{Value: dateStr} } func after(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 || len(args) != 1 { return &object.Error{Message: "tunahitaji hoja moja tu kwenye baada_ya"} } secondsStr := args[0].Inspect() seconds, err := strconv.Atoi(secondsStr) if err != nil { return &object.Error{Message: "hoja lazima iwe namba"} } future := time.Now().Add(time.Duration(seconds) * time.Second) return &object.Time{TimeValue: future.Format("15:04:05 02-01-2006")} } func diff(args []object.Object, defs map[string]object.Object) object.Object { if len(defs) != 0 || len(args) != 2 { return &object.Error{Message: "tunahitaji hoja mbili kwenye tofauti"} } parseTime := func(o object.Object) (time.Time, error) { switch v := o.(type) { case *object.Time: return time.Parse("15:04:05 02-01-2006", v.TimeValue) case *object.String: return time.Parse("15:04:05 02-01-2006", v.Value) default: return time.Time{}, fmt.Errorf("aina batili") } } t1, err1 := parseTime(args[0]) t2, err2 := parseTime(args[1]) if err1 != nil || err2 != nil { return &object.Error{Message: "tofauti inahitaji nyakati halali mbili"} } diff := t1.Sub(t2).Seconds() return &object.Integer{Value: int64(diff)} } func addTime(args []object.Object, defs map[string]object.Object) object.Object { if len(args) != 1 { return &object.Error{Message: "ongeza inahitaji wakati mmoja wa kuanzia"} } baseTimeObj := args[0] baseTime, err := func() (time.Time, error) { switch t := baseTimeObj.(type) { case *object.Time: return time.Parse("15:04:05 02-01-2006", t.TimeValue) case *object.String: return time.Parse("15:04:05 02-01-2006", t.Value) default: return time.Time{}, fmt.Errorf("aina ya wakati sio sahihi") } }() if err != nil { return &object.Error{Message: "wakati uliotolewa sio sahihi"} } secs := getInt(defs["sekunde"]) mins := getInt(defs["dakika"]) hours := getInt(defs["masaa"]) days := getInt(defs["siku"]) weeks := getInt(defs["wiki"]) months := getInt(defs["miezi"]) years := getInt(defs["miaka"]) result := baseTime. Add(time.Second * time.Duration(secs)). Add(time.Minute * time.Duration(mins)). Add(time.Hour * time.Duration(hours)). AddDate(years, months, days+(weeks*7)) return &object.Time{TimeValue: result.Format("15:04:05 02-01-2006")} } func getInt(obj object.Object) int { if obj == nil { return 0 } switch o := obj.(type) { case *object.Integer: return int(o.Value) case *object.String: n, err := strconv.Atoi(o.Value) if err == nil { return n } } return 0 } ================================================ FILE: object/array.go ================================================ package object import ( "bytes" "strings" ) type Array struct { Elements []Object offset int } func (ao *Array) Type() ObjectType { return ARRAY_OBJ } func (ao *Array) Inspect() string { var out bytes.Buffer elements := []string{} if len(ao.Elements) != 0 { for _, e := range ao.Elements { if e.Inspect() != "" { elements = append(elements, e.Inspect()) } } } out.WriteString("[") out.WriteString(strings.Join(elements, ", ")) out.WriteString("]") return out.String() } func (ao *Array) Next() (Object, Object) { idx := ao.offset if len(ao.Elements) > idx { ao.offset = idx + 1 return &Integer{Value: int64(idx)}, ao.Elements[idx] } return nil, nil } func (ao *Array) Reset() { ao.offset = 0 } func (a *Array) Method(method string, args []Object) Object { switch method { case "idadi": return a.len(args) case "sukuma": return a.push(args) case "yamwisho": return a.last() case "unga": return a.join(args) case "chuja": return a.filter(args) case "tafuta": return a.find(args) default: return newError("Samahani, kiendesha hiki hakitumiki na tungo (Neno)") } } func (a *Array) len(args []Object) Object { if len(args) != 0 { return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) } return &Integer{Value: int64(len(a.Elements))} } func (a *Array) last() Object { length := len(a.Elements) if length > 0 { return a.Elements[length-1] } return &Null{} } func (a *Array) push(args []Object) Object { a.Elements = append(a.Elements, args...) return a } func (a *Array) join(args []Object) Object { if len(args) > 1 { return newError("Samahani, tunahitaji Hoja 1 au 0, wewe umeweka %d", len(args)) } if len(a.Elements) > 0 { glue := "" if len(args) == 1 { glue = args[0].(*String).Value } length := len(a.Elements) newElements := make([]string, length) for k, v := range a.Elements { newElements[k] = v.Inspect() } return &String{Value: strings.Join(newElements, glue)} } else { return &String{Value: ""} } } func (a *Array) filter(args []Object) Object { if len(args) != 1 { return newError("Samahani, idadi ya hoja sii sahihi") } dummy := []Object{} filteredArr := Array{Elements: dummy} for _, obj := range a.Elements { if obj.Inspect() == args[0].Inspect() && obj.Type() == args[0].Type() { filteredArr.Elements = append(filteredArr.Elements, obj) } } return &filteredArr } func (a *Array) find(args []Object) Object { if len(args) != 1 { return newError("Samahani, idadi ya hoja sii sahihi") } for _, obj := range a.Elements { if obj.Inspect() == args[0].Inspect() && obj.Type() == args[0].Type() { return obj } } return &Null{} } ================================================ FILE: object/at.go ================================================ package object import "fmt" type At struct { Instance *Instance } func (a *At) Type() ObjectType { return AT } func (a *At) Inspect() string { return fmt.Sprintf("@.%s", a.Instance.Package.Name.Value) } ================================================ FILE: object/bool.go ================================================ package object type Boolean struct { Value bool } func (b *Boolean) Inspect() string { if b.Value { return "kweli" } else { return "sikweli" } } func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ } func (b *Boolean) HashKey() HashKey { var value uint64 if b.Value { value = 1 } else { value = 0 } return HashKey{Type: b.Type(), Value: value} } ================================================ FILE: object/break.go ================================================ package object type Break struct{} func (b *Break) Type() ObjectType { return BREAK_OBJ } func (b *Break) Inspect() string { return "break" } ================================================ FILE: object/builtin.go ================================================ package object type BuiltinFunction func(args ...Object) Object type Builtin struct { Fn BuiltinFunction } func (b *Builtin) Inspect() string { return "builtin function" } func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ } ================================================ FILE: object/byte.go ================================================ package object type Byte struct { Value []byte String string } func (b *Byte) Inspect() string { return "b" + b.String } func (b *Byte) Type() ObjectType { return BYTE_OBJ } ================================================ FILE: object/continue.go ================================================ package object type Continue struct{} func (c *Continue) Type() ObjectType { return CONTINUE_OBJ } func (c *Continue) Inspect() string { return "continue" } ================================================ FILE: object/dict.go ================================================ package object import ( "bytes" "fmt" "sort" "strings" ) type DictPair struct { Key Object Value Object } type Dict struct { Pairs map[HashKey]DictPair offset int } func (d *Dict) Type() ObjectType { return DICT_OBJ } func (d *Dict) Inspect() string { var out bytes.Buffer pairs := []string{} for _, pair := range d.Pairs { pairs = append(pairs, fmt.Sprintf("%s: %s", pair.Key.Inspect(), pair.Value.Inspect())) } out.WriteString("{") out.WriteString(strings.Join(pairs, ", ")) out.WriteString("}") return out.String() } func (d *Dict) Next() (Object, Object) { idx := 0 dict := make(map[string]DictPair) var keys []string for _, v := range d.Pairs { dict[v.Key.Inspect()] = v keys = append(keys, v.Key.Inspect()) } sort.Strings(keys) for _, k := range keys { if d.offset == idx { d.offset += 1 return dict[k].Key, dict[k].Value } idx += 1 } return nil, nil } func (d *Dict) Reset() { d.offset = 0 } ================================================ FILE: object/environment.go ================================================ package object func NewEnclosedEnvironment(outer *Environment) *Environment { env := NewEnvironment() env.outer = outer return env } func NewEnvironment() *Environment { s := make(map[string]Object) return &Environment{store: s, outer: nil} } type Environment struct { store map[string]Object outer *Environment } func (e *Environment) Get(name string) (Object, bool) { obj, ok := e.store[name] if !ok && e.outer != nil { obj, ok = e.outer.Get(name) } return obj, ok } func (e *Environment) Set(name string, val Object) Object { e.store[name] = val return val } func (e *Environment) Del(name string) bool { _, ok := e.store[name] if ok { delete(e.store, name) } return true } ================================================ FILE: object/error.go ================================================ //go:build !js || !wasm package object import "fmt" type Error struct { Message string } func (e *Error) Inspect() string { msg := fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, "Kosa: ") return msg + e.Message } func (e *Error) Type() ObjectType { return ERROR_OBJ } ================================================ FILE: object/error_wasm.go ================================================ //go:build wasm && js package object type Error struct { Message string } func (e *Error) Inspect() string { // msg := fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, "Kosa: ") // removes ANSI codes return "Kosa: " + e.Message } func (e *Error) Type() ObjectType { return ERROR_OBJ } ================================================ FILE: object/file.go ================================================ package object import ( "os" ) type File struct { Filename string Content string } func (f *File) Type() ObjectType { return FILE_OBJ } func (f *File) Inspect() string { return f.Filename } func (f *File) Method(method string, args []Object) Object { switch method { case "soma": return f.read(args) case "andika": return f.write(args) case "ongeza": return f.append(args) } return nil } func (f *File) read(args []Object) Object { if len(args) != 0 { return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) } return &String{Value: f.Content} } func (f *File) write(args []Object) Object { if len(args) != 1 { return newError("Samahani, tunahitaji Hoja 1, wewe umeweka %d", len(args)) } content, ok := args[0].(*String) if !ok { return newError("Samahani, hoja lazima iwe Tungo") } err := os.WriteFile(f.Filename, []byte(content.Value), 0644) if err != nil { return newError("Hitilafu katika kuandika faili: %s", err.Error()) } f.Content = content.Value return &Boolean{Value: true} } func (f *File) append(args []Object) Object { if len(args) != 1 { return newError("Samahani, tunahitaji Hoja 1, wewe umeweka %d", len(args)) } content, ok := args[0].(*String) if !ok { return newError("Samahani, hoja lazima iwe Tungo") } file, err := os.OpenFile(f.Filename, os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return newError("Hitilafu katika kufungua faili: %s", err.Error()) } defer file.Close() _, err = file.WriteString(content.Value) if err != nil { return newError("Hitilafu katika kuongeza kwa faili: %s", err.Error()) } f.Content += content.Value return &Boolean{Value: true} } ================================================ FILE: object/float.go ================================================ package object import ( "hash/fnv" "strconv" ) type Float struct { Value float64 } func (f *Float) Inspect() string { return strconv.FormatFloat(f.Value, 'f', -1, 64) } func (f *Float) Type() ObjectType { return FLOAT_OBJ } func (f *Float) HashKey() HashKey { h := fnv.New64a() h.Write([]byte(f.Inspect())) return HashKey{Type: f.Type(), Value: h.Sum64()} } ================================================ FILE: object/function.go ================================================ package object import ( "bytes" "strings" "github.com/NuruProgramming/Nuru/ast" ) type Function struct { Name string Parameters []*ast.Identifier Defaults map[string]ast.Expression Body *ast.BlockStatement Env *Environment } func (f *Function) Type() ObjectType { return FUNCTION_OBJ } func (f *Function) Inspect() string { var out bytes.Buffer params := []string{} for _, p := range f.Parameters { params = append(params, p.String()) } out.WriteString("unda") out.WriteString("(") out.WriteString(strings.Join(params, ", ")) out.WriteString(") {\n") out.WriteString(f.Body.String()) out.WriteString("\n}") return out.String() } ================================================ FILE: object/instance.go ================================================ package object import "fmt" type Instance struct { Package *Package Env *Environment } func (i *Instance) Type() ObjectType { return INSTANCE } func (i *Instance) Inspect() string { return fmt.Sprintf("Pakeji: %s", i.Package.Name.Value) } ================================================ FILE: object/integer.go ================================================ package object import "fmt" type Integer struct { Value int64 } func (i *Integer) Inspect() string { return fmt.Sprintf("%d", i.Value) } func (i *Integer) Type() ObjectType { return INTEGER_OBJ } func (i *Integer) HashKey() HashKey { return HashKey{Type: i.Type(), Value: uint64(i.Value)} } ================================================ FILE: object/module.go ================================================ package object type ModuleFunction func(args []Object, defs map[string]Object) Object type Module struct { Name string Functions map[string]ModuleFunction } func (m *Module) Type() ObjectType { switch m.Name { case "time": return TIME_OBJ case "json": return JSON_OBJ default: return MODULE_OBJ } } func (m *Module) Inspect() string { return "Module: " + m.Name } ================================================ FILE: object/null.go ================================================ package object type Null struct{} func (n *Null) Inspect() string { return "null" } func (n *Null) Type() ObjectType { return NULL_OBJ } ================================================ FILE: object/object.go ================================================ package object import ( "fmt" ) type ObjectType string const ( INTEGER_OBJ = "NAMBA" FLOAT_OBJ = "DESIMALI" BOOLEAN_OBJ = "BOOLEAN" NULL_OBJ = "TUPU" RETURN_VALUE_OBJ = "RUDISHA" ERROR_OBJ = "KOSA" FUNCTION_OBJ = "UNDO (FUNCTION)" STRING_OBJ = "NENO" BUILTIN_OBJ = "YA_NDANI" ARRAY_OBJ = "ORODHA" DICT_OBJ = "KAMUSI" CONTINUE_OBJ = "ENDELEA" BREAK_OBJ = "VUNJA" FILE_OBJ = "FAILI" TIME_OBJ = "MUDA" JSON_OBJ = "JSONI" MODULE_OBJ = "MODULE" BYTE_OBJ = "BYTE" PACKAGE_OBJ = "PAKEJI" INSTANCE = "PAKEJI" AT = "@" ) type Object interface { Type() ObjectType Inspect() string } type HashKey struct { Type ObjectType Value uint64 } type Hashable interface { HashKey() HashKey } // Iterable interface for dicts, strings and arrays type Iterable interface { Next() (Object, Object) Reset() } func newError(format string, a ...interface{}) *Error { format = fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, format) return &Error{Message: fmt.Sprintf(format, a...)} } ================================================ FILE: object/object_test.go ================================================ package object import "testing" func TestStringHashKey(t *testing.T) { hello1 := &String{Value: "Hello World"} hello2 := &String{Value: "Hello World"} diff1 := &String{Value: "My name is Avi"} diff2 := &String{Value: "My name is Avi"} if hello1.HashKey() != hello2.HashKey() { t.Errorf("string with the same content have different dict keys") } if diff1.HashKey() != diff2.HashKey() { t.Errorf("String with the same content have different dict keys") } if hello1.HashKey() == diff1.HashKey() { t.Errorf("Strings with different content have the same dict keys") } } ================================================ FILE: object/package.go ================================================ package object import ( "fmt" "github.com/NuruProgramming/Nuru/ast" ) type Package struct { Name *ast.Identifier Env *Environment Scope *Environment } func (p *Package) Type() ObjectType { return PACKAGE_OBJ } func (p *Package) Inspect() string { return fmt.Sprintf("Pakeji: %s", p.Name.Value) } ================================================ FILE: object/return.go ================================================ package object type ReturnValue struct { Value Object } func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() } func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ } ================================================ FILE: object/strings.go ================================================ package object import ( "fmt" "hash/fnv" "strconv" "strings" ) type String struct { Value string offset int } func (s *String) Inspect() string { return s.Value } func (s *String) Type() ObjectType { return STRING_OBJ } func (s *String) HashKey() HashKey { h := fnv.New64a() h.Write([]byte(s.Value)) return HashKey{Type: s.Type(), Value: h.Sum64()} } func (s *String) Next() (Object, Object) { offset := s.offset if len(s.Value) > offset { s.offset = offset + 1 return &Integer{Value: int64(offset)}, &String{Value: string(s.Value[offset])} } return nil, nil } func (s *String) Reset() { s.offset = 0 } func (s *String) Method(method string, args []Object) Object { switch method { case "idadi": return s.len(args) case "herufikubwa": return s.upper(args) case "herufindogo": return s.lower(args) case "gawa": return s.split(args) case "panga": return s.format(args) default: return newError("Samahani, kiendesha hiki hakitumiki na tungo (Neno)") } } func (s *String) len(args []Object) Object { if len(args) != 0 { return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) } return &Integer{Value: int64(len(s.Value))} } func (s *String) upper(args []Object) Object { if len(args) != 0 { return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) } return &String{Value: strings.ToUpper(s.Value)} } func (s *String) lower(args []Object) Object { if len(args) != 0 { return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) } return &String{Value: strings.ToLower(s.Value)} } func (s *String) split(args []Object) Object { if len(args) > 1 { return newError("Samahani, tunahitaji Hoja 1 au 0, wewe umeweka %d", len(args)) } sep := " " if len(args) == 1 { sep = args[0].(*String).Value } parts := strings.Split(s.Value, sep) length := len(parts) elements := make([]Object, length) for k, v := range parts { elements[k] = &String{Value: v} } return &Array{Elements: elements} } func (s *String) format(args []Object) Object { value, err := formatStr(s.Value, args) if err != nil { return newError(err.Error()) } return &String{Value: value} } func formatStr(format string, options []Object) (string, error) { var str strings.Builder var val strings.Builder var check_val bool var opts_len int = len(options) var escapeChar bool type optM struct { val bool obj Object } var optionsMap = make(map[int]optM, opts_len) for i, optm := range options { optionsMap[i] = optM{val: false, obj: optm} } for _, opt := range format { if !escapeChar && opt == '\\' { escapeChar = true continue } if opt == '{' && !escapeChar { check_val = true continue } if escapeChar { if opt != '{' && opt != '}' { str.WriteRune('\\') } escapeChar = false } if check_val && opt == '}' { vstr := strings.TrimSpace(val.String()) arrv, err := strconv.Atoi(vstr) if err != nil { return "", fmt.Errorf(fmt.Sprintf("Ulichopeana si NAMBA, jaribu tena: `%s'", vstr)) } oVal, exists := optionsMap[arrv] if !exists { return "", fmt.Errorf(fmt.Sprintf("Nambari ya chaguo unalolitaka %d ni kubwa kuliko ulizopeana (%d)", arrv, opts_len)) } str.WriteString(oVal.obj.Inspect()) optionsMap[arrv] = optM{val: true, obj: oVal.obj} check_val = false val.Reset() continue } if check_val { val.WriteRune(opt) continue } str.WriteRune(opt) } if check_val { return "", fmt.Errorf(fmt.Sprintf("Haukufunga '{', tuliokota kabla ya kufika mwisho `%s'", val.String())) } for _, v := range optionsMap { if !v.val { return "", fmt.Errorf(fmt.Sprintf("Ulipeana hili chaguo (%s) {%s} lakini haukutumia", v.obj.Inspect(), v.obj.Type())) } } return str.String(), nil } ================================================ FILE: object/time.go ================================================ package object import ( "fmt" "strconv" "time" ) type Time struct { TimeValue string } func (t *Time) Type() ObjectType { return TIME_OBJ } func (t *Time) Inspect() string { return t.TimeValue } func (t *Time) Method(method string, args []Object, defs map[string]Object) Object { switch method { case "ongeza": return t.add(args, defs) case "tangu": return t.since(args, defs) } return nil } func (t *Time) add(args []Object, defs map[string]Object) Object { if len(defs) != 0 { var sec, min, hr, d, m, y int for k, v := range defs { objvalue := v.Inspect() inttime, err := strconv.Atoi(objvalue) if err != nil { return newError("namba tu zinaruhusiwa kwenye hoja") } switch k { case "sekunde": sec = inttime case "dakika": min = inttime case "saa": hr = inttime case "siku": d = inttime case "miezi": m = inttime case "miaka": y = inttime default: return newError("Hukuweka muda sahihi") } } cur_time, _ := time.Parse("15:04:05 02-01-2006", t.Inspect()) next_time := cur_time. Add(time.Duration(sec)*time.Second). Add(time.Duration(min)*time.Minute). Add(time.Duration(hr)*time.Hour). AddDate(y, m, d) return &Time{TimeValue: string(next_time.Format("15:04:05 02-01-2006"))} } if len(args) != 1 { return newError("Samahani, tunahitaji Hoja 1, wewe umeweka %d", len(args)) } cur_time, _ := time.Parse("15:04:05 02-01-2006", t.Inspect()) objvalue := args[0].Inspect() inttime, err := strconv.Atoi(objvalue) if err != nil { return newError("namba tu zinaruhusiwa kwenye hoja") } next_time := cur_time.Add(time.Duration(inttime) * time.Hour) return &Time{TimeValue: string(next_time.Format("15:04:05 02-01-2006"))} } func (t *Time) since(args []Object, defs map[string]Object) Object { if len(defs) != 0 { return &Error{Message: "Hoja hii hairuhusiwi"} } if len(args) != 1 { return &Error{Message: "tunahitaji hoja moja tu"} } var ( o time.Time err error ) switch m := args[0].(type) { case *Time: o, _ = time.Parse("15:04:05 02-01-2006", m.TimeValue) case *String: o, err = time.Parse("15:04:05 02-01-2006", m.Value) if err != nil { return &Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} } default: return &Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} } ct, _ := time.Parse("15:04:05 02-01-2006", t.TimeValue) diff := ct.Sub(o) durationInSeconds := diff.Seconds() return &Integer{Value: int64(durationInSeconds)} } ================================================ FILE: parser/arrays.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseArrayLiteral() ast.Expression { array := &ast.ArrayLiteral{Token: p.curToken} array.Elements = p.parseExpressionList(token.RBRACKET) return array } func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression { list := []ast.Expression{} if p.peekTokenIs(end) { p.nextToken() return list } p.nextToken() list = append(list, p.parseExpression(LOWEST)) for p.peekTokenIs(token.COMMA) { p.nextToken() p.nextToken() list = append(list, p.parseExpression(LOWEST)) } if !p.expectPeek(end) { return nil } return list } ================================================ FILE: parser/assignEqual.go ================================================ package parser import ( "fmt" "github.com/NuruProgramming/Nuru/ast" ) func (p *Parser) parseAssignEqualExpression(exp ast.Expression) ast.Expression { switch node := exp.(type) { case *ast.Identifier: e := &ast.AssignEqual{ Token: p.curToken, Left: exp.(*ast.Identifier), } precendence := p.curPrecedence() p.nextToken() e.Value = p.parseExpression(precendence) return e case *ast.IndexExpression: ae := &ast.AssignmentExpression{Token: p.curToken, Left: exp} p.nextToken() ae.Value = p.parseExpression(LOWEST) return ae default: if node != nil { msg := fmt.Sprintf("Mstari %d:Tulitegemea kupata kitambulishi au array, badala yake tumepata: %s", p.curToken.Line, node.TokenLiteral()) p.errors = append(p.errors, msg) } else { msg := fmt.Sprintf("Mstari %d: Umekosea mkuu", p.curToken.Line) p.errors = append(p.errors, msg) } return nil } } ================================================ FILE: parser/assignment.go ================================================ package parser import ( "fmt" "github.com/NuruProgramming/Nuru/ast" ) func (p *Parser) parseAssignmentExpression(exp ast.Expression) ast.Expression { switch node := exp.(type) { case *ast.Identifier: e := &ast.Assign{ Token: p.curToken, Name: exp.(*ast.Identifier), } precedence := p.curPrecedence() p.nextToken() e.Value = p.parseExpression(precedence) return e case *ast.IndexExpression: case *ast.PropertyExpression: e := &ast.PropertyAssignment{ Token: p.curToken, Name: exp.(*ast.PropertyExpression), } precedence := p.curPrecedence() p.nextToken() e.Value = p.parseExpression(precedence) return e default: if node != nil { msg := fmt.Sprintf("Mstari %d:Tulitegemea kupata kitambulishi au array, badala yake tumepata: %s", p.curToken.Line, node.TokenLiteral()) p.errors = append(p.errors, msg) } else { msg := fmt.Sprintf("Mstari %d: Umekosea mkuu", p.curToken.Line) p.errors = append(p.errors, msg) } return nil } ae := &ast.AssignmentExpression{Token: p.curToken, Left: exp} p.nextToken() ae.Value = p.parseExpression(LOWEST) return ae } ================================================ FILE: parser/at.go ================================================ package parser import "github.com/NuruProgramming/Nuru/ast" func (p *Parser) parseAt() ast.Expression { return &ast.At{Token: p.curToken} } ================================================ FILE: parser/boolean.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseBoolean() ast.Expression { return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)} } ================================================ FILE: parser/break.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseBreak() *ast.Break { stmt := &ast.Break{Token: p.curToken} for p.curTokenIs(token.SEMICOLON) { p.nextToken() } return stmt } ================================================ FILE: parser/continue.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseContinue() *ast.Continue { stmt := &ast.Continue{Token: p.curToken} for p.curTokenIs(token.SEMICOLON) { p.nextToken() } return stmt } ================================================ FILE: parser/dict.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseDictLiteral() ast.Expression { dict := &ast.DictLiteral{Token: p.curToken} dict.Pairs = make(map[ast.Expression]ast.Expression) for !p.peekTokenIs(token.RBRACE) { p.nextToken() key := p.parseExpression(LOWEST) if !p.expectPeek(token.COLON) { return nil } p.nextToken() value := p.parseExpression(LOWEST) dict.Pairs[key] = value if !p.peekTokenIs(token.RBRACE) && !p.expectPeek(token.COMMA) { return nil } } if !p.expectPeek(token.RBRACE) { return nil } return dict } ================================================ FILE: parser/dot.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseMethod(obj ast.Expression) ast.Expression { tok := p.curToken precedence := p.curPrecedence() p.nextToken() if p.peekTokenIs(token.LPAREN) { exp := &ast.MethodExpression{Token: tok, Object: obj} exp.Method = p.parseExpression(precedence) if !p.expectPeek(token.LPAREN) { return nil } exp.Defaults = make(map[string]ast.Expression) for !p.peekTokenIs(token.RPAREN) { p.nextToken() if p.curTokenIs(token.COMMA) { continue } if p.peekTokenIs(token.ASSIGN) { name := p.curToken.Literal p.nextToken() p.nextToken() val := p.parseExpression(LOWEST) exp.Defaults[name] = val } else { exp.Arguments = append(exp.Arguments, p.parseExpression(LOWEST)) } } if !p.expectPeek(token.RPAREN) { return nil } return exp } else { exp := &ast.PropertyExpression{Token: tok, Object: obj} exp.Property = p.parseIdentifier() return exp } } ================================================ FILE: parser/float.go ================================================ package parser import ( "fmt" "strconv" "github.com/NuruProgramming/Nuru/ast" ) func (p *Parser) parseFloatLiteral() ast.Expression { fl := &ast.FloatLiteral{Token: p.curToken} value, err := strconv.ParseFloat(p.curToken.Literal, 64) if err != nil { msg := fmt.Sprintf("Mstari %d: Hatuwezi kuparse %q kama desimali", p.curToken.Line, p.curToken.Literal) p.errors = append(p.errors, msg) return nil } fl.Value = value return fl } ================================================ FILE: parser/for.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseForExpression() ast.Expression { expression := &ast.For{Token: p.curToken} p.nextToken() if !p.curTokenIs(token.IDENT) { return nil } if !p.peekTokenIs(token.ASSIGN) { return p.parseForInExpression(expression) } // In future will allow: kwa i = 0; i<10; i++ {andika(i)} // expression.Identifier = p.curToken.Literal // expression.StarterName = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} // if expression.StarterName == nil { // return nil // } // if !p.expectPeek(token.ASSIGN) { // return nil // } // p.nextToken() // expression.StarterValue = p.parseExpression(LOWEST) // // expression.Starter = p.parseExpression(LOWEST) // if expression.StarterValue == nil { // return nil // } // p.nextToken() // for p.curTokenIs(token.SEMICOLON) { // p.nextToken() // } // expression.Condition = p.parseExpression(LOWEST) // if expression.Condition == nil { // return nil // } // p.nextToken() // for p.curTokenIs(token.SEMICOLON) { // p.nextToken() // } // expression.Closer = p.parseExpression(LOWEST) // if expression.Closer == nil { // return nil // } // p.nextToken() // for p.curTokenIs(token.SEMICOLON) { // p.nextToken() // } // if !p.curTokenIs(token.LBRACE) { // return nil // } // expression.Block = p.parseBlockStatement() // return expression return nil } func (p *Parser) parseForInExpression(initialExpression *ast.For) ast.Expression { expression := &ast.ForIn{Token: initialExpression.Token} if !p.curTokenIs(token.IDENT) { return nil } val := p.curToken.Literal var key string p.nextToken() if p.curTokenIs(token.COMMA) { p.nextToken() if !p.curTokenIs(token.IDENT) { return nil } key = val val = p.curToken.Literal p.nextToken() } expression.Key = key expression.Value = val if !p.curTokenIs(token.IN) { return nil } p.nextToken() expression.Iterable = p.parseExpression(LOWEST) if !p.expectPeek(token.LBRACE) { return nil } expression.Block = p.parseBlockStatement() return expression } ================================================ FILE: parser/function.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseFunctionLiteral() ast.Expression { lit := &ast.FunctionLiteral{Token: p.curToken} if p.peekTokenIs(token.IDENT) { p.nextToken() lit.Name = p.curToken.Literal } if !p.expectPeek(token.LPAREN) { return nil } if !p.parseFunctionParameters(lit) { return nil } if !p.expectPeek(token.LBRACE) { return nil } lit.Body = p.parseBlockStatement() return lit } func (p *Parser) parseFunctionParameters(lit *ast.FunctionLiteral) bool { lit.Defaults = make(map[string]ast.Expression) for !p.peekTokenIs(token.RPAREN) { p.nextToken() if p.curTokenIs(token.COMMA) { continue } ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} lit.Parameters = append(lit.Parameters, ident) if p.peekTokenIs(token.ASSIGN) { p.nextToken() p.nextToken() lit.Defaults[ident.Value] = p.parseExpression(LOWEST) } else { if len(lit.Defaults) > 0 { return false } } if !(p.peekTokenIs(token.COMMA) || p.peekTokenIs(token.RPAREN)) { return false } } return p.expectPeek(token.RPAREN) } func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression { exp := &ast.CallExpression{Token: p.curToken, Function: function} exp.Arguments = p.parseExpressionList(token.RPAREN) return exp } ================================================ FILE: parser/identifier.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" ) func (p *Parser) parseIdentifier() ast.Expression { return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} } ================================================ FILE: parser/if.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseIfExpression() ast.Expression { expression := &ast.IfExpression{Token: p.curToken} if !p.expectPeek(token.LPAREN) { return nil } p.nextToken() expression.Condition = p.parseExpression(LOWEST) if !p.expectPeek(token.RPAREN) { return nil } if !p.expectPeek(token.LBRACE) { return nil } expression.Consequence = p.parseBlockStatement() if p.peekTokenIs(token.ELSE) { p.nextToken() if p.peekTokenIs(token.IF) { p.nextToken() expression.Alternative = &ast.BlockStatement{ Statements: []ast.Statement{ &ast.ExpressionStatement{ Expression: p.parseIfExpression(), }, }, } return expression } if !p.expectPeek(token.LBRACE) { return nil } expression.Alternative = p.parseBlockStatement() } return expression } ================================================ FILE: parser/import.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseImport() ast.Expression { exp := &ast.Import{Token: p.curToken} exp.Identifiers = make(map[string]*ast.Identifier) for p.curToken.Line == p.peekToken.Line { p.nextToken() identifier := &ast.Identifier{Value: p.curToken.Literal} exp.Identifiers[p.curToken.Literal] = identifier if p.peekTokenIs(token.COMMA) { p.nextToken() } if p.peekTokenIs(token.EOF) { break } } return exp } ================================================ FILE: parser/index.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression { exp := &ast.IndexExpression{Token: p.curToken, Left: left} p.nextToken() exp.Index = p.parseExpression(LOWEST) if !p.expectPeek(token.RBRACKET) { return nil } return exp } ================================================ FILE: parser/integer.go ================================================ package parser import ( "fmt" "strconv" "github.com/NuruProgramming/Nuru/ast" ) func (p *Parser) parseIntegerLiteral() ast.Expression { lit := &ast.IntegerLiteral{Token: p.curToken} value, err := strconv.ParseInt(p.curToken.Literal, 0, 64) if err != nil { msg := fmt.Sprintf("Mstari %d: Hatuwezi kuparse %q kama namba", p.curToken.Line, p.curToken.Literal) p.errors = append(p.errors, msg) return nil } lit.Value = value return lit } ================================================ FILE: parser/null.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" ) func (p *Parser) parseNull() ast.Expression { return &ast.Null{Token: p.curToken} } ================================================ FILE: parser/package.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parsePackage() ast.Expression { expression := &ast.Package{Token: p.curToken} p.nextToken() expression.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} if !p.expectPeek(token.LBRACE) { return nil } expression.Block = p.parseBlockStatement() return expression } ================================================ FILE: parser/parser.go ================================================ package parser import ( "fmt" "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/lexer" "github.com/NuruProgramming/Nuru/token" ) const ( _ int = iota LOWEST ASSIGN // = COND // OR or AND EQUALS // == LESSGREATER // > OR < SUM // + PRODUCT // * POWER // ** we got the power XD MODULUS // % PREFIX // -X OR !X CALL // myFunction(X) INDEX // Arrays DOT // For methods ) var precedences = map[token.TokenType]int{ token.AND: COND, token.OR: COND, token.IN: COND, token.ASSIGN: ASSIGN, token.EQ: EQUALS, token.NOT_EQ: EQUALS, token.LT: LESSGREATER, token.LTE: LESSGREATER, token.GT: LESSGREATER, token.GTE: LESSGREATER, token.PLUS: SUM, token.PLUS_ASSIGN: SUM, token.MINUS: SUM, token.MINUS_ASSIGN: SUM, token.SLASH: PRODUCT, token.SLASH_ASSIGN: PRODUCT, token.ASTERISK: PRODUCT, token.ASTERISK_ASSIGN: PRODUCT, token.POW: POWER, token.MODULUS: MODULUS, token.MODULUS_ASSIGN: MODULUS, // token.BANG: PREFIX, token.LPAREN: CALL, token.LBRACKET: INDEX, token.DOT: DOT, // Highest priority } type ( prefixParseFn func() ast.Expression infixParseFn func(ast.Expression) ast.Expression postfixParseFn func() ast.Expression ) type Parser struct { l *lexer.Lexer curToken token.Token peekToken token.Token prevToken token.Token errors []string prefixParseFns map[token.TokenType]prefixParseFn infixParseFns map[token.TokenType]infixParseFn postfixParseFns map[token.TokenType]postfixParseFn } func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) { p.prefixParseFns[tokenType] = fn } func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) { p.infixParseFns[tokenType] = fn } func (p *Parser) registerPostfix(tokenType token.TokenType, fn postfixParseFn) { p.postfixParseFns[tokenType] = fn } func New(l *lexer.Lexer) *Parser { p := &Parser{l: l, errors: []string{}} p.nextToken() p.nextToken() p.prefixParseFns = make(map[token.TokenType]prefixParseFn) p.registerPrefix(token.STRING, p.parseStringLiteral) p.registerPrefix(token.IDENT, p.parseIdentifier) p.registerPrefix(token.INT, p.parseIntegerLiteral) p.registerPrefix(token.FLOAT, p.parseFloatLiteral) p.registerPrefix(token.BANG, p.parsePrefixExpression) p.registerPrefix(token.MINUS, p.parsePrefixExpression) p.registerPrefix(token.PLUS, p.parsePrefixExpression) p.registerPrefix(token.TRUE, p.parseBoolean) p.registerPrefix(token.FALSE, p.parseBoolean) p.registerPrefix(token.LPAREN, p.parseGroupedExpression) p.registerPrefix(token.IF, p.parseIfExpression) p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral) p.registerPrefix(token.LBRACKET, p.parseArrayLiteral) p.registerPrefix(token.LBRACE, p.parseDictLiteral) p.registerPrefix(token.WHILE, p.parseWhileExpression) p.registerPrefix(token.NULL, p.parseNull) p.registerPrefix(token.FOR, p.parseForExpression) p.registerPrefix(token.SWITCH, p.parseSwitchStatement) p.registerPrefix(token.IMPORT, p.parseImport) p.registerPrefix(token.PACKAGE, p.parsePackage) p.registerPrefix(token.AT, p.parseAt) p.infixParseFns = make(map[token.TokenType]infixParseFn) p.registerInfix(token.AND, p.parseInfixExpression) p.registerInfix(token.OR, p.parseInfixExpression) p.registerInfix(token.PLUS, p.parseInfixExpression) p.registerInfix(token.PLUS_ASSIGN, p.parseAssignEqualExpression) p.registerInfix(token.MINUS, p.parseInfixExpression) p.registerInfix(token.MINUS_ASSIGN, p.parseAssignEqualExpression) p.registerInfix(token.SLASH, p.parseInfixExpression) p.registerInfix(token.SLASH_ASSIGN, p.parseAssignEqualExpression) p.registerInfix(token.ASTERISK, p.parseInfixExpression) p.registerInfix(token.ASTERISK_ASSIGN, p.parseAssignEqualExpression) p.registerInfix(token.POW, p.parseInfixExpression) p.registerInfix(token.MODULUS, p.parseInfixExpression) p.registerInfix(token.MODULUS_ASSIGN, p.parseAssignmentExpression) p.registerInfix(token.EQ, p.parseInfixExpression) p.registerInfix(token.NOT_EQ, p.parseInfixExpression) p.registerInfix(token.LT, p.parseInfixExpression) p.registerInfix(token.LTE, p.parseInfixExpression) p.registerInfix(token.GT, p.parseInfixExpression) p.registerInfix(token.GTE, p.parseInfixExpression) p.registerInfix(token.LPAREN, p.parseCallExpression) p.registerInfix(token.LBRACKET, p.parseIndexExpression) p.registerInfix(token.ASSIGN, p.parseAssignmentExpression) p.registerInfix(token.IN, p.parseInfixExpression) p.registerInfix(token.DOT, p.parseMethod) p.postfixParseFns = make(map[token.TokenType]postfixParseFn) p.registerPostfix(token.PLUS_PLUS, p.parsePostfixExpression) p.registerPostfix(token.MINUS_MINUS, p.parsePostfixExpression) return p } func (p *Parser) ParseProgram() *ast.Program { program := &ast.Program{} program.Statements = []ast.Statement{} for !p.curTokenIs(token.EOF) { stmt := p.parseStatement() program.Statements = append(program.Statements, stmt) p.nextToken() } return program } // manage token literals: func (p *Parser) nextToken() { p.prevToken = p.curToken p.curToken = p.peekToken p.peekToken = p.l.NextToken() } func (p *Parser) curTokenIs(t token.TokenType) bool { return p.curToken.Type == t } func (p *Parser) peekTokenIs(t token.TokenType) bool { return p.peekToken.Type == t } func (p *Parser) expectPeek(t token.TokenType) bool { if p.peekTokenIs(t) { p.nextToken() return true } else { p.peekError(t) return false } } func (p *Parser) peekPrecedence() int { if p, ok := precedences[p.peekToken.Type]; ok { return p } return LOWEST } func (p *Parser) curPrecedence() int { if p, ok := precedences[p.curToken.Type]; ok { return p } return LOWEST } // error messages func (p *Parser) Errors() []string { return p.errors } func (p *Parser) peekError(t token.TokenType) { msg := fmt.Sprintf("Mstari %d: Tulitegemea kupata %s, badala yake tumepata %s", p.curToken.Line, t, p.peekToken.Type) p.errors = append(p.errors, msg) } // parse expressions func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement { stmt := &ast.ExpressionStatement{Token: p.curToken} stmt.Expression = p.parseExpression(LOWEST) if p.peekTokenIs(token.SEMICOLON) { p.nextToken() } return stmt } func (p *Parser) parseExpression(precedence int) ast.Expression { postfix := p.postfixParseFns[p.curToken.Type] if postfix != nil { return (postfix()) } prefix := p.prefixParseFns[p.curToken.Type] if prefix == nil { p.noPrefixParseFnError(p.curToken.Type) return nil } leftExp := prefix() for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() { infix := p.infixParseFns[p.peekToken.Type] if infix == nil { p.noInfixParseFnError(p.peekToken.Type) return nil } p.nextToken() leftExp = infix(leftExp) } return leftExp } // prefix expressions func (p *Parser) parsePrefixExpression() ast.Expression { expression := &ast.PrefixExpression{ Token: p.curToken, Operator: p.curToken.Literal, } p.nextToken() expression.Right = p.parseExpression(PREFIX) return expression } func (p *Parser) noPrefixParseFnError(t token.TokenType) { msg := fmt.Sprintf("Mstari %d: Tumeshindwa kuparse %s", p.curToken.Line, t) p.errors = append(p.errors, msg) } // infix expressions func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression { expression := &ast.InfixExpression{ Token: p.curToken, Operator: p.curToken.Literal, Left: left, } precedence := p.curPrecedence() p.nextToken() expression.Right = p.parseExpression(precedence) return expression } func (p *Parser) noInfixParseFnError(t token.TokenType) { msg := fmt.Sprintf("Mstari %d: Tumeshindwa kuparse %s", p.curToken.Line, t) p.errors = append(p.errors, msg) } func (p *Parser) parseGroupedExpression() ast.Expression { p.nextToken() exp := p.parseExpression(LOWEST) if !p.expectPeek(token.RPAREN) { return nil } return exp } // postfix expressions func (p *Parser) parsePostfixExpression() ast.Expression { expression := &ast.PostfixExpression{ Token: p.prevToken, Operator: p.curToken.Literal, } return expression } ================================================ FILE: parser/parser_test.go ================================================ package parser import ( "fmt" "testing" "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/lexer" ) func TestLetStatements(t *testing.T) { tests := []struct { input string expectedIdentifier string expectedValue interface{} }{ {"fanya x = 5;", "x", 5}, {"fanya y = x;", "y", "x"}, {"fanya bangi = y;", "bangi", "y"}, } for _, tt := range tests { l := lexer.New(tt.input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Statements does not contain 1 statements. got=%d", len(program.Statements)) } stmt := program.Statements[0] if !testLetStatement(t, stmt, tt.expectedIdentifier) { return } val := stmt.(*ast.LetStatement).Value if !testLiteralExpression(t, val, tt.expectedValue) { return } } } func testLetStatement(t *testing.T, s ast.Statement, name string) bool { if s.TokenLiteral() != "fanya" { t.Errorf("s.TokenLiteral not 'fanya', got = %q", s.TokenLiteral()) return false } letStmt, ok := s.(*ast.LetStatement) if !ok { t.Errorf("s not *ast.LetStatement, got = %T", s) return false } if letStmt.Name.Value != name { t.Errorf("letStmt.Name.Value not '%s', got='%s'", name, letStmt.Name.Value) return false } if letStmt.Name.TokenLiteral() != name { t.Errorf("s.Name not %s, got=%s", name, letStmt.Name) return false } return true } func checkParserErrors(t *testing.T, p *Parser) { errors := p.Errors() if len(errors) == 0 { return } t.Errorf("Parser has %d errors", len(errors)) for _, msg := range errors { t.Errorf("Parser error: %q", msg) } t.FailNow() } func TestReturnStatements(t *testing.T) { tests := []struct { input string expectedValue interface{} }{ {"rudisha 5;", 5}, {"rudisha kweli;", true}, {"rudisha bangi;", "bangi"}, } for _, tt := range tests { l := lexer.New(tt.input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Statements does not contain 1 statements. got=%d", len(program.Statements)) } stmt := program.Statements[0] returnStmt, ok := stmt.(*ast.ReturnStatement) if !ok { t.Fatalf("stmt not *ast.returnStatement. got=%T", stmt) } if returnStmt.TokenLiteral() != "rudisha" { t.Fatalf("returnStmt.TokenLiteral not 'rudisha', got %q", returnStmt.TokenLiteral()) } if testLiteralExpression(t, returnStmt.ReturnValue, tt.expectedValue) { return } } } func TestIdentifierExpression(t *testing.T) { input := "foobar;" l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program has not enough statements. got=%d", len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not an ast.ExpressionStatement, got=%T", program.Statements[0]) } ident, ok := stmt.Expression.(*ast.Identifier) if !ok { t.Fatalf("exp not *ast.Identifier, got=%T", stmt.Expression) } if ident.Value != "foobar" { t.Errorf("ident.Value not %s, got=%s", "foobar", ident.Value) } if ident.TokenLiteral() != "foobar" { t.Errorf("ident.TokenLiteral not %s, got=%s", "foobar", ident.TokenLiteral()) } } func TestIntergerLiteral(t *testing.T) { input := "5;" l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program has not enough statements, got=%d", len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0]) } literal, ok := stmt.Expression.(*ast.IntegerLiteral) if !ok { t.Fatalf("exp not *ast.IntegerLiteral, got=%T", stmt.Expression) } if literal.Value != 5 { t.Errorf("literal.Value not %d, got=%d", 5, literal.Value) } if literal.TokenLiteral() != "5" { t.Errorf("literal.TokenLiteral not %s, got=%s", "5", literal.TokenLiteral()) } } func TestParsingPrefixExpressions(t *testing.T) { prefixTests := []struct { input string operator string value interface{} }{ {"!5;", "!", 5}, {"-15;", "-", 15}, {"!kweli", "!", true}, {"!sikweli", "!", false}, } for _, tt := range prefixTests { l := lexer.New(tt.input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("Program statements does not contain %d statements, got=%d\n", 1, len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0]) } exp, ok := stmt.Expression.(*ast.PrefixExpression) if !ok { t.Fatalf("stmt is not ast.PrefixExpression, got=%T", stmt.Expression) } if exp.Operator != tt.operator { t.Fatalf("exp.Operator is not %s, got=%s", tt.operator, exp.Operator) } if !testLiteralExpression(t, exp.Right, tt.value) { return } } } func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool { integ, ok := il.(*ast.IntegerLiteral) if !ok { t.Errorf("il not *ast.IntegerLiteral, got=%T", il) return false } if integ.Value != value { t.Errorf("il not %d, got=%d", value, integ.Value) return false } if integ.TokenLiteral() != fmt.Sprintf("%d", value) { t.Errorf("integ.TokenLiteral not %d, got=%s", value, integ.TokenLiteral()) return false } return true } func TestParsingInfixExpressions(t *testing.T) { infixTests := []struct { input string leftValue interface{} operator string rightValue interface{} }{ {"5 + 5;", 5, "+", 5}, {"5 - 5;", 5, "-", 5}, {"5 * 5;", 5, "*", 5}, {"5 / 5;", 5, "/", 5}, {"5 > 5;", 5, ">", 5}, {"5 < 5;", 5, "<", 5}, {"5 == 5;", 5, "==", 5}, {"5 != 5;", 5, "!=", 5}, {"5 >= 5;", 5, ">=", 5}, {"5 <= 5;", 5, "<=", 5}, {"5 || 5;", 5, "||", 5}, {"5 && 5;", 5, "&&", 5}, {"kweli == kweli", true, "==", true}, {"kweli != sikweli", true, "!=", false}, {"sikweli == sikweli", false, "==", false}, } for _, tt := range infixTests { l := lexer.New(tt.input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("Program.statements does not contain %d statements, got =%d", 1, len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0]) } if !testInfixExpression(t, stmt.Expression, tt.leftValue, tt.operator, tt.rightValue) { return } } } func TestOperatorPrecedenceParsing(t *testing.T) { tests := []struct { input string expected string }{ { "-a * b", "((-a) * b)", }, { "!-a", "(!(-a))", }, { "a + b + c", "((a + b) + c)", }, { "a + b - c", "((a + b) - c)", }, { "a * b * c", "((a * b) * c)", }, { "a*b /c", "((a * b) / c)", }, { "a + b / c", "(a + (b / c))", }, { "a + b * c + d / e - f", "(((a + (b * c)) + (d / e)) - f)", }, { "3 + 4; -5 * 5", "(3 + 4)((-5) * 5)", }, { "5 > 4 == 3 < 4", "((5 > 4) == (3 < 4))", }, { "5 < 4 != 3 > 4", "((5 < 4) != (3 > 4))", }, { "3 + 4 * 5 == 3 * 1 + 4 * 5", "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))", }, { "kweli", "kweli", }, { "sikweli", "sikweli", }, { "3 > 5 == sikweli", "((3 > 5) == sikweli)", }, { "3 < 5 == kweli", "((3 < 5) == kweli)", }, { "1 + (2 + 3) + 4", "((1 + (2 + 3)) + 4)", }, { "(5 + 5) * 2", "((5 + 5) * 2)", }, { "2 / (5 + 5)", "(2 / (5 + 5))", }, { "-(5 + 5)", "(-(5 + 5))", }, { "!(kweli == kweli)", "(!(kweli == kweli))", }, { "a + add(b * c) + d", "((a + add((b * c))) + d)", }, { "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))", "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))", }, { "add(a + b + c * d / f + g)", "add((((a + b) + ((c * d) / f)) + g))", }, { "a * [1, 2, 3, 4][b * c] * d", "((a * ([1, 2, 3, 4][(b * c)])) * d)", }, { "add(a *b[2], b[1], 2 * [1, 2][1])", "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))", }, } for _, tt := range tests { l := lexer.New(tt.input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) actual := program.String() if actual != tt.expected { t.Errorf("expected=%q, got=%q", tt.expected, actual) } } } func testIdentifier(t *testing.T, exp ast.Expression, value string) bool { ident, ok := exp.(*ast.Identifier) if !ok { t.Errorf("exp not *ast.Identifier, got=%T", exp) return false } if ident.Value != value { t.Errorf("ident.Value not %s, got=%s", value, ident.Value) return false } if ident.TokenLiteral() != value { t.Errorf("ident.TokenLiteral is not %s, got=%s", value, ident.TokenLiteral()) return false } return true } func testLiteralExpression( t *testing.T, exp ast.Expression, expected interface{}, ) bool { switch v := expected.(type) { case int: return testIntegerLiteral(t, exp, int64(v)) case int64: return testIntegerLiteral(t, exp, v) case string: return testIdentifier(t, exp, v) case bool: return testBooleanLiteral(t, exp, v) } t.Errorf("type of exp not handled, got=%T", exp) return false } func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool { bo, ok := exp.(*ast.Boolean) if !ok { t.Errorf("exp not *ast.Boolean, got=%T", exp) return false } if bo.Value != value { t.Errorf("bo.Value not %t,got=%t", value, bo.Value) return false } return true } func testInfixExpression( t *testing.T, exp ast.Expression, left interface{}, operator string, right interface{}, ) bool { opExp, ok := exp.(*ast.InfixExpression) if !ok { t.Errorf("exp is not ast.OperatorExpression, got=%T(%s)", exp, exp) return false } if !testLiteralExpression(t, opExp.Left, left) { return false } if opExp.Operator != operator { t.Errorf("exp.Operator is not %s, got=%q", operator, opExp.Operator) return false } if !testLiteralExpression(t, opExp.Right, right) { return false } return true } func TestBooleanExpression(t *testing.T) { tests := []struct { input string expectedBoolean bool }{ {"kweli;", true}, {"sikweli;", false}, } for _, tt := range tests { l := lexer.New(tt.input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program has not enough statements. got=%d", len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not an ast.ExpressionStatement, got=%T", program.Statements[0]) } boolean, ok := stmt.Expression.(*ast.Boolean) if !ok { t.Fatalf("exp not *ast.Boolean, got=%T", stmt.Expression) } if boolean.Value != tt.expectedBoolean { t.Errorf("boolean.Value not %t, got=%t", tt.expectedBoolean, boolean.Value) } } } func TestIfExpression(t *testing.T) { input := `kama (x < y) { x }` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Body does not contain %d statements, got=%d", 1, len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not an ast.ExpressionStatement, got=%T", program.Statements[0]) } exp, ok := stmt.Expression.(*ast.IfExpression) if !ok { t.Fatalf("stmt.Expression is not ast.IfExpression, got=%T", stmt.Expression) } if !testInfixExpression(t, exp.Condition, "x", "<", "y") { return } if len(exp.Consequence.Statements) != 1 { t.Errorf("Consequences is not 1 statement, got=%d\n", len(exp.Consequence.Statements)) } consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("Statements[0] is not ast.Expression, got=%T", exp.Consequence.Statements[0]) } if !testIdentifier(t, consequence.Expression, "x") { return } if exp.Alternative != nil { t.Errorf("exp.Alternative.Statement was not nil, got=%+v", exp.Alternative) } } func TestIfElseExpression(t *testing.T) { input := `kama (x < y) { x } sivyo { y }` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Body does not contain %d statements. got=%d\n", 1, len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0]) } exp, ok := stmt.Expression.(*ast.IfExpression) if !ok { t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T", stmt.Expression) } if !testInfixExpression(t, exp.Condition, "x", "<", "y") { return } if len(exp.Consequence.Statements) != 1 { t.Errorf("consequence is not 1 statements. got=%d\n", len(exp.Consequence.Statements)) } consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", exp.Consequence.Statements[0]) } if !testIdentifier(t, consequence.Expression, "x") { return } if len(exp.Alternative.Statements) != 1 { t.Errorf("exp.Alternative.Statements does not contain 1 statements. got=%d\n", len(exp.Alternative.Statements)) } alternative, ok := exp.Alternative.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", exp.Alternative.Statements[0]) } if !testIdentifier(t, alternative.Expression, "y") { return } } func TestFunctionLiteralParsing(t *testing.T) { input := `unda(x, y) {x + y}` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Body does not contain %d statements, got=%d\n", 1, len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0]) } function, ok := stmt.Expression.(*ast.FunctionLiteral) if !ok { t.Fatalf("stmt.Expression is not ast.FunctionLiteral, got=%T", stmt.Expression) } if len(function.Parameters) != 2 { t.Fatalf("function literal parameters wrong, want 2, got=%d\n", len(function.Parameters)) } testLiteralExpression(t, function.Parameters[0], "x") testLiteralExpression(t, function.Parameters[1], "y") if len(function.Body.Statements) != 1 { t.Fatalf("function.Body.Statements has not 1 statement, got=%d\n", len(function.Body.Statements)) } bodyStmt, ok := function.Body.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("function body stmt is not ast.ExpressionStatement, got=%T", function.Body.Statements[0]) } testInfixExpression(t, bodyStmt.Expression, "x", "+", "y") } func TestFunctionParameterParsing(t *testing.T) { tests := []struct { input string expectedParams []string }{ {input: "unda() {};", expectedParams: []string{}}, {input: "unda(x) {};", expectedParams: []string{"x"}}, {input: "unda(x, y, z) {};", expectedParams: []string{"x", "y", "z"}}, } for _, tt := range tests { l := lexer.New(tt.input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) function := stmt.Expression.(*ast.FunctionLiteral) if len(function.Parameters) != len(tt.expectedParams) { t.Errorf("length parameters wrong,want %d, got=%d\n", len(tt.expectedParams), len(function.Parameters)) } for i, ident := range tt.expectedParams { testLiteralExpression(t, function.Parameters[i], ident) } } } func TestCallExpressionParsing(t *testing.T) { input := "jumlisha(1, 2 * 3, 4 + 5);" l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Statements does not have 1 statements, got=%d\n", len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("stmt is not ast.ExpressionStatement, got=%T", program.Statements[0]) } exp, ok := stmt.Expression.(*ast.CallExpression) if !ok { t.Fatalf("stmt.Expression is not ast.CallExpression, got=%T", stmt.Expression) } if !testIdentifier(t, exp.Function, "jumlisha") { return } if len(exp.Arguments) != 3 { t.Fatalf("wrong length of arguments, got=%d", len(exp.Arguments)) } testLiteralExpression(t, exp.Arguments[0], 1) testInfixExpression(t, exp.Arguments[1], 2, "*", 3) testInfixExpression(t, exp.Arguments[2], 4, "+", 5) } func TestStringLiteralExpression(t *testing.T) { input := `"habari yako"` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) literal, ok := stmt.Expression.(*ast.StringLiteral) if !ok { t.Fatalf("exp not *ast.StringLiteral, got=%T", stmt.Expression) } if literal.Value != "habari yako" { t.Errorf("literal.Value not %q, got=%q", "habari yako", literal.Value) } } func TestParsingArrayLiterals(t *testing.T) { input := "[1,2*2,3+3]" l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) array, ok := stmt.Expression.(*ast.ArrayLiteral) if !ok { t.Fatalf("Expression not ast.ArrayLiteral, got=%T", len(array.Elements)) } testIntegerLiteral(t, array.Elements[0], 1) testInfixExpression(t, array.Elements[1], 2, "*", 2) testInfixExpression(t, array.Elements[2], 3, "+", 3) } func TestParsingIndexExpressions(t *testing.T) { input := "myArray[1+1]" l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) indexExp, ok := stmt.Expression.(*ast.IndexExpression) if !ok { t.Fatalf("Expression not *ast.IndexExpression, got=%T", stmt.Expression) } if !testIdentifier(t, indexExp.Left, "myArray") { return } if !testInfixExpression(t, indexExp.Index, 1, "+", 1) { return } } func TestParsingDictLiteralsStringKeys(t *testing.T) { input := `{"one": 1, "two": 2, "three": 3}` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) dict, ok := stmt.Expression.(*ast.DictLiteral) if !ok { t.Fatalf("Expression is not a Dict, got=%T", stmt.Expression) } if len(dict.Pairs) != 3 { t.Errorf("dict.Pairs wrong, got=%d", len(dict.Pairs)) } expected := map[string]int64{ "one": 1, "two": 2, "three": 3, } for key, value := range dict.Pairs { literal, ok := key.(*ast.StringLiteral) if !ok { t.Errorf("Key is not a string, got=%T", key) } expectedValue := expected[literal.String()] testIntegerLiteral(t, value, expectedValue) } } func TestParsingDictLiteralsIntegerKeys(t *testing.T) { input := `{1: 1, 2: 2, 3: 3}` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) dict, ok := stmt.Expression.(*ast.DictLiteral) if !ok { t.Fatalf("Expression is not a Dict, got=%T", stmt.Expression) } if len(dict.Pairs) != 3 { t.Errorf("dict.Pairs wrong, got=%d", len(dict.Pairs)) } expected := map[int64]int64{ 1: 1, 2: 2, 3: 3, } for key, value := range dict.Pairs { literal, ok := key.(*ast.IntegerLiteral) if !ok { t.Errorf("Key is not a string, got=%T", key) } expectedValue := expected[literal.Value] testIntegerLiteral(t, value, expectedValue) } } func TestParsingDictLiteralsBoolKeys(t *testing.T) { input := `{kweli: 1, sikweli: 2}` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) dict, ok := stmt.Expression.(*ast.DictLiteral) if !ok { t.Fatalf("Expression is not a Dict, got=%T", stmt.Expression) } if len(dict.Pairs) != 2 { t.Errorf("dict.Pairs wrong, got=%d", len(dict.Pairs)) } expected := map[bool]int64{ true: 1, false: 2, } for key, value := range dict.Pairs { literal, ok := key.(*ast.Boolean) if !ok { t.Errorf("Key is not a string, got=%T", key) } expectedValue := expected[literal.Value] testIntegerLiteral(t, value, expectedValue) } } func TestParsingDictLiteralWithExpressions(t *testing.T) { input := `{"one": 0+1, "two": 100-98, "three": 15/5}` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) dict, ok := stmt.Expression.(*ast.DictLiteral) if !ok { t.Fatalf("Expression is not a dict, got=%T", stmt.Expression) } if len(dict.Pairs) != 3 { t.Errorf("Dict has wrong length, got=%d", len(dict.Pairs)) } tests := map[string]func(ast.Expression){ "one": func(e ast.Expression) { testInfixExpression(t, e, 0, "+", 1) }, "two": func(e ast.Expression) { testInfixExpression(t, e, 100, "-", 98) }, "three": func(e ast.Expression) { testInfixExpression(t, e, 15, "/", 5) }, } for key, value := range dict.Pairs { literal, ok := key.(*ast.StringLiteral) if !ok { t.Errorf("key is not a string, got=%T", key) continue } testFunc, ok := tests[literal.String()] if !ok { t.Errorf("No test function for key %q found", literal.String()) continue } testFunc(value) } } func TestParsingEmptyDict(t *testing.T) { input := "{}" l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) stmt := program.Statements[0].(*ast.ExpressionStatement) dict, ok := stmt.Expression.(*ast.DictLiteral) if !ok { t.Fatalf("Expression not a dict, got=%T", stmt.Expression) } if len(dict.Pairs) != 0 { t.Errorf("Dict pairs has wrong length, got=%d", len(dict.Pairs)) } } func TestWhileLoop(t *testing.T) { input := `wakati ( x > y ) { fanya x = 2 }` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Body does not contain %d statements. got=%d", 1, len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not ast.Expression, got=%T", program.Statements[0]) } exp, ok := stmt.Expression.(*ast.WhileExpression) if !ok { t.Fatalf("stmt.Expression is not ast.WhileExpression. got=%T", stmt.Expression) } if !testInfixExpression(t, exp.Condition, "x", ">", "y") { return } if len(exp.Consequence.Statements) != 1 { t.Errorf("Consequence is not 1 statements. got=%d\n", len(exp.Consequence.Statements)) } consequence, ok := exp.Consequence.Statements[0].(*ast.LetStatement) if !ok { t.Fatalf("exp.Consequence.Statements[0] is not ast.ExpressionStatement. got=%T", exp.Consequence.Statements[0]) } if !testLetStatement(t, consequence, "x") { t.Fatalf("exp.Consequence is not LetStatement") } } func TestShorthandAssignment(t *testing.T) { input := []string{ "fanya x = 10; x *= 20;", "fanya x = 5; x += 4;", "fanya x = 7; x /= 2;", "fanya x = 8; x -= 1;", "fanya x = 5; x++;", "fanya x = 3; x--;", "fanya x = 40; fanya y = 13; x += y;"} for _, txt := range input { l := lexer.New(txt) p := New(l) _ = p.ParseProgram() checkParserErrors(t, p) } } func TestForExpression(t *testing.T) { input := `kwa i, v ktk j {andika(i)}` l := lexer.New(input) p := New(l) program := p.ParseProgram() checkParserErrors(t, p) if len(program.Statements) != 1 { t.Fatalf("program.Body does not contain %d statements. got=%d", 1, len(program.Statements)) } stmt, ok := program.Statements[0].(*ast.ExpressionStatement) if !ok { t.Fatalf("program.Statements[0] is not ast.Expression, got=%T", program.Statements[0]) } exp, ok := stmt.Expression.(*ast.ForIn) if !ok { t.Fatalf("stmt.Expression is not ast.ForIn. got=%T", stmt.Expression) } if exp.Key != "i" { t.Fatalf("Wrong Key Index, expected 'i' got %s", exp.Key) } if exp.Value != "v" { t.Fatalf("Wrong Value Index, expected 'v' got %s", exp.Value) } } func TestParsePostfix(t *testing.T) { input := []string{ "a = 5; a++;", "b = 5; b--;", } for _, txt := range input { l := lexer.New(txt) p := New(l) _ = p.ParseProgram() checkParserErrors(t, p) } } func TestParseDot(t *testing.T) { input := []string{ "a.b()", "5.5", } for _, txt := range input { l := lexer.New(txt) p := New(l) _ = p.ParseProgram() checkParserErrors(t, p) } } func TestParseSwitch(t *testing.T) { input := ` badili (a) { ikiwa 2 { andika(2) } ikiwa 3 { andika(3) } kawaida { andika(0) } } ` l := lexer.New(input) p := New(l) _ = p.ParseProgram() checkParserErrors(t, p) } func TestParseImport(t *testing.T) { input := ` tumia muda muda.hasahivi() ` l := lexer.New(input) p := New(l) _ = p.ParseProgram() checkParserErrors(t, p) } ================================================ FILE: parser/statements.go ================================================ package parser import ( "fmt" "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseStatement() ast.Statement { switch p.curToken.Type { case token.LET: return p.parseLetStatement() case token.RETURN: return p.parseReturnStatement() case token.BREAK: return p.parseBreak() case token.CONTINUE: return p.parseContinue() default: return p.parseExpressionStatement() } } func (p *Parser) parseLetStatement() *ast.LetStatement { stmt := &ast.LetStatement{Token: p.curToken} if !p.expectPeek(token.IDENT) { return nil } stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} if !p.expectPeek(token.ASSIGN) { return nil } p.nextToken() stmt.Value = p.parseExpression(LOWEST) if p.peekTokenIs(token.SEMICOLON) { p.nextToken() } return stmt } func (p *Parser) parseReturnStatement() *ast.ReturnStatement { stmt := &ast.ReturnStatement{Token: p.curToken} p.nextToken() stmt.ReturnValue = p.parseExpression(LOWEST) if p.peekTokenIs(token.SEMICOLON) { p.nextToken() } return stmt } func (p *Parser) parseBlockStatement() *ast.BlockStatement { block := &ast.BlockStatement{Token: p.curToken} block.Statements = []ast.Statement{} p.nextToken() for !p.curTokenIs(token.RBRACE) { if p.curTokenIs(token.EOF) { msg := fmt.Sprintf("Mstari %d: Hukufunga Mabano '}'", p.curToken.Line) p.errors = append(p.errors, msg) return nil } stmt := p.parseStatement() block.Statements = append(block.Statements, stmt) p.nextToken() } return block } ================================================ FILE: parser/string.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" ) func (p *Parser) parseStringLiteral() ast.Expression { return &ast.StringLiteral{Token: p.curToken, Value: p.curToken.Literal} } ================================================ FILE: parser/switch.go ================================================ package parser import ( "fmt" "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseSwitchStatement() ast.Expression { expression := &ast.SwitchExpression{Token: p.curToken} if !p.expectPeek(token.LPAREN) { return nil } p.nextToken() expression.Value = p.parseExpression(LOWEST) if expression.Value == nil { return nil } if !p.expectPeek(token.RPAREN) { return nil } if !p.expectPeek(token.LBRACE) { return nil } p.nextToken() for !p.curTokenIs(token.RBRACE) { if p.curTokenIs(token.EOF) { msg := fmt.Sprintf("Mstari %d: Haukufunga ENDAPO (SWITCH)", p.curToken.Line) p.errors = append(p.errors, msg) return nil } tmp := &ast.CaseExpression{Token: p.curToken} if p.curTokenIs(token.DEFAULT) { tmp.Default = true } else if p.curTokenIs(token.CASE) { p.nextToken() if p.curTokenIs(token.DEFAULT) { tmp.Default = true } else { tmp.Expr = append(tmp.Expr, p.parseExpression(LOWEST)) for p.peekTokenIs(token.COMMA) { p.nextToken() p.nextToken() tmp.Expr = append(tmp.Expr, p.parseExpression(LOWEST)) } } } else { msg := fmt.Sprintf("Mstari %d: Tulitegemea Kauli IKIWA (CASE) au KAWAIDA (DEFAULT) lakini tumepewa: %s", p.curToken.Line, p.curToken.Type) p.errors = append(p.errors, msg) return nil } if !p.expectPeek(token.LBRACE) { return nil } tmp.Block = p.parseBlockStatement() p.nextToken() expression.Choices = append(expression.Choices, tmp) } count := 0 for _, c := range expression.Choices { if c.Default { count++ } } if count > 1 { msg := fmt.Sprintf("Kauli ENDAPO (SWITCH) hua na kauli 'KAWAIDA' (DEFAULT) moja tu! Wewe umeweka %d", count) p.errors = append(p.errors, msg) return nil } return expression } ================================================ FILE: parser/while.go ================================================ package parser import ( "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) func (p *Parser) parseWhileExpression() ast.Expression { expression := &ast.WhileExpression{Token: p.curToken} if !p.expectPeek(token.LPAREN) { return nil } p.nextToken() expression.Condition = p.parseExpression(LOWEST) if !p.expectPeek(token.RPAREN) { return nil } if !p.expectPeek(token.LBRACE) { return nil } expression.Consequence = p.parseBlockStatement() return expression } ================================================ FILE: repl/docs/en/README.md ================================================ # NURU PROGRAMMING LANGUAGE DOCUMENTATION This documentation is intended for people with some experience in programming. It describes the syntax, types and how to perform various operations using the language. ## Table Of Contents - [Arrays in Nuru](arrays.md#arrays-in-nuru) - [Creating Arrays](arrays.md#creating-arrays) - [Accessing and Modifying Array Elements](arrays.md#accessing-and-modifying-array-elements) - [Concatenating Arrays](arrays.md#concatenating-arrays) - [Checking for Array Membership](arrays.md#checking-for-array-membership) - [Looping Over Arrays](arrays.md#looping-over-arrays) - [Array Methods](arrays.md#array-methods) - [idadi()](arrays.md#idadi()) - [sukuma()](arrays.md#sukuma()) - [yamwisho()](arrays.md#yamwisho()) - [Built-in Functions in Nuru](builtins.md#built-in-functions-in-nuru) - [The andika() Function](builtins.md#the-andika()-function) - [The jaza() Function](builtins.md#the-jaza()-function) - [The aina() Function](builtins.md#the-aina()-function) - [The fungua() Function](builtins.md#the-fungua()-function) - [Comments in Nuru](comments.md#comments-in-nuru) - [Single-Line Comments](comments.md#single-line-comments) - [Multi-Line Comments](comments.md#multi-line-comments) - [Conditional Statements in Nuru](ifStatements.md#conditional-statements-in-nuru) - [If Statement (Kama)](ifStatements.md#if-statement-(kama)) - [Else If and Else Blocks (Au Kama and Sivyo)](ifStatements.md#else-if-and-else-blocks-(au-kama-and-sivyo)) - [Dictionaries in Nuru](dictionaries.md#dictionaries-in-nuru) - [Creating Dictionaries](dictionaries.md#creating-dictionaries) - [Accessing Elements](dictionaries.md#accessing-elements) - [Updating Elements](dictionaries.md#updating-elements) - [Adding New Elements](dictionaries.md#adding-new-elements) - [Concatenating Dictionaries](dictionaries.md#concatenating-dictionaries) - [Checking If a Key Exists in a Dictionary](dictionaries.md#checking-if-a-key-exists-in-a-dictionary) - [Looping Over a Dictionary](dictionaries.md#looping-over-a-dictionary) - [Files in Nuru](files.md#files-in-nuru) - [Opening a File](files.md#opening-a-file) - [Reading a File](files.md#reading-a-file) - [For Loops in Nuru](for.md#for-loops-in-nuru) - [Basic Syntax](for.md#basic-syntax) - [Iterating Over Key-Value Pairs](for.md#iterating-over-key-value-pairs) - [Dictionaries](for.md#dictionaries) - [Strings](for.md#strings) - [Lists](for.md#lists) - [Break (Vunja) and Continue (Endelea)](for.md#break-(vunja)-and-continue-(endelea)) - [Break (Vunja)](for.md#break-(vunja)) - [Continue (Endelea)](for.md#continue-(endelea)) - [Functions in Nuru](function.md#functions-in-nuru) - [Basic Syntax](function.md#basic-syntax) - [Parameters](function.md#parameters) - [Default Parameters](function.md#default-parameters) - [Return (rudisha)](function.md#return-(rudisha)) - [Recursion](function.md#recursion) - [Closures](function.md#closures) - [HTTP with Nuru](net.md#http-with-nuru) - [Importing](net.md#importing) - [Methods](net.md#methods) - [peruzi()](net.md#peruzi()) - [tuma()](net.md#tuma()) - [Identifiers in Nuru](identifiers.md#identifiers-in-nuru) - [Syntax Rules](identifiers.md#syntax-rules) - [Best Practices](identifiers.md#best-practices) - [INTEGERS (NAMBA) AND FLOATS (DESIMALI)](numbers.md#integers-(namba)-and-floats-(desimali)) - [PRECEDENCE](numbers.md#precedence) - [UNARY INCREMENTS](numbers.md#unary-increments) - [SHORTHAND ASSIGNMENT](numbers.md#shorthand-assignment) - [NEGATIVE NUMBERS](numbers.md#negative-numbers) - [JSON in Nuru](json.md#json-in-nuru) - [Import JSONI](json.md#import-jsoni) - [Decoding JSON with dikodi()](json.md#decoding-json-with-dikodi()) - [Encoding JSON with enkodi()](json.md#encoding-json-with-enkodi()) - [Hisabati in nuru](hisabati.md#module-hisabati) - [Import Hisabati](hisabati.md#usage) - [In-built Constants](hisabati.md#1-constants) - [In-built Methods](hisabati.md#2-methods) - [KEYWORDS](keywords.md#keywords) - [Reserved Keywords](keywords.md#reserved-keywords) - [BuiltIns](keywords.md#builtins) - [Null (Tupu) in Nuru](null.md#null-(tupu)-in-nuru) - [Definition](null.md#definition) - [Evaluation](null.md#evaluation) - [NURU PROGRAMMING LANGUAGE DOCUMENTATION](README.md#nuru-programming-language-documentation) - [Table Of Contents](README.md#table-of-contents) - [OPERATORS](operators.md#operators) - [ASSIGNMENT](operators.md#assignment) - [ARITHMETIC OPERATORS](operators.md#arithmetic-operators) - [COMPARISON OPERATORS](operators.md#comparison-operators) - [MEMBER OPERATOR](operators.md#member-operator) - [LOGIC OPERATORS](operators.md#logic-operators) - [PRECEDENCE OF OPERATORS](operators.md#precedence-of-operators) - [Strings in Nuru](strings.md#strings-in-nuru) - [Basic Syntax](strings.md#basic-syntax) - [Concatenating Strings](strings.md#concatenating-strings) - [Looping over a String](strings.md#looping-over-a-string) - [Comparing Strings](strings.md#comparing-strings) - [String Methods](strings.md#string-methods) - [idadi()](strings.md#idadi()) - [herufikubwa()](strings.md#herufikubwa()) - [herufindogo](strings.md#herufindogo) - [gawa](strings.md#gawa) - [Switch Statements in Nuru](switch.md#switch-statements-in-nuru) - [Basic Syntax](switch.md#basic-syntax) - [Multiple Values in a Case](switch.md#multiple-values-in-a-case) - [Default Case (kawaida)](switch.md#default-case-(kawaida)) - [Time in Nuru](time.md#time-in-nuru) - [Importing Time](time.md#importing-time) - [Time Methods](time.md#time-methods) - [hasahivi()](time.md#hasahivi()) - [tangu()](time.md#tangu()) - [lala()](time.md#lala()) - [ongeza()](time.md#ongeza()) - [WHILE (WAKATI)](while.md#while-(wakati)) - [Basic Syntax](while.md#basic-syntax) - [Break (vunja) and Continue (endelea)](while.md#break-(vunja)-and-continue-(endelea)) - [Break (Vunja)](while.md#break-(vunja)) - [Continue (Endelea)](while.md#continue-(endelea)) - [Working with Booleans in Nuru](bool.md#working-with-booleans-in-nuru) - [Evaluating Boolean Expressions](bool.md#evaluating-boolean-expressions) - [Evaluating Simple Expressions](bool.md#evaluating-simple-expressions) - [Evaluating Complex Expressions](bool.md#evaluating-complex-expressions) - [Boolean Operators](bool.md#boolean-operators) - [The && Operator](bool.md#the-&&-operator) - [The || Operator](bool.md#the-||-operator) - [The ! Operator](bool.md#the-!-operator) - [Working with Boolean Values in Loops](bool.md#working-with-boolean-values-in-loops) ================================================ FILE: repl/docs/en/arrays.md ================================================ # Arrays in Nuru Arrays in Nuru are versatile data structures that can hold multiple items, including different types such as numbers, strings, booleans, functions, and null values. This page covers various aspects of arrays, including how to create, manipulate, and iterate over them using Nuru's built-in keywords and methods. ## Creating Arrays To create an array, use square brackets [] and separate items with commas: ```s orodha = [1, "pili", kweli] ``` ## Accessing and Modifying Array Elements Arrays in Nuru are zero-indexed. To access an element, use the element's index in square brackets: ```s namba = [10, 20, 30] jina = namba[1] // jina is 20 ``` You can reassign an element in an array using its index: ```s namba[1] = 25 ``` ## Concatenating Arrays To concatenate two or more arrays, use the + operator: ```s a = [1, 2, 3] b = [4, 5, 6] c = a + b // c is now [1, 2, 3, 4, 5, 6] ``` ## Checking for Array Membership Use the `ktk` keyword to check if an item exists in an array: ```s namba = [10, 20, 30] andika(20 ktk namba) // will print kweli ``` ## Looping Over Arrays You can use the kwa and ktk keywords to loop over array elements. To loop over just the values, use the following syntax: ``` namba = [1, 2, 3, 4, 5] kwa thamani ktk namba { andika(thamani) } ``` To loop over both index and value pairs, use this syntax: ```s majina = ["Juma", "Asha", "Haruna"] kwa idx, jina ktk majina { andika(idx, "-", jina) } ``` ## Array Methods Arrays in Nuru have several built-in methods: ### idadi() idadi() returns the length of an array: ```s a = [1, 2, 3] urefu = a.idadi() andika(urefu) // will print 3 ``` ### sukuma() sukuma() adds one or more items to the end of an array: ```s a = [1, 2, 3] a.sukuma("s", "g") andika(a) // will print [1, 2, 3, "s", "g"] ``` ### yamwisho() yamwisho() returns the last item in an array, or tupu if the array is empty: ```s a = [1, 2, 3] mwisho = a.yamwisho() andika(mwisho) // will print 3 b = [] mwisho = b.yamwisho() andika(mwisho) // will print tupu ``` ### map() map() goes through every element in the array and applies the passed function to each element. It will then return a new array with the updated elements: ```s a = [1, 2, 3] b = a.map(unda(x){rudisha x*2}) andika(b) // [2, 4, 6] ``` ### chuja() chuja() will go through every single element of an array and checks if that element returns true or false when passed into a function. It will return a new array with elements that returned true: ```s a = [1, 2, 3, 4] b = a.chuja(unda(x){ kama (x % 2 == 0) {rudisha kweli} rudisha sikweli }) andika(b) // [2, 4] ``` With this information, you can now effectively work with arrays in Nuru, making it easy to manipulate collections of data in your programs. ================================================ FILE: repl/docs/en/bool.md ================================================ # Working with Booleans in Nuru Boolean objects in Nuru are truthy, meaning that any value is true, except tupu and sikweli. They are used to evaluate expressions that return true or false values. ## Evaluating Boolean Expressions ### Evaluating Simple Expressions In Nuru, you can evaluate simple expressions that return a boolean value: ```s andika(1 > 2) // Output: `sikweli` andika(1 + 3 < 10) // Output: `kweli` ``` ### Evaluating Complex Expressions In Nuru, you can use boolean operators to evaluate complex expressions: ```s a = 5 b = 10 c = 15 result = (a < b) && (b < c) kama (result) { andika("Both conditions are true") } sivyo { andika("At least one condition is false") } // Output: "Both conditions are true" ``` Here, we create three variables a, b, and c. We then evaluate the expression (a < b) && (b < c). Since both conditions are true, the output will be "Both conditions are true". ## Boolean Operators Nuru has several boolean operators that you can use to evaluate expressions: ### The && Operator The && operator evaluates to true only if both operands are true. Here's an example: ```s andika(kweli && kweli) // Output: `kweli` andika(kweli && sikweli) // Output: `sikweli` ``` ### The || Operator The || operator evaluates to true if at least one of the operands is true. Here's an example: ```s andika(kweli || sikweli) // Output: `kweli` andika(sikweli || sikweli) // Output: `sikweli` ``` ### The ! Operator The ! operator negates the value of the operand. Here's an example: ```s andika(!kweli) // Output: `sikweli` andika(!sikweli) // Output: `kweli` ``` ## Working with Boolean Values in Loops In Nuru, you can use boolean expressions in loops to control their behavior. Here's an example: ```s namba = [1, 2, 3, 4, 5] kwa thamani ktk namba { kama (thamani % 2 == 0) { andika(thamani, "is even") } sivyo { andika(thamani, "is odd") } } // Output: // 1 is odd // 2 is even // 3 is odd // 4 is even // 5 is odd ``` Here, we create an array namba with the values 1 through 5. We then loop over each value in the array and use the % operator to determine if it is even or odd. The output will be "is even" for even numbers and "is odd" for odd numbers. Boolean objects in Nuru can be used to evaluate expressions that return true or false values. You can use boolean operators to evaluate complex expressions and control the behavior of loops. Understanding how to work with boolean values is an essential skill for any Nuru programmer. ================================================ FILE: repl/docs/en/builtins.md ================================================ # Built-in Functions in Nuru Nuru has several built-in functions that perform specific tasks. ## The andika() Function The andika() function is used to print out messages to the console. It can take zero or more arguments, and the arguments will be printed out with a space in between them. Additionally, andika() supports basic formatting such as /n for a new line, /t for a tab space, and \\ for a backslash. Here's an example: ```s andika(1, 2, 3) // Output: "1 2 3" ``` ## The jaza() Function The jaza() function is used to get input from the user. It can take zero or one argument, which is a string that will be used as a prompt for the user. Here's an example: ```s fanya salamu = unda() { fanya jina = jaza("Unaitwa nani? ") andika("Mambo vipi", jina) } salamu() ``` In this example, we define a function `salamu()` that prompts the user to enter their name using the `jaza()` function. We then use the `andika()` function to print out a message that includes the user's name. ## The aina() Function The `aina()` function is used to determine the type of an object. It accepts one argument, and the return value will be a string indicating the type of the object. Here's an example: ```s aina(2) // Output: "NAMBA" aina("Nuru") // Output: "NENO" ``` ## The fungua() Function The `fungua()` function is used to open a file. It accepts one argument, which is the path to the file that you want to open. Here's an example: ```s faili = fungua("data.txt") ``` In this example, we use the `fungua()` function to open a file named "data.txt". The variable faili will contain a reference to the opened file. ================================================ FILE: repl/docs/en/comments.md ================================================ # Comments in Nuru In Nuru, you can write comments to provide explanations and documentation for your code. Comments are lines of text that are ignored by the Nuru interpreter, so they will not affect the behavior of your program. There are two types of comments in Nuru: single-line comments and multi-line comments. ## Single-Line Comments Single-line comments are used to provide brief explanations or documentation for a single line of code. To write a single-line comment in Nuru, use two forward slashes (//) followed by your comment text. Here's an example: ```s // This line will be ignored by the Nuru interpreter ``` In this example, the comment text "This line will be ignored by the Nuru interpreter" will be ignored by the interpreter, so it will not affect the behavior of the program. ## Multi-Line Comments Multi-line comments are used to provide more detailed explanations or documentation for multiple lines of code. To write a multi-line comment in Nuru, use a forward slash followed by an asterisk ( /* ) to start the comment, and an asterisk followed by a forward slash ( */ ) to end the comment. Here's an example: ```s /* These lines Will be ignored */ ``` In this example, all the lines between the /* and */ symbols will be ignored by the Nuru interpreter, so they will not affect the behavior of the program. By utilizing single-line and multi-line comments in Nuru, you can make your code more readable and easier to maintain for yourself and others who may need to work with your code in the future. ================================================ FILE: repl/docs/en/dictionaries.md ================================================ # Dictionaries in Nuru Dictionaries in Nuru, also known as "kamusi," are powerful and flexible data structures that store key-value pairs. This page provides a comprehensive overview of dictionaries in Nuru, including how to create, access, modify, and iterate over them. ## Creating Dictionaries Dictionaries are enclosed in curly braces {} and consist of keys and values separated by colons. Here's an example of defining a dictionary: ```s orodha = {"jina": "Juma", "umri": 25} ``` Keys can be strings, integers, floats, or booleans, while values can be any data type, including strings, integers, floats, booleans, null, or functions: ```s k = { "jina": "Juma", "umri": 25, kweli: "kweli", "salimu": unda(x) { andika("habari", x) }, "sina value": tupu } ``` ## Accessing Elements Access individual elements in a dictionary using their keys: ```s andika(k[kweli]) // kweli andika(k["salimu"]("Juma")) // habari Juma ``` ## Updating Elements Update the value of an element by assigning a new value to its key: ```s k['umri'] = 30 andika(k['umri']) // 30 ``` ## Adding New Elements Add a new key-value pair to a dictionary by assigning a value to a non-existent key: ```s k["lugha"] = "Kiswahili" andika(k["lugha"]) // Kiswahili ``` ## Concatenating Dictionaries Combine two dictionaries using the + operator: ```s matunda = {"a": "apple", "b": "banana"} mboga = {"c": "carrot", "d": "daikon"} vyakula = matunda + mboga andika(vyakula) // {"a": "apple", "b": "banana", "c": "carrot", "d": "daikon"} ``` ## Checking If a Key Exists in a Dictionary Use the ktk keyword to check if a key exists in a dictionary: ```s "umri" ktk k // kweli "urefu" ktk k // sikweli ``` ## Looping Over a Dictionary Loop over a dictionary to access its keys and values: ```s hobby = {"a": "asili", "b": "baiskeli", "c": "chakula"} kwa i, v ktk hobby { andika(i, "=>", v) } ``` Output ```s a => asili b => baiskeli c => chakula ``` Loop over just the values: ```s kwa v ktk hobby { andika(v) } ``` Output ```s asili baiskeli chakula ``` With this knowledge, you can now effectively use dictionaries in Nuru to store and manage key-value pairs, offering a flexible way to organize and access data in your programs. ================================================ FILE: repl/docs/en/files.md ================================================ # Files in Nuru Nuru's ability to deal with files is primitive, and as for now it only allows you to read contents of a file. ## Opening a File You open a file with the `fungua` keyword. This will return an object of type `FAILI`: ``` fileYangu = fungua("file.txt") aina(fileYangu) // FAILI ``` ## Reading a File Once you have a file object you can read its contents with the `soma()` method. This will return the contents of the file as a string: ``` fileYangu = fungua("file.txt") fileYangu.soma() ``` ================================================ FILE: repl/docs/en/for.md ================================================ # For Loops in Nuru For loops are a fundamental control structure in Nuru, used for iterating over iterable objects such as strings, arrays, and dictionaries. This page covers the syntax and usage of for loops in Nuru, including key-value pair iteration, and the use of break and continue statements. ## Basic Syntax To create a for loop, use the kwa keyword followed by a temporary identifier (such as i or v) and the iterable object. Enclose the loop body in curly braces {}. Here's an example with a string: ```s jina = "lugano" kwa i ktk jina { andika(i) } ``` Output: ```s l u g a n o ``` ## Iterating Over Key-Value Pairs ### Dictionaries Nuru allows you to iterate over both the value or the key-value pair of an iterable. To iterate over just the values, use one temporary identifier: ```s kamusi = {"a": "andaa", "b": "baba"} kwa v ktk kamusi { andika(v) } ``` Output: ```s andaa baba ``` To iterate over both the keys and the values, use two temporary identifiers: ```s kwa k, v ktk kamusi { andika(k + " ni " + v) } ``` Output: ```s a ni andaa b ni baba ``` ### Strings To iterate over just the values in a string, use one temporary identifier: ```s kwa v ktk "mojo" { andika(v) } ``` Output: ```s m o j o ``` To iterate over both the keys and the values in a string, use two temporary identifiers: ```s kwa i, v ktk "mojo" { andika(i, "->", v) } ``` Output: ```s 0 -> m 1 -> o 2 -> j 3 -> o ``` ### Lists To iterate over just the values in a list, use one temporary identifier: ```s majina = ["juma", "asha", "haruna"] kwa v ktk majina { andika(v) } ``` Output: ```s juma asha haruna ``` To iterate over both the keys and the values in a list, use two temporary identifiers: ```s kwa i, v ktk majina { andika(i, "-", v) } ``` Output: ```s 0 - juma 1 - asha 2 - haruna ``` ## Break (Vunja) and Continue (Endelea) ### Break (Vunja) Use the vunja keyword to terminate a loop: ```s kwa i, v ktk "mojo" { kama (i == 2) { andika("nimevunja") vunja } andika(v) } ``` Output: ```s m o nimevunja ``` ### Continue (Endelea) Use the endelea keyword to skip a specific iteration: ```s kwa i, v ktk "mojo" { kama (i == 2) { andika("nimeruka") endelea } andika(v) } ``` Output: ```s m o nimeruka o ``` ================================================ FILE: repl/docs/en/function.md ================================================ # Functions in Nuru Functions are a fundamental part of Nuru programming, allowing you to define reusable blocks of code. This page covers the syntax and usage of functions in Nuru, including parameters, default parameters, return statements, recursion, and closures. ## Basic Syntax A function block starts with the unda keyword, followed by parameters enclosed in parentheses () and the body enclosed in curly braces {}. Functions must be assigned to a variable: ```s jum = unda(x, y) { rudisha x + y } jum(2, 3) // 5 ``` ## Parameters Functions can have zero or any number of arguments. Arguments can be of any type, even other functions: ```s salamu = unda() { andika("Habari yako") } salamu() salamu = unda(jina) { andika("Habari yako", jina) } salamu("asha") // Habari yako asha ``` ## Default Parameters Functions can be provided with default parameters: ```s salimu = unda(salamu="Habari") { andika(salamu) } salimu() // Habari salimu("Mambo") // Mambo ``` ## Return (rudisha) You can return values with the rudisha keyword. The rudisha keyword will terminate the block and return the value: ```s mfano = unda(x) { rudisha "nimerudi" andika(x) } mfano("x") // nimerudi ``` ## Recursion Nuru also supports recursion. Here's an example of a recursive Fibonacci function: ```s fib = unda(n) { kama (n <= 1) { rudisha n } sivyo { rudisha fib(n-1) + fib(n-2) } } andika(fib(10)) // 55 ``` The fib function calculates the nth Fibonacci number by recursively calling itself with n-1 and n-2 as arguments until n is less than or equal to 1. ## Closures Closures are anonymous functions that can capture and store references to variables from their surrounding context. In Nuru, you can create closures using the unda keyword without assigning them to a variable. Here's an example: ```s fanya jum = unda(x) { rudisha unda(y) { rudisha x + y } } fanya jum_x = jum(5) andika(jum_x(3)) // 8 ``` In the example above, the jum function returns another function that takes a single parameter y. The returned function has access to the x variable from its surrounding context. Now that you understand the basics of functions in Nuru, including recursion and closures, you can create reusable blocks of code to simplify your programs and improve code organization. ================================================ FILE: repl/docs/en/hisabati.md ================================================ # Module Hisabati Module Hisabati is a inbuilt math module by [VictorKariuki](https://github.com/VictorKariuki). This in-built module provides various mathematical functions and constants. It includes methods for `trigonometric functions`, `logarithmic functions`, `array operations`, and `utility functions`. ## Usage To use the `hisabati` in-built module follow the steps below: 1. You directly import the `hisabati` in-built module and any required in-built modules in your Nuru code using the `tumia` keyword. ```nuru tumia hisabati ``` 2. Calling the in-built module methods: ```nuru andika(hisabati.e()) ``` ## Yaliyomo This in-built module covers a wide range of mathematical operations, including : - `Basic Mathematical Functions:` - `Hyperbolic` & `Trigonometric Functions` - `Exponential` & `Logarithmic Functions` - `Rounding` & `Comparison Functions` Here is an in-depth classification of the methods: 1. Trigonometric Functions: - `cos(n)` - `sin(n)` - `tan(n)` - `acos(n)` - `asin(n)` - `atan(n)` - `hypot(numbers)` 2. Hyperbolic Functions: - `cosh(n)` - `sinh(n)` - `tanh(n)` - `acosh(n)` - `asinh(n)` - `atanh(n)` 3. Exponential and Logarithmic Functions: - `exp(n)` - `expm1(n)` - `log(n)` - `log2(n)` - `log10(n)` - `log1p(n)` 4. Basic Mathematical Functions: - `abs(n)` - `sqrt(n)` - `cbrt(n)` - `root(x, n)` - `factorial(n)` - `sign(n)` 5. Rounding and Comparison Functions: - `ceil(n)` - `floor(n)` - `round(n)` - `max(numbers)` - `min(numbers)` ### 1. Constants: - **PI**: Represents the mathematical constant `π`. - **e**: Represents `Euler's Number`. - **phi**: Represents the `Golden Ratio`. - **ln10**: Represents the `natural logarithm of 10`. - **ln2**: Represents the `natural logarithm of 2`. - **log10e**: Represents the `base 10 logarithms` of Euler's number `(e)`. - **log2e**: Represents the `base 2 logarithm` of Euler's number` (e)`. - **sqrt1_2**: Represents the `square root` of `1/2`. - **sqrt2**: Represents the `square root` of `2`. - **sqrt3**: Represents the `square root` of `3`. - **sqrt5**: Represents the `square root` of `5`. - **EPSILON**: Represents a small value `2.220446049250313e-16`. ### 2. Methods: 1. **abs(namba)** - Description: Calculates the absolute value of a number. - Example: `hisabati.abs(-42)` returns `42`. 2. **acos(n)** - Description: Calculates the arccosine of a number. - Example: `hisabati.acos(0.5)` returns `1.0471975511965979`. 3. **acosh(n)** - Description: Calculates the inverse hyperbolic cosine of a number. - Example: `hisabati.acosh(2.0)` returns `1.3169578969248166`. 4. **asin(n)** - Description: Calculates the arcsine of a number using the Taylor series. - Example: `hisabati.arcsin(0.5)` returns `0.5235987755982988`. 5. **asinh(n)** - Description: Calculates the inverse hyperbolic sine of a number. - Example: `hisabati.arsinh(2.0)` returns `1.4436354751788103`. 6. **atan(n)** - Description: Calculates the arctangent of a number using the Taylor series. - Example: `hisabati.atan(1.0)` returns `0.7853981633974483`. 7. **atan2(y, x)** - Description: Calculates the arctangent of the quotient of its arguments. - Example: `hisabati.atan2(1.0, 1.0)` returns `0.7853981633974483`. 8. **atanh(n)** - Description: Calculates the inverse hyperbolic tangent of a number. - Example: `hisabati.atanh(0.5)` returns `0.5493061443340549`. 9. **cbrt(n)** - Description: Calculates the cube root of a number. - Example: `hisabati.cbrt(8)` returns `2`. 10. **root(x, n)** - Description: Calculates the nth root of a number using the Newton-Raphson method. - Example: `hisabati.root(27, 3)` returns `3`. 11. **ceil(n)** - Description: Rounds up to the smallest integer greater than or equal to a given number. - Example: `hisabati.ceil(4.3)` returns `5`. 12. **cos(n)** - Description: Calculates the cosine of an angle in radians using the Taylor series. - Example: `hisabati.cos(0.0)` returns `1`. 13. **cosh(n)** - Description: Calculates the hyperbolic cosine of a number. - Example: `hisabati.cosh(0.0)` returns `1`. 14. **exp(n)** - Description: Calculates the value of Euler's number raised to the power of a given number. - Example: `hisabati.exp(2.0)` returns `7.38905609893065`. 15. **expm1(n)** - Description: Calculates Euler's number raised to the power of a number minus 1. - Example: `hisabati.expm1(1.0)` returns `1.718281828459045`. 16. **floor(n)** - Description: Rounds down to the largest integer less than or equal to a given number. - Example: `hisabati.floor(4.7)` returns `4`. 17. **hypot(values)** - Description: Calculates the square root of the sum of squares of the given values. - Example: `hisabati.hypot([3, 4])` returns `5`. 18. **log(n)** - Description: Calculates the natural logarithm of a number. - Example: `hisabati.log(1.0)` returns `0`. 19. **log10(n)** - Description: Calculates the base 10 logarithm of a number. - Example: `hisabati.log10(100.0)` returns `2`. 20. **log1p(n)** - Description: Calculates the natural logarithm of 1 plus the given number. - Example: `hisabati.log1p(1.0)` returns `0.6931471805599453`. 21. **log2(n)** - Description: Calculates the base 2 logarithm of a number. - Example: `hisabati.log2(8)` returns `3`. 22. **max(numbers)** - Description: Finds the maximum value in a list of numbers. - Example: `hisabati.max([4, 2, 9, 5])` returns `9`. 23. **min(numbers)** - Description: Finds the minimum value in a list of numbers. - Example: `hisabati.min([4, 2, 9, 5])` returns `2`. 24. **round(x, method)** - Description: Rounds a number to the nearest integer using the specified method. - Example: `hisabati.round(4.6)` returns `5`. 25. **sign(n)** - Description: Determines the sign of a number. - Example: `hisabati.sign(-5)` returns `-1`. 26. **sin(n)** - Description: Calculates the sine of an angle in radians using the Taylor series. - Example: `hisabati.sin(1.0)` returns `0.8414709848078965`. 27. **sinh(n)** - Description: Calculates the hyperbolic sine of a number. - Example: `hisabati.sinh(1.0)` returns `1.1752011936438014`. 28. **sqrt(n)** - Description: Calculates the square root of a number. - Example: `hisabati.sqrt(4)` returns `2`. 29. **tan(n)** - Description: Calculates the tangent of an angle in radians. - Example: `hisabati.tan(1.0)` returns `1.557407724654902`. 30. **tanh(n)** - Description: Calculates the hyperbolic tangent of a number. - Example: `hisabati.tanh(1.0)` returns `0.7615941559557649`. 31. **factorial(n)** - Description: Calculates the factorial of a number. - Example: `hisabati.factorial(5)` returns `120`. ### Contributing Contributions to the `module hisabati` are welcome. If you have any improvements or bug fixes, feel free to create a pull request. ### License This in-built module is available under the MIT License. See the [LICENSE](LICENSE) file for more information. ================================================ FILE: repl/docs/en/identifiers.md ================================================ # Identifiers in Nuru Identifiers are used to name variables, functions, and other elements in your Nuru code. This page covers the rules and best practices for creating identifiers in Nuru. ## Syntax Rules Identifiers can contain letters, numbers, and underscores. However, there are a few rules you must follow when creating identifiers: - Identifiers cannot start with a number. - Identifiers are case-sensitive. For example, myVar and myvar are considered distinct identifiers. Here are some examples of valid identifiers: ```s fanya birth_year = 2020 andika(birth_year) // 2020 fanya convert_c_to_p = "C to P" andika(convert_c_to_p) // "C to P" ``` In the examples above, birth_year and convert_c_to_p are both valid identifiers. ## Best Practices When choosing identifiers, it's important to follow best practices to ensure your code is clear and easy to understand: - Use descriptive names that clearly indicate the purpose or meaning of the variable or function. - Follow a consistent naming convention, such as camelCase (myVariableName) or snake_case (my_variable_name). - Avoid using single-letter variable names, except for commonly accepted cases like loop counters (i, j, k). By following these best practices when creating identifiers, you will make your Nuru code more readable and maintainable for yourself and others. ================================================ FILE: repl/docs/en/ifStatements.md ================================================ # Conditional Statements in Nuru Conditional statements in Nuru are used to perform different actions based on different conditions. The if/else statement is a fundamental control structure that allows you to execute code based on specific conditions. This page covers the basics of if/else statements in Nuru. ## If Statement (Kama) An if statement starts with the kama keyword, followed by a condition in parentheses (). If the condition is true, the code inside the curly braces {} will be executed. ```s kama (2 > 1) { andika(kweli) // kweli } ``` In this example, the condition 2 > 1 is true, so the andika(kweli) statement is executed, and the output is kweli. ## Else If and Else Blocks (Au Kama and Sivyo) You can use au kama to test multiple conditions and sivyo to specify a default block of code to be executed when none of the conditions are true. ```s fanya a = 10 kama (a > 100) { andika("a imezidi 100") } au kama (a < 10) { andika("a ndogo kuliko 10") } sivyo { andika("Thamani ya a ni", a) } // The output will be 'Thamani ya a ni 10' ``` In this example, the first condition a > 100 is false, and the second condition a < 10 is also false. Therefore, the code inside the sivyo block is executed, and the output is 'Thamani ya a ni 10'. By using if/else statements with the kama, au kama, and sivyo keywords, you can control the flow of your Nuru code based on different conditions. ================================================ FILE: repl/docs/en/json.md ================================================ # JSON in Nuru Nuru also makes it easy to deal with JSON. ## Import JSONI Use the following to import the json module: ``` tumia jsoni ``` ## Decoding JSON with dikodi() Use this to convert a string to a dictionary: ``` jsonString = '{ "error": false, "category": "Pun", "type": "single", "joke": "I was reading a great book about an immortal dog the other day. It was impossible to put down." }' // to make it a dict tumia jsoni k = jsoni.dikodi(jsonString) k["joke"] // I was reading a great book about an immortal dog the other day. It was impossible to put down. ``` ## Encoding JSON with enkodi() You can encode JSON with the `enkodi` method, this will turn a dictionary to a string: ``` tumia jsoni k = { "a": "apple", "b": "banana" } j = json.enkodi(k) ``` ================================================ FILE: repl/docs/en/keywords.md ================================================ # KEYWORDS Keywords in Nuru are reserved words that have special meanings and cannot be used as identifiers for variables, functions, or classes. This page covers the syntax and usage of keywords in Nuru, including reserved keywords and built-in functions. ## Reserved Keywords The table below lists the reserved keywords in Nuru. These words have specific meanings in the language and cannot be used as identifiers:
kweli sikweli unda fanya
kama au sivyo wakati
rudisha vunja endelea tupu
ktk kwa badili ikiwa
kawaida
## BuiltIns Nuru also provides several built-in functions that are reserved and cannot be used as identifiers. These functions offer essential functionalities for common tasks in the language:
andika aina jaza fungua
Understanding the keywords and built-in functions in Nuru is essential for writing clear, concise, and error-free code. By respecting the reserved words and their specific meanings, you can create more robust and maintainable programs in Nuru. ================================================ FILE: repl/docs/en/net.md ================================================ # HTTP with Nuru You can access the internet via http protocol using the `mtandao` module. ## Importing Import the module with: ``` tumia mtandao ``` ## Methods ### peruzi() Use this as GET method. It can either accept one positional argument which will be the URL: ``` tumia mtandao mtandao.peruzi("http://google.com") ``` Or you can use keyword arguments to pass in parameters and headers as shown below. Note that headers and parameters must be a dictionary: ``` tumia mtandao url = "http://mysite.com" headers = {"Authentication": "Bearer XXXX"} mtandao.peruzi(yuareli=url, vichwa=headers, mwili=params) ``` ### tuma() Use this as POST method. Use keyword arguments to pass in parameters and headers as shown below. Note that headers and parameters must be a dictionary: ``` tumia mtandao url = "http://mysite.com" headers = {"Authentication": "Bearer XXXX"} params = {"key": "Value"} mtandao.tuma(yuareli=url, vichwa=headers, mwili=params) ``` ================================================ FILE: repl/docs/en/null.md ================================================ # Null (Tupu) in Nuru The null data type in Nuru represents the absence of a value or the concept of "nothing" or "empty." This page covers the syntax and usage of the null data type in Nuru, including its definition and evaluation. ## Definition A null data type is a data type with no value, defined with the tupu keyword: ```s fanya a = tupu ``` ## Evaluation When evaluating a null data type in a conditional expression, it will evaluate to false: ```s kama (a) { andika("niko tupu") } sivyo { andika("nimevaa nguo") } // Output: nimevaa nguo ``` The null data type is useful in Nuru when you need to represent an uninitialized, missing, or undefined value in your programs. By understanding the null data type, you can create more robust and flexible code. ================================================ FILE: repl/docs/en/numbers.md ================================================ # INTEGERS (NAMBA) AND FLOATS (DESIMALI) Integers and floats are the basic numeric data types in Nuru, used for representing whole numbers and decimal numbers, respectively. This page covers the syntax and usage of integers and floats in Nuru, including precedence, unary increments, shorthand assignments, and negative numbers. ## PRECEDENCE Integers and floats behave as expected in mathematical operations, following the BODMAS rule: ```go 2 + 3 * 5 // 17 fanya a = 2.5 fanya b = 3/5 a + b // 2.8 ``` ## UNARY INCREMENTS You can perform unary increments (++ and --) on both floats and integers. These will add or subtract 1 from the current value. Note that the float or int have to be assigned to a variable for this operation to work. Here's an example: ```go fanya i = 2.4 i++ // 3.4 ``` ## SHORTHAND ASSIGNMENT Nuru supports shorthand assignments with +=, -=, /=, *=, and %=: You ```go fanya i = 2 i *= 3 // 6 i /= 2 // 3 i += 100 // 103 i -= 10 // 93 i %= 90 // 3 ``` ## NEGATIVE NUMBERS Negative numbers also behave as expected: ```go fanya i = -10 wakati (i < 0) { andika(i) i++ } ``` Output: ```s -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 ``` ================================================ FILE: repl/docs/en/operators.md ================================================ # OPERATORS Operators are the foundation of any programming language, allowing you to perform various operations on variables and values. This page covers the syntax and usage of operators in Nuru, including assignment, arithmetic, comparison, member, and logic operators. ## ASSIGNMENT Assuming `i` and `v` are predefined variables, Nuru supports the following assignment operators: - `i = v`: which is the regular assignment operator - `i += v`: which is the equivalent of `i = i + v` - `i -= v`: which is the equivalent of `i = i - v` - `i *= v`: which is the equivalent of `i = i * v` - `i /= v`: which is the equivalent of `i = i / v` - `i += v`: which is the equivalent of `i = i + v` For `strings`, `arrays` and `dictionaries`, the `+=` sign operator is permissible. Example: ``` list1 += list2 // this is equivalent to list1 = list1 + list2 ``` ## ARITHMETIC OPERATORS Nuru supports the following arithmetic operators: - `+`: Additon - `-`: Subtraction - `*`: Multiplication - `/`: Division - `%`: Modulo (ie the remainder of a division) - `**`: Exponential power (eg: `2**3 = 8`) ## COMPARISON OPERATORS Nuru supports the following comparison operators: - `==`: Equal to - `!=`: Not equal to - `>`: Greater than - `>=`: Greater than or equal to - `<`: Less than - `<=`: Less than or equal to ## MEMBER OPERATOR The member operator in Nuru is `ktk`. It will check if an object exists in another object: ```go fanya majina = ['juma', 'asha', 'haruna'] "haruna" ktk majina // kweli "halima" ktk majina // sikweli ``` ## LOGIC OPERATORS Nuru supports the following logic operators: - `&&`: Logical `AND`. It will evaluate to true if both are true, otherwise it will evaluate to false. - `||`: Logical `OR`. It will evaluate to false if both are false, otherwise it will evaluate to true. - `!`: Logical `NOT`. It will evaluate to the opposite of a given expression. ## PRECEDENCE OF OPERATORS Operators have the following precedence, starting from the highest priority to the lowest: - `()` : Items in paranthesis have the highest priority - `!`: Negation - `%`: Modulo - `**`: Exponential power - `/, *`: Division and Multiplication - `+, +=, -, -=`: Addition and Subtraction - `>, >=, <, <=`: Comparison operators - `==, !=`: Equal or Not Equal to - `=`: Assignment Operator - `ktk`: Member Operator - `&&, ||`: Logical AND and OR Understanding operators in Nuru allows you to create complex expressions, perform calculations, and make decisions based on the values of variables. ================================================ FILE: repl/docs/en/packages.md ================================================ # Packages in Nuru You can use third packages written in Nuru with the following conditions: - The package file MUST be in the same directory - The package file MUST end with `nr` - The package name and package file MUST have the same name (eg: if `pakeji hesabu` then the file name must be `hesabu.nr`) - The package must have the following structure: ``` // imports if any pakeji [name of package] { andaa = unda() { // the andaa function is mandatory even if its empty } [body of package] } ``` - The package must be initialized with the `andaa` keyword (see above). The `andaa` keyword is for initializing your package. This is also where you'd put your global variables. The global variables should be prefixed with `@.` Eg: `@.myGlobalVar`. A variable being globally available means that the variable can be accessed and manipulated by all other methods in the package. Below is an example Sarufi package: ``` // import modules tumia mtandao tumia jsoni // package body pakeji sarufi { // initialize function andaa = unda(file) { config = fungua(file) // read passwords from json file configString = config.soma() configDict = jsoni.dikodi(configString) // convert it to a dict clientID = configDict["client_id"] clientSecret = configDict["client_secret"] // fill in params params = {"client_id": clientID, "client_secret": clientSecret} // get response resp = mtandao.tuma(yuareli="https://api.sarufi.io/api/access_token", mwili=params) tokenDict = jsoni.dikodi(resp) // extract token and make it globally available @.token = tokenDict["access_token"] // make the "Bearer " globally available @.Auth = "Bearer " + @.token } // a method to get token tokenYangu = unda() { rudisha @.token } // a method to create new chatbots. // the data should be a dict tengenezaChatbot = unda(data) { majibu = mtandao.tuma(yuareli="https://api.sarufi.io/chatbot", vichwa={"Authorization": @.Auth}, mwili = data) rudisha majibu } // a method to get all available chatbots pataChatbotZote = unda() { majibu = mtandao.peruzi(yuareli="https://api.sarufi.io/chatbots", vichwa={"Authorization": @.Auth}) rudisha majibu } } ``` ================================================ FILE: repl/docs/en/range.md ================================================ ## Range Function (mfululizo) The `mfululizo` function generates a sequence of numbers. It can be used in loops or to create arrays of sequential numbers. ### Syntax ```go mfululizo(end) mfululizo(start, end) mfululizo(start, end, step) ``` ### Parameters - `end`: The upper limit of the sequence (exclusive). - `start` (optional): The starting value of the sequence. Default is 0. - `step` (optional): The increment between each number in the sequence. Default is 1. ### Return Value Returns an array of integers. ### Examples ```go // Generate numbers from 0 to 4 kwa i katika mfululizo(5) { andika(i) } // Output: 0 1 2 3 4 // Generate numbers from 1 to 9 kwa i katika mfululizo(1, 10) { andika(i) } // Output: 1 2 3 4 5 6 7 8 9 // Generate even numbers from 0 to 8 kwa i katika mfululizo(0, 10, 2) { andika(i) } // Output: 0 2 4 6 8 // Generate numbers in reverse order kwa i katika mfululizo(10, 0, -1) { andika(i) } // Output: 10 9 8 7 6 5 4 3 2 1 ``` ### Notes - The `end` value is exclusive, meaning the sequence will stop before reaching this value. - If a negative `step` is provided, `start` should be greater than `end`. - The `step` value cannot be zero. ================================================ FILE: repl/docs/en/strings.md ================================================ # Strings in Nuru Strings are a sequence of characters that can represent text in the Nuru programming language. This page covers the basics of strings, their manipulation, and some built-in methods. ## Basic Syntax Strings can be enclosed in either single quotes '' or double quotes "": ```s andika("mambo") // mambo fanya a = 'niaje' andika("mambo", a) // mambo niaje ``` ## Concatenating Strings Strings can be concatenated using the + operator: ```s fanya a = "habari" + " " + "yako" andika(a) // habari yako fanya b = "habari" b += " yako" // habari yako ``` You can also repeat a string n number of times using the * operator: ```s andika("mambo " * 4) // mambo mambo mambo mambo fanya a = "habari" a *= 4 // habarihabarihabarihabari ``` ## Looping over a String You can loop through a string using the kwa keyword: ```s fanya jina = "avicenna" kwa i ktk jina {andika(i)} ``` Output ```s a v i c e n n a ``` And for key-value pairs: ```s kwa i, v ktk jina { andika(i, "=>", v) } ``` Output ```s 0 => a 1 => v 2 => i 3 => c 4 => e 5 => n 6 => n 7 => a ``` ## Comparing Strings You can compare two strings using the == operator: ```s fanya a = "nuru" andika(a == "nuru") // kweli andika(a == "mambo") // sikweli ``` ## String Methods ### idadi() You can find the length of a string using the idadi method. It does not accept any parameters. ```s fanya a = "mambo" a.idadi() // 5 ``` ### herufikubwa() This method converts a string to uppercase. It does not accept any parameters. ```s fanya a = "nuru" a.herufikubwa() // NURU ``` ### herufindogo This method converts a string to lowercase. It does not accept any parameters. ```s fanya a = "NURU" a.herufindogo() // nuru ``` ### gawa The gawa method splits a string into an array based on a specified delimiter. If no argument is provided, it will split the string according to whitespace. Example without a parameter: ```s fanya a = "nuru mambo habari" fanya b = a.gawa() andika(b) // ["nuru", "mambo", "habari"] ``` Example with a parameter: ```s fanya a = "nuru,mambo,habari" fanya b = a.gawa(",") andika(b) // ["nuru", "mambo", "habari"] ``` By understanding strings and their manipulation in Nuru, you can effectively work with text data in your programs. ================================================ FILE: repl/docs/en/switch.md ================================================ # Switch Statements in Nuru Switch statements in Nuru allow you to execute different code blocks based on the value of a given expression. This page covers the basics of switch statements and their usage. ## Basic Syntax You initialize a switch statement with the badili keyword, the expression inside parentheses (), and all cases enclosed within curly braces {}. A case statement has the keyword ikiwa followed by a value to check. Multiple values can be in a single case separated by commas ,. The consequence to execute if a condition is fulfilled must be inside curly braces {}. Here's an example: ```s fanya a = 2 badili (a){ ikiwa 3 { andika("a ni tatu") } ikiwa 2 { andika ("a ni mbili") } } ``` ## Multiple Values in a Case Multiple possibilities can be assigned to a single case (ikiwa) statement: ```s badili (a) { ikiwa 1,2,3 { andika("a ni kati ya 1, 2 au 3") } ikiwa 4 { andika("a ni 4") } } ``` ## Default Case (kawaida) The default statement will be executed when no condition is satisfied. The default statement is represented by kawaida: ```s fanya z = 20 badili(z) { ikiwa 10 { andika("kumi") } ikiwa 30 { andika("thelathini") } kawaida { andika("ishirini") } } ``` By understanding switch statements in Nuru, you can create more efficient and organized code that can handle multiple conditions easily. ================================================ FILE: repl/docs/en/time.md ================================================ # Time in Nuru ## Importing Time To use Time in Nuru, you first have to import the `muda` module as follows: ```so tumia muda ``` --- ## Time Methods ### `hasahivi()` To get the current time, use `muda.hasahivi()`. It returns a `muda` object with the current time in the format `HH:mm:ss dd-MM-YYYY`. ```so tumia muda saivi = muda.hasahivi() ``` --- ### `leo()` To get today’s date in the format `dd-MM-YYYY`: ```so tumia muda leo = muda.leo() ``` --- ### `tangu(time)` Gets the total time elapsed **in seconds** from the given time to now. Accepts a `muda` object or string in `HH:mm:ss dd-MM-YYYY` format. ```so tumia muda muda_ulioyopita = muda.tangu("15:00:00 01-01-2024") ``` --- ### `lala(sekunde)` Pauses the program for the given number of seconds: ```so tumia muda muda.lala(5) // sleeps for 5 seconds ``` --- ### `baada_ya(sekunde)` Returns a `muda` object representing the time after the given number of seconds from now. ```so tumia muda baadaye = muda.baada_ya(60) // one minute from now ``` --- ### `tofauti(muda1, muda2)` Returns the difference between two time values in seconds. ```so tumia muda saa1 = muda.hasahivi() saa2 = muda.baada_ya(30) tofauti = muda.tofauti(saa2, saa1) // 30 ``` --- ### `ongeza(...)` To add time to a `muda` object. You must specify at least one of the following fields `sekunde`, `dakika`, `masaa`, `siku`, `wiki`, `miezi`, `miaka`. Example: ```so tumia muda sasa = muda.hasahivi() kesho = sasa.ongeza(siku=1) mwakani = sasa.ongeza(miaka=1) ``` ================================================ FILE: repl/docs/en/while.md ================================================ # WHILE (WAKATI) While loops in Nuru are used to execute a block of code repeatedly, as long as a given condition is true. This page covers the basics of while loops, including how to use the break and continue keywords within them. ## Basic Syntax A while loop is executed when a specified condition is true. You initiliaze a while loop with the `wakati` keyword followed by the condition in paranthesis `()`. The consequence of the loop should be enclosed in brackets `{}`: ```s fanya i = 1 wakati (i <= 5) { andika(i) i++ } ``` Output ```s 1 2 3 4 5 ``` ## Break (vunja) and Continue (endelea) ### Break (Vunja) Use the vunja keyword to terminate a loop: ```s fanya i = 1 wakati (i < 5) { kama (i == 3) { andika("nimevunja") vunja } andika(i) i++ } ``` Output ```s 1 2 nimevunja ``` ### Continue (Endelea) Use the endelea keyword to skip a specific iteration: ```s fanya i = 0 wakati (i < 5) { i++ kama (i == 3) { andika("nimeruka") endelea } andika(i) } ``` Output ```s 1 2 nimeruka 4 5 ``` By understanding while loops in Nuru, you can create code that repeats a specific action or checks for certain conditions, offering more flexibility and control over your code execution. ================================================ FILE: repl/docs/sw/README.md ================================================ # NYARAKA YA LUGHA YA PROGRAMU YA NURU Hii nyaraka imeandikwa ikilenga watu wenye uzoefu na kuandika au kupanga programu. Inaelezea sintaksia, aina na namna ya kufanya operesheni mbali mbali kutumia lugha ya NURU. ## Yaliyomo - [Safu Kwenye Nuru](arrays.md#arrays-in-nuru) - [Kutengeneza Safu](arrays.md#creating-arrays) - [Kupata na Kurekebisha vipengele vya Safu](arrays.md#accessing-and-modifying-array-elements) - [Kuunganisha Safu](arrays.md#concatenating-arrays) - [Kuangalia uwepo wa vipengele ndani ya safu](arrays.md#checking-for-array-membership) - [Kupita na Kurejea kwenye safu](arrays.md#looping-over-arrays) - [Mbinu za Safu](arrays.md#array-methods) - [idadi()](arrays.md#idadi()) - [sukuma()](arrays.md#sukuma()) - [yamwisho()](arrays.md#yamwisho()) - [Visaidia-kazi vya Nuru](builtins.md#built-in-functions-in-nuru) - [Kisaidia-kazi andika() ](builtins.md#the-andika()-function) - [Kisaidia-kazi jaza()](builtins.md#the-jaza()-function) - [Kisaidia-kazi aina()](builtins.md#the-aina()-function) - [Kisaidia-kazi fungua()](builtins.md#the-fungua()-function) - [Maoni kwenye Nuru](comments.md#comments-in-nuru) - [Maoni ya mstari mmoja](comments.md#single-line-comments) - [Maoni ya mistari mingi](comments.md#multi-line-comments) - [Kauli za masharti kwenye Nuru](ifStatements.md#conditional-statements-in-nuru) - [Kauli ya kama](ifStatements.md#if-statement-(kama)) - [Kauli za Au Kama na Sivyo)](ifStatements.md#else-if-and-else-blocks-(au-kama-and-sivyo)) ================================================ FILE: repl/docs/sw/arrays.md ================================================ # Orodha Au Safu Katika Nuru Safu katika nuru ni miundo ya data ambayo inaweza kubeba vitu vingi, ikiwa ni pamoja na aina za data tofauti tofauti kama `namba`, `tungo`, `buliani`, `vitendakazi`, na thamani `tupu`. Ukurasa huu unaangazia vipengele mbalimbali vya safu, ikiwemo namna ya kutengeneza, kuchambua, na kuzunguka ndani yake kwa kutumia vitendakazi vilivyojengwa ndani ya Nuru. ## Kutengeneza Safu Kutengeneza safu, tumia mabano mraba na tenganisha kila kitu kimoja kwa kutumia mkwaju: ```s orodha = [1, "pili", kweli] ``` ## Kupata na Kubadilisha Vipengele vya Safu Safu katika Nuru ni zero-indexed; ikimaanisha kipengele cha kwanza katika safu kina kumbukumbu namba 0. Kupata kipengele, unaweza ukatumia kumbukumbu namba yake ndani ya mabano mraba: ```s namba = [10, 20, 30] jina = namba[1] // jina is 20 ``` Unaweza ukabadilisha kipengele katika safu kwa kutumia kumbukumbu namba yake: ```s namba = [10, 20, 30] namba[1] = 25 andika(namba) // Tokeo: [10,25,30] ``` ## Kuunganisha Safu Kuunganisha safu mbili au zaidi, tumia kiendeshi `+`: ```s a = [1, 2, 3] b = [4, 5, 6] c = a + b // c is now [1, 2, 3, 4, 5, 6] ``` ## Kuangalia Uanachama Katika Safu Tumia neno msingi `ktk` kuangalia kama kipengele kipo ndani ya safu: ```s namba = [10, 20, 30] andika(20 ktk namba) // Tokeo: kweli ``` ## Kuzunguka Ndani ya Safu Unaweza kutumia maneno msingi `kwa` na `ktk` kuzunguka ndani ya safu. Kuzunguka ndani ya safu na kupata kipengele peke yake tumia sintaksia ifuatayo: ```s namba = [1, 2, 3, 4, 5] kwa thamani ktk namba { andika(thamani) } //Tokeo: 1 2 3 4 5 ``` Kuzunguka ndani ya safu na kupata kumbukumbu namba na kipengele tumia sintaksi aifuatayo: ```s majina = ["Juma", "Asha", "Haruna"] kwa idx, jina ktk majina { andika(idx, "-", jina) } //Tokeo: 0-Juma 1-Asha 2-Haruna ``` ## Vitendakazi vya Safu Nuru ina vitendakazi mbalimbali vilivyojengwa ndani kwa ajili ya Safu: ### idadi() `idadi()` hurudisha urefu wa safu: ```s a = [1, 2, 3] urefu = a.idadi() andika(urefu) // Tokeo: 3 ``` ### sukuma() `sukuma()` huongeza kipengele kimoja au zaidi mwishoni mwa safu: ```s a = [1, 2, 3] a.sukuma("s", "g") andika(a) // Tokeo [1, 2, 3, "s", "g"] ``` ### yamwisho() `yamwisho()` hurudisha kipengele cha mwisho katika safu, au `tupu` kama safu haina kitu: ```s a = [1, 2, 3] mwisho = a.yamwisho() andika(mwisho) // Tokeo: 3 b = [] mwisho = b.yamwisho() andika(mwisho) // Tokeo: tupu ``` Kwa kutumia taarifa hii, unaweza ukafanyakazi na safu za Nuru kwa ufanisi, kufanya iwe rahisi kuchambua mikusanyo ya data katika programu zako. ================================================ FILE: repl/docs/sw/bools.md ================================================ # Kufanya Kazi na Buliani Katika Nuru Vitu vyote katika Nuru ni kweli, yaani thamani yoyote ni kweli isipokua tupu and sikweli. Hutumika kutathmini semi ambazo zinarudisha kweli au sikweli. ## Kutathmini Semi za Buliani ### Kutathmini Semi Rahisi Katika Nuru, unaweza kutathmini semi rahisi zinazorudisha thamani ya buliani: ```go andika(1 > 2) // Matokeo: `sikweli` andika(1 + 3 < 10) // Matokeo: `kweli` ``` ### Kutathmini Semi Tata Katika Nuru, unaweza kutumia viendeshaji vya buliani kutathmini semi tata: ```go a = 5 b = 10 c = 15 tokeo = (a < b) && (b < c) kama (tokeo) { andika("Hali zote mbili ni kweli") } sivyo { andika("Angalau hali moja ni sikweli") } // Tokeo: "Hali zote mbili ni kweli" ``` Hapa tumetengeneza vibadilika vitatu a,b,c. Kisha tukatathmini semi (a < b) && (b < c). Kwa sababu semi zote mbili ni kweli, tokeo litakua "Hali zote mbili ni kweli". ## Vitendakazi vya Buliani Nuru ina vitendakazi vya buliani kadhaa ambavyo unaweza ukatumia kutathmini semi: ### Kitendakazi `&&` Kitendakazi `&&` hutathmini kwenda kweli kama tu vitu vyote vinavyohusika ni kweli. Kwa mfano: ```go andika(kweli && kweli) // Tokeo: `kweli` andika(kweli && sikweli) // Tokeo: `sikweli` ``` ### Kitendakazi `||` Kitendakazi || hutathmini kwenda kweli kama angalau kitu kimoja kati ya vyote vinavyohusika ni kweli. Kwa mfano: ```go andika(kweli || sikweli) // Tokeo: `kweli` andika(sikweli || sikweli) // Tokeo: `sikweli` ``` ### Kitendakazi `!` Kitendakazi `!` hukanusha thamani ya kitu. Kwa mfano: ```go andika(!kweli) // Tokeo: `sikweli` andika(!sikweli) // Tokeo: `kweli` ``` ## Kufanya Kazi na Thamani za Buliani Katika Vitanzi Katika Nuru, unaweza ukatumia semi za buliani katika vitanzi kuendesha tabia zake. Kwa mfano: ```go namba = [1, 2, 3, 4, 5] kwa thamani ktk namba { kama (thamani % 2 == 0) { andika(thamani, " ni namba shufwa") } sivyo { andika(thamani, " ni namba witiri") } } // Output: // 1 ni namba witiri // 2 ni namba shufwa // 3 ni namba witiri // 4 ni namba shufwa // 5 ni namba witiri ``` Hapa , tumetengeneza safu yenye namba 1 hadi 5 kisha tukazunguka ndani ya safu hiyo na kwa kila namba tukatumia kitendakazi `%` ilikubaini kama namba ni shufwa au witiri. Matokeo yatakua ni "ni namba shufwa" kwa namba shufwa na "ni namba witiri" kwa namba witiri. Vitu buliani katika Nuru vinaweza kutumika kutathmini semi ambazo zinarudisha thamani ya kweli au sikweli. Unaweza kutumia vitendakazi vya buliani kutathmini semi tata na kuendesha tabia ya vitanzi. Kuelewa namna ya kufanya kazi na thamani za buliani ni ujuzi wamsingi kwa mtengenezaji programu yeyote wa Nuru. ================================================ FILE: repl/docs/sw/builtins.md ================================================ # Vitendakazi Vilivyojengwa Ndani ya Nuru Nuru ina vitendakazi kadhaa vilivyojengwa ndani vinavyofanya kazi husika. ## Kitendakazi andika() Kitendakazi `andika()` kinatumika kuchapisha ujumbe kwenye konsoli. Inawezakuchukua hoja sifuri au zaidi, na hoja zitachapishwa na nafasi kati yao. Kwa kuongeza, `andika()` huhimili uundaji wa msingi kama vile `/n` kwa ajili ya mstari mpya, `/t` kwa ajili ya nafasi ya kichupo, na `\\` kwa ajili ya mkwajunyuma. Mfano: ```go andika(1, 2, 3) // Output: 1 2 3 ``` ```go andika("Jina: Asha /n Umri: 20 /n Chuo: IFM") // Output: // Jina: Asha // Umri: 20 // Chuo: IFM ``` ## Kitendakazi jaza() Kitendakazi `jaza()` kinatumika kupata ingizo kutoka kwa mtumiaji. Inawezakuchukua hoja sifuri au moja, ambayo ni utungo utakao tumika kama kimahasishi kwa mtumiaji. Mfano: ```go fanya salamu = unda() { fanya jina = jaza("Unaitwa nani? ") andika("Mambo vipi", jina) } salamu() ``` Katika mfano huu, tunaainisha kitendakazi `salamu()` ambacho kinamhamasisha mtumiaji kuingiza jina kwa kutumia kitendakazi `jaza()`. Kisha tunatumia kitendakazi `andika()` kuchapisha ujumbe unaobeba jina la mtumiaji aliloingiza. ## Kitendakazi aina() Kitendakazi `aina()` kinatumika kutambua aina ya kitu. Inakubali hoja moja, na thamani inayorudi hua ni utungo unaoonyesha aina ya kitu. Mfano: ```go aina(2) // Output: "NAMBA" aina("Nuru") // Output: "NENO" ``` ## Kitendakazi fungua() Kitendakazi `fungua()` kinatumika kufungua faili. Inakubali hoja moja, ambayo ni njia ya faili unalotaka kufungua. Mfano: ```go faili = fungua("data.txt") ``` Katika mfano huu, tumetumia kitendakazi `fungua()` kufungua faili linaloitwa "data.txt". Kibadilika `faili` kinabeba kumbukumbu ya faili lililofunguliwa. ================================================ FILE: repl/docs/sw/dictionaries.md ================================================ # Kamusi Katika Nuru Kamusi katika Nuru ni miundo ya data inayotunza jozi za funguo-thamani. Ukurasa huu unatoa maelezo kuhusu Kamusi katika Nuru, ikiwemo namna ya kutengeneza, namna ya kubadilisha, na namna ya kuzunguka ndani yake. ## Kutengeneza Kamusi Kamusi zinawekwa kwenye mabano singasinga na hujumuisha funguo na thamani zake zikitenganishwa na nukta pacha. Mfano wa uainishwaji wa kamusi: ```go orodha = {"jina": "Juma", "umri": 25} ``` Funguo zinawezakua tungo, namba, desimali, au buliani na thamani inaweza kua aina ya data yoyote ikiwemo tungo, namba, desimali, buliani, tupu, au kitendakazi: ```go k = { "jina": "Juma", "umri": 25, kweli: "kweli", "salimu": unda(x) { andika("habari", x) }, "sina thamani": tupu } ``` ## Kupata Vipengele Unaweza kupata vipengele vya kamusi kwa kutumia funguo zake: ```go k = { "jina": "Juma", "umri": 25, kweli: "kweli", "salimu": unda(x) { andika("habari", x) }, "sina thamani": tupu } andika(k[kweli]) // kweli andika(k["salimu"]("Juma")) // habari Juma ``` ## Kuboresha Vipengele Boresha thamani ya kipengele kwa kukipa thamani mpya kwenye funguo yake: ```go k = { "jina": "Juma", "umri": 25, kweli: "kweli", "salimu": unda(x) { andika("habari", x) }, "sina thamani": tupu } k['umri'] = 30 andika(k['umri']) // 30 ``` ## Kuongeza Vipengele Vipya Ongeza jozi mpya ya funguo-thamani kwenye kamusi kwa kuipa thamani funguo ambayo haipo kwenye kamusi husika: ```go k["lugha"] = "Kiswahili" andika(k["lugha"]) // Kiswahili ``` ## Kuunganisha Kamusi Unganisha kamusi mbili kwa kutumia kiendeshi `+`: ```go matunda = {"a": "apple", "b": "banana"} mboga = {"c": "tembele", "d": "mchicha"} vyakula = matunda + mboga andika(vyakula) // {"a": "apple", "b": "banana", "c": "tembele", "d": "mchicha"} ``` ## Angalia Kama Funguo Ipo Kwenye Kamusi Tumia neno msingi `ktk` kuangalia kama funguo ipo kwenye kamusi: ```go k = { "jina": "Juma", "umri": 25, kweli: "kweli", "salimu": unda(x) { andika("habari", x) }, "sina thamani": tupu } "umri" ktk k // kweli "urefu" ktk k // sikweli ``` ## Kuzunguka Ndani Ya Kamusi Zunguka ndani ya kamusi kupata funguo na thamani zake: ```go hobby = {"a": "kulala", "b": "kucheza mpira", "c": "kuimba"} kwa i, v ktk hobby { andika(i, "=>", v) } //Output a => kulala b => kucheza mpira c => kuimba ``` Kuzunguka ndani ya kamusi na kupata thamani peke yake: ```go hobby = {"a": "kulala", "b": "kucheza mpira", "c": "kuimba"} kwa i, v ktk hobby { andika(i, "=>", v) } //Output kulala kucheza mpira kuimba ``` Kwa ufahamu huu, unaweza ukatumia kamusi kikamilifu katika Nuru kutunza na kusimamia jozi za funguo-thamani, na kupata namna nyumbufu ya kupangilia na kupata data katika programu zako. ================================================ FILE: repl/docs/sw/for.md ================================================ # Vitanzi Vya Kwa Katika Nuru Vitanzi vya `kwa` ni muundo msingi wa udhibiti katika Nuru ambavyo hutumika kuzunguka vitu vinavyozungukika kama tungo, safu, na kamusi. Ukurasahuu unaangazia sintaksia na matumizi ya Vitanzi katika Nuru, ikiwemo kuzunguka ndani ya jozi ya funguo-thamani, na matumizi ya matamshi `vunja` na `endelea`. ## Sintaksia Kutengeneza kitanzi cha `kwa`, tumia neno msingi `kwa` likifwatiwa na kitambulishi cha muda mfupi kama vile `i` au `v` na kitu kinachozungukika. Funga mwili wa kitanzi na mabano singasinga `{}`. Mfano unaotumia tungo: ```go jina = "lugano" kwa i ktk jina { andika(i) } // Tokeo: l u g a n o ``` ## Kuzunguka Ndani ya Jozi ya Funguo-Thamani ### Kamusi Nuru inakuruhusu kuzunguka ndani ya kamusi kupata thamani moja moja au jozi ya funguo na thamani yake. Kupata tu thamani, tumia kitambulisha cha muda mfupi: ```go kamusi = {"a": "andaa", "b": "baba"} kwa v ktk kamusi { andika(v) } // Tokeo: andaa baba ``` Kupata thamani ya funguo na thamani zake, tumia vitambulishi vya muda mfupi viwili: ```go kwa k, v ktk kamusi { andika(k + " ni " + v) } // Tokeo: a ni andaa b ni baba ``` ### Tungo Kuzunguka juu ya thamani za tungo, tumia kitambulishi cha muda mfupi: ```go kwa v ktk "mojo" { andika(v) } // Tokeo: m o j o ``` Kuzunguka juu ya funguo na thamani zake, tumia vitambulishi vya muda mfupi viwili: ```go kwa i, v ktk "mojo" { andika(i, "->", v) } // Tokeo: 0 -> m 1 -> o 2 -> j 3 -> o ``` ### Safu Kuzunguka juu ya thamani za safu, tumia kitambulishi cha muda mfupi: ```go majina = ["juma", "asha", "haruna"] kwa v ktk majina { andika(v) } // Tokeo: juma asha haruna ``` Kuzunguka juu ya funguo na thamani katika safy, tumia vitambulishi vya muda mfupi viwili: ```go kwa i, v ktk majina { andika(i, "-", v) } // Tokeo: 0 - juma 1 - asha 2 - haruna ``` ## Vunja na Endelea ### Vunja Tumia neno msingi `vunja` kisitisha kitanzi: ```go kwa i, v ktk "mojo" { kama (i == 2) { andika("nimevunja") vunja } andika(v) } // Tokeo: m o j nimevunja ``` ### Endelea Tumia neno msingi `endelea` kuruka mzunguko maalum: ```go kwa i, v ktk "mojo" { kama (i == 2) { andika("nimeruka") endelea } andika(v) } // Tokeo: m o nimeruka o ``` ================================================ FILE: repl/docs/sw/functions.md ================================================ # Undo (Functions) Vitendakazi ni sehemu ya msingi ya Nuru inayokuwezesha kuainisha mapande ya msimbo yanayoweza kutumika tena. Ukurasa huu unaainisha sintaksia na matumizi ya vitendakazi katika nuru ikiwemo vipengele, vipengele vya msingi, matamshi ya kurudisha, kujirudia, na vifungizi. ## Sintaksia Pande la kitendakazi huanza na neno msingi `unda` likifuatiwa na vipengele vinavyowekwa ndani ya mabano `()` na mwili unaowekwa ndani ya mabano singasinga `{}`. Vitendakazi lazima viwekwe kwenye kibadiliki: ```go jumla = unda(x, y) { rudisha x + y } jumla(2, 3) // 5 ``` ## Vipengele Vitendakazi vinawezakuwa nazifuri au idadi yoyote ya vipengele. Vipengele vinawezakua vya aina yoyote hata vitendakazi vingine: ```go salamu = unda(jina) { andika("Habari yako", jina) } salamu("asha") // Habari yako asha ``` ## Vipengele Vya Msingi Vitendakazi vinawezakupewa vipengele vya msingi: ```go salimu = unda(salamu="Habari") { andika(salamu) } salimu() // Habari salimu("Mambo") // Mambo ``` ## Rudisha Unaweza pia ukarudisha thamani kwa kutumia neno msingi `rudisha`. Neno msingi `rudisha` husitisha pande la msimbo na kurudisha thamani: ```go mfano = unda(x) { rudisha "nimerudi" andika(x) } mfano("x") // nimerudi ``` ## Kujirudia Nuru pia inahimili kujirudia. Mfano wa kujirudia kwa kitendakazi cha Fibonacci: ```go fib = unda(n) { kama (n <= 1) { rudisha n } sivyo { rudisha fib(n-1) + fib(n-2) } } andika(fib(10)) // 55 ``` Kitendakazi fib kinakokotoa namba ya Fibonacci ya n kwa kujiita yenyewe ikiwa na n-1 na n-2 kama vipengele mpaka ambapo n ni ndogo kuliko au sawa na moja. ## Vifungizi Vifungizi ni vitendakazi visivyo na jina ambayo vinaweza kudaka na kuhifadhi marejeo ya vibadilika kutoka katika muktadha unaovizunguka. Katika Nuru, unaweza kutengeneza vifungizi kwa kutumia neno msingin `unda` bila kuiweka kwenye kibadiliki. Mfano: ```go fanya jum = unda(x) { rudisha unda(y) { rudisha x + y } } fanya jum_x = jum(5) andika(jum_x(3)) // 8 ``` Katika mfano hapo juu, kitendakazi `jum` kinarudisha kitendakazi kingine ambacho kinabeba kipengele kimoja tu `y`. Kitendakazi kinachorudisha kinawezakupata kibadiliki x kutoka katika muktadha unaokizunguka. Sasa umeshaelewa misingi ya vitendakazi katika Nuru, ikiwemo kujirudia na vifungizi, unaweza ukatengeneza mapande ya msimbo yanayoweza kutumika tena na tena na kurahisisha programu zako na kuboresha mpangilio wa msimbo wako. ================================================ FILE: repl/docs/sw/identifiers.md ================================================ # Vitambulisho katika Nuru Vitambulisho hutumika kuweka majina kwenye vigezo, vitendakazi na vipengele vingine katika msimbo wako wa Nuru. Ukurasa huu unashughulikia sheria na mbinu bora za kuunda vitambulisho katika Nuru. ## Sheria za Sintaksia Vitambulisho vinaweza kuwa na herufi, nambari na nistari wa chini `_`. Walakini, kuna sheria chache ambazo unapaswa kufuata wakati wa kuunda vitambulisho: - Vitambulisho haviwezi kuanza na nambari. - Vitambulisho huwa na tofauti kulingana na matumizi ya herufi kubwa na ndogo. Kwa mfano, `kibadilikaChangu` na `kibadilikachangu` huchukuliwa kuwa vitambulisho tofauti. Hapa kuna mifano ya vitambulisho halali: ```go fanya mwaka_wa_kuzaliwa = 2020 andika(mwaka_wa_kuzaliwa) // 2020 fanya badili_c_kwenda_p = "C kwenda P" andika(badili_c_kwenda_p) // "C kwenda P" ``` Katika mifano iliyo hapo juu, mwaka_wa_kuzaliwa na badili_c_kwenda_p zote ni vitambulisho halali. ## Mazoea Bora Wakati wa kuchagua vitambulisho, ni muhimu kufuata mazoea bora ili kuhakikisha kuwa msimbo wako uko wazi na rahisi kueleweka: - Tumia majina yanayoelezea wazi kusudi au maana ya kigezo au kitendakazi. - Fuata kanuni thabiti ya kuweka majina, kama vile camelCase (kibadilikaChangu) au snake_case (kibadilika_changu). - Epuka kutumia majina tofauti ya herufi moja, isipokuwa kwa vijisehemu vinavyokubalika kwa kawaida kama vile vihesabu vitanzi (i, j, k). Kwa kufuata mbinu bora hizi unapounda vitambulisho, utafanya code yako ya Nuru iwe rahisi kusoma na kutunza kwa wewe na wengine. ================================================ FILE: repl/docs/sw/if.md ================================================ # Kama/Sivyo (If/Else) ================================================ FILE: repl/docs/sw/keywords.md ================================================ # Maneno Muhimu (Keywords) ================================================ FILE: repl/docs/sw/maoni.md ================================================ # Maoni Katika Nuru Katika Nuru, unaweza kuandika maoni kutoa maelezo na hati kwa kazi yako. Maoni ni mistari ya maandishi ambayo hupuuzwa na mfasiri (interpreter) wa Nuru, kwa hivyo haitaathiri tabia ya programu yako. Kuna aina mbili za maoni katika Nuru: maoni ya mstari mmoja na maoni ya mistari mingi. ## Maoni ya Mstari Mmoja Maoni ya mstari mmoja yanatumiwa kutoa maelezo mafupi au hati kwa mstari mmoja wa kazi. Kuandika maoni ya mstari mmoja katika Nuru, tumia mikwaju miwili ya mbele (//) ikifuatiwa na maandishi ya maoni yako. Hapa kuna mfano: ```s // Mstari huu utapuuzwa na mfasiri wa Nuru ``` Katika mfano huu, maandishi ya maoni "Mstari huu utapuuzwa na mfasiri wa Nuru" yatapuuzwa na mfasiri, kwa hivyo haitaathiri tabia ya programu. ## Maoni ya Mistari Mingi Maoni ya mistari mingi yanatumiwa kutoa maelezo ya kina zaidi au hati kwa mistari mingi ya programu yako. Kuandika maoni ya mistari mingi katika Nuru, tumia ukwaju wa mbele ikifuatiwa na nyota ( /* ) kuanza maoni, na nyota ikifuatiwa na ukwaju wa mbele ( */ ) kumaliza maoni. Hapa kuna mfano: ```s / Mistari hii Ita puuzwa / ``` Katika mfano huu, mistari yote kati ya alama /* na */ itapuuzwa na mfasiri wa Nuru, kwa hivyo haitaathiri tabia ya programu. Kwa kutumia maoni ya mstari mmoja na maoni ya mistari mingi katika Nuru, unaweza kufanya kazi yako iwe rahisi kusoma na kudumisha kwa ajili yako na wengine ambao watahitaji kufanya kazi na programu yako katika siku zijazo. ================================================ FILE: repl/docs/sw/null.md ================================================ # Tupu (Null) ================================================ FILE: repl/docs/sw/numbers.md ================================================ # Namba na Desimali (Ints/Floats) ================================================ FILE: repl/docs/sw/operators.md ================================================ # Matendaji (Operators) ================================================ FILE: repl/docs/sw/range.md ================================================ ## Kitendakazi cha Mfululizo Kitendakazi cha `mfululizo` hutoa mfululizo wa nambari, sawa na kitendakazi cha `range()` cha Python. Kinaweza kutumika katika vitanzi au kuunda safu za nambari zinazofuatana. ### Muundo ```go mfululizo(mwisho) mfululizo(mwanzo, mwisho) mfululizo(mwanzo, mwisho, hatua) ``` ### Vipengele - `mwisho`: Kikomo cha juu cha mfululizo (haijumuishwi). - `mwanzo` (si lazima): Thamani ya kuanzia ya mfululizo. Chaguo-msingi ni 0. - `hatua` (si lazima): Ongezeko kati ya kila nambari katika mfululizo. Chaguo-msingi ni 1. ### Thamani Inayorudishwa Hurudisha safu ya nambari kamili. ### Mifano ```go // Toa nambari kutoka 0 hadi 4 kwa i katika mfululizo(5) { andika(i) } // Tokeo: 0 1 2 3 4 // Toa nambari kutoka 1 hadi 9 kwa i katika mfululizo(1, 10) { andika(i) } // Tokeo: 1 2 3 4 5 6 7 8 9 // Toa nambari shufwa kutoka 0 hadi 8 kwa i katika mfululizo(0, 10, 2) { andika(i) } // Tokeo: 0 2 4 6 8 // Toa nambari kwa mpangilio wa kurudi nyuma kwa i katika mfululizo(10, 0, -1) { andika(i) } // Tokeo: 10 9 8 7 6 5 4 3 2 1 ``` ### Vidokezo - Thamani ya `mwisho` haijumuishwi, ikimaanisha mfululizo utasimama kabla ya kufikia thamani hii. - Ikiwa `hatua` hasi imetolewa, `mwanzo` inapaswa kuwa kubwa kuliko `mwisho`. - Thamani ya `hatua` haiwezi kuwa sifuri. ================================================ FILE: repl/docs/sw/strings.md ================================================ # Neno (Strings) ================================================ FILE: repl/docs/sw/switch.md ================================================ # Badili (Switch) ================================================ FILE: repl/docs/sw/while.md ================================================ # Wakati (While) ================================================ FILE: repl/docs.go ================================================ package repl import ( "fmt" "strings" "github.com/NuruProgramming/Nuru/evaluator" "github.com/NuruProgramming/Nuru/lexer" "github.com/NuruProgramming/Nuru/object" "github.com/NuruProgramming/Nuru/parser" "github.com/NuruProgramming/Nuru/styles" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/glamour" "github.com/charmbracelet/lipgloss" zone "github.com/lrstanley/bubblezone" ) var ( buttonStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder(), true, false). Padding(0, 3). MarginTop(1) activeButtonStyle = buttonStyle.Copy(). Foreground(lipgloss.Color("#FFF7DB")). Background(lipgloss.Color("#aa6f5a")). Margin(0, 2). Underline(true) tableOfContentStyle = lipgloss.NewStyle().Margin(1, 2).BorderStyle(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("#aa6f5a")). Foreground(lipgloss.Color("#aa6f5a")). Padding(2) ) type item struct { title, desc, filename string } func (i item) Title() string { return i.title } func (i item) Description() string { return i.desc } func (i item) FilterValue() string { return i.title } type languages struct { title, desc, dir string } func (l languages) Title() string { return l.title } func (l languages) Description() string { return l.desc } func (l languages) FilterValue() string { return l.title } type playground struct { id string output viewport.Model code string editor textarea.Model docs viewport.Model ready bool filename string content []byte mybutton string fileSelected bool toc list.Model windowWidth int windowHeight int docRenderer *glamour.TermRenderer language string languageCursor list.Model } func (pg playground) Init() tea.Cmd { return textarea.Blink } func (pg playground) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var ( edCmd tea.Cmd opCmd tea.Cmd docCmd tea.Cmd tocCmd tea.Cmd ) pg.editor, edCmd = pg.editor.Update(msg) pg.output, opCmd = pg.output.Update(msg) pg.languageCursor, _ = pg.languageCursor.Update(msg) if !pg.fileSelected { pg.toc, tocCmd = pg.toc.Update(msg) } switch msg := msg.(type) { case tea.KeyMsg: switch msg.Type { case tea.KeyCtrlC: fmt.Println(pg.editor.Value()) return pg, tea.Quit case tea.KeyEnter: if pg.language == "" { i, ok := pg.languageCursor.SelectedItem().(languages) if ok { pg.language = i.dir if pg.language == "en" { pg.toc = list.New(englishItems, list.NewDefaultDelegate(), pg.windowWidth/2-4, pg.windowHeight-8) pg.toc.Title = "Table of Contents" } else { pg.toc = list.New(kiswahiliItems, list.NewDefaultDelegate(), pg.windowWidth/2-4, pg.windowHeight-8) pg.toc.Title = "Yaliyomo" } return pg, tea.EnterAltScreen } } i, ok := pg.toc.SelectedItem().(item) if ok { pg.filename = i.filename content, err := res.ReadFile("docs/" + pg.language + "/" + pg.filename) if err != nil { panic(err) } pg.content = content str, err := pg.docRenderer.Render(string(pg.content)) if err != nil { panic(err) } pg.docs.SetContent(str + "\n\n\n\n\n\n") if err != nil { panic(err) } pg.fileSelected = true pg.editor.Focus() } case tea.KeyCtrlR: if strings.Contains(pg.editor.Value(), "jaza") { pg.output.SetContent(styles.HelpStyle.Italic(false).Render("Samahani, huwezi kutumia `jaza()` kwa sasa.")) } else { // this is just for the output will find a better solution code := strings.ReplaceAll(pg.editor.Value(), "andika", "_andika") pg.code = code env := object.NewEnvironment() l := lexer.New(pg.code) p := parser.New(l) program := p.ParseProgram() if len(p.Errors()) != 0 { pg.output.Style = styles.ErrorStyle.PaddingLeft(3) pg.output.SetContent(strings.Join(p.Errors(), "\n")) } else { evaluated := evaluator.Eval(program, env) if evaluated != nil { if evaluated.Type() != object.NULL_OBJ { pg.output.Style = styles.ReplStyle.PaddingLeft(3) content := evaluated.Inspect() l := strings.Split(content, "\n") if len(l) > 15 { content = strings.Join(l[len(l)-16:], "\n") } pg.output.SetContent(content) } } } } case tea.KeyEsc: if pg.fileSelected { pg.fileSelected = false pg.editor.Blur() } } case tea.MouseMsg: if zone.Get(pg.id + "docs").InBounds(msg) { pg.docs, docCmd = pg.docs.Update(msg) } switch msg.Type { case tea.MouseLeft: if zone.Get(pg.id + "run").InBounds(msg) { if strings.Contains(pg.editor.Value(), "jaza") { pg.output.SetContent(styles.HelpStyle.Italic(false).Render("Samahani, huwezi kutumia `jaza()` kwa sasa.")) } else { // this is just for the output will find a better solution code := strings.ReplaceAll(pg.editor.Value(), "andika", "_andika") pg.code = code env := object.NewEnvironment() l := lexer.New(pg.code) p := parser.New(l) program := p.ParseProgram() if len(p.Errors()) != 0 { pg.output.Style = styles.ErrorStyle.PaddingLeft(3) pg.output.SetContent(strings.Join(p.Errors(), "\n")) } else { evaluated := evaluator.Eval(program, env) if evaluated != nil { if evaluated.Type() != object.NULL_OBJ { pg.output.Style = styles.ReplStyle.PaddingLeft(3) content := evaluated.Inspect() l := strings.Split(content, "\n") if len(l) > 15 { content = strings.Join(l[len(l)-16:], "\n") } pg.output.SetContent(content) } } } } } } case tea.WindowSizeMsg: if !pg.ready { // editor code pg.editor = textarea.New() if pg.language == "en" { pg.editor.Placeholder = "Write Nuru code here..." } else { pg.editor.Placeholder = "Andika code yako hapa..." } pg.editor.Prompt = "┃ " pg.editor.SetWidth(msg.Width / 2) pg.editor.SetHeight((2 * msg.Height / 3) - 4) pg.editor.CharLimit = 0 pg.editor.FocusedStyle.CursorLine = lipgloss.NewStyle() pg.editor.FocusedStyle.Base = lipgloss.NewStyle().PaddingTop(2). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("238")) pg.editor.ShowLineNumbers = true // output of editor pg.output = viewport.New(msg.Width/2, msg.Height/3-4) pg.output.Style = lipgloss.NewStyle().PaddingLeft(3) var output string if pg.language == "en" { output = "Your code output will be displayed here..." + strings.Repeat(" ", msg.Width-6) } else { output = "Matokeo hapa..." + strings.Repeat(" ", msg.Width-6) } pg.output.SetContent(output) // documentation pg.docs = viewport.New(msg.Width/2, msg.Height) pg.docs.KeyMap = viewport.KeyMap{ Up: key.NewBinding( key.WithKeys("up"), ), Down: key.NewBinding( key.WithKeys("down"), ), } pg.docs.Style = lipgloss.NewStyle(). BorderStyle(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("62")). Padding(2) renderer, err := glamour.NewTermRenderer( glamour.WithAutoStyle(), glamour.WithWordWrap(msg.Width/2-4), ) if err != nil { panic(err) } pg.docRenderer = renderer pg.toc.SetSize(msg.Width, msg.Height-8) pg.windowWidth = msg.Width pg.windowHeight = msg.Height if pg.language == "en" { pg.mybutton = activeButtonStyle.Width(msg.Width / 2).Height(1).Align(lipgloss.Center).Render("Run (CTRL + R)") } else { pg.mybutton = activeButtonStyle.Width(msg.Width / 2).Height(1).Align(lipgloss.Center).Render("Run (CTRL + R)") } pg.ready = true } else { pg.editor.SetHeight((2 * msg.Height / 3) - 4) pg.editor.SetWidth(msg.Width / 2) pg.output.Height = msg.Height/3 - 4 pg.output.Width = msg.Width / 2 renderer, err := glamour.NewTermRenderer( glamour.WithAutoStyle(), glamour.WithWordWrap(msg.Width/2-4), ) if err != nil { panic(err) } pg.docRenderer = renderer str, err := pg.docRenderer.Render(string(pg.content)) if err != nil { panic(err) } pg.docs.Height = msg.Height pg.docs.Width = msg.Width / 2 pg.docs.SetContent(str + "\n\n\n\n\n\n") if pg.language == "en" { pg.mybutton = activeButtonStyle.Width(msg.Width / 2).Height(1).Align(lipgloss.Center).Render("Run (CTRL + R)") } else { pg.mybutton = activeButtonStyle.Width(msg.Width / 2).Height(1).Align(lipgloss.Center).Render("Run (CTRL + R)") } pg.toc.SetSize(msg.Width, msg.Height-8) pg.windowWidth = msg.Width pg.windowHeight = msg.Height } } return pg, tea.Batch(edCmd, opCmd, docCmd, tocCmd) } func (pg playground) View() string { if pg.language == "" { return lipgloss.NewStyle().PaddingTop(1).Render(pg.languageCursor.View()) } if !pg.ready { return "\n Tunakuandalia....." } var docs string if !pg.fileSelected { docs = zone.Mark(pg.id+"toc", tableOfContentStyle.Width(pg.windowWidth/2-4).Height(pg.windowHeight-8).Render(pg.toc.View())) } else { docs = zone.Mark(pg.id+"docs", pg.docs.View()) } button := zone.Mark(pg.id+"run", pg.mybutton) return zone.Scan(lipgloss.JoinHorizontal(lipgloss.Center, docs, lipgloss.JoinVertical(lipgloss.Left, pg.editor.View(), button, pg.output.View()))) } ================================================ FILE: repl/repl.go ================================================ package repl import ( "embed" "fmt" "log" "os" "strings" prompt "github.com/AvicennaJr/GoPrompt" "github.com/NuruProgramming/Nuru/evaluator" "github.com/NuruProgramming/Nuru/lexer" "github.com/NuruProgramming/Nuru/object" "github.com/NuruProgramming/Nuru/parser" "github.com/NuruProgramming/Nuru/styles" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" zone "github.com/lrstanley/bubblezone" ) const PROMPT = ">>> " //go:embed docs var res embed.FS func Read(contents string) { env := object.NewEnvironment() l := lexer.New(contents) p := parser.New(l) program := p.ParseProgram() if len(p.Errors()) != 0 { fmt.Println(styles.ErrorStyle.Italic(false).Render("Kuna Errors Zifuatazo:")) for _, msg := range p.Errors() { fmt.Println("\t" + styles.ErrorStyle.Render(msg)) } } evaluated := evaluator.Eval(program, env) if evaluated != nil { if evaluated.Type() != object.NULL_OBJ { fmt.Println(styles.ReplStyle.Render(evaluated.Inspect())) } } } func Start() { env := object.NewEnvironment() var d dummy d.env = env p := prompt.New( d.executor, completer, prompt.OptionPrefix(PROMPT), prompt.OptionTitle("Nuru Programming Language"), ) p.Run() } type dummy struct { env *object.Environment } func (d *dummy) executor(in string) { if strings.TrimSpace(in) == "exit()" || strings.TrimSpace(in) == "toka()" { fmt.Println(lipgloss.NewStyle().Render("\n🔥🅺🅰🆁🅸🅱🆄 🆃🅴🅽🅰 🔥")) os.Exit(0) } l := lexer.New(in) p := parser.New(l) program := p.ParseProgram() if len(p.Errors()) != 0 { for _, msg := range p.Errors() { fmt.Println("\t" + styles.ErrorStyle.Render(msg)) } } env := d.env evaluated := evaluator.Eval(program, env) if evaluated != nil { if evaluated.Type() != object.NULL_OBJ { fmt.Println(styles.ReplStyle.Render(evaluated.Inspect())) } } } func completer(in prompt.Document) []prompt.Suggest { return []prompt.Suggest{} } func Docs() { zone.NewGlobal() languageChoice := []list.Item{ languages{title: "Kiswahili", desc: "Soma nyaraka kwa Kiswahili", dir: "sw"}, languages{title: "English", desc: "Read documentation in English", dir: "en"}, } var p playground p.languageCursor = list.New(languageChoice, list.NewDefaultDelegate(), 50, 8) p.languageCursor.Title = "Chagua Lugha" p.languageCursor.SetFilteringEnabled(false) p.languageCursor.SetShowStatusBar(false) p.languageCursor.SetShowPagination(false) p.languageCursor.SetShowHelp(false) p.toc = list.New(englishItems, list.NewDefaultDelegate(), 0, 0) p.toc.Title = "Table of Contents" p.id = zone.NewPrefix() if _, err := tea.NewProgram(p, tea.WithMouseAllMotion()).Run(); err != nil { log.Fatal(err) } } var ( englishItems = []list.Item{ item{title: "Arrays", desc: "🚀 Unleash the power of arrays in Nuru", filename: "arrays.md"}, item{title: "Booleans", desc: "👍👎 Master the world of 'if' and 'else' with bools", filename: "bool.md"}, item{title: "Builtins", desc: "💡 Reveal the secrets of builtin functions in Nuru", filename: "builtins.md"}, item{title: "Comments", desc: "💬 Speak your mind with comments in Nuru", filename: "comments.md"}, item{title: "Dictionaries", desc: "📚 Unlock the knowledge of dictionaries in Nuru", filename: "dictionaries.md"}, item{title: "Files", desc: "💾 Handle files effortlessly in Nuru", filename: "files.md"}, item{title: "For", desc: "🔄 Loop like a pro with 'for' in Nuru", filename: "for.md"}, item{title: "Function", desc: "🔧 Create powerful functions in Nuru", filename: "function.md"}, item{title: "Identifiers", desc: "🔖 Give your variables their own identity in Nuru", filename: "identifiers.md"}, item{title: "If Statements", desc: "🔮 Control the flow with 'if' statements in Nuru", filename: "ifStatements.md"}, item{title: "JSON", desc: "📄 Master the art of JSON in Nuru", filename: "json.md"}, item{title: "Keywords", desc: "🔑 Learn the secret language of Nuru's keywords", filename: "keywords.md"}, item{title: "Net", desc: "🌐 Explore the world of networking in Nuru", filename: "net.md"}, item{title: "Null", desc: "🌌 Embrace the void with Null in Nuru", filename: "null.md"}, item{title: "Numbers", desc: "🔢 Discover the magic of numbers in Nuru", filename: "numbers.md"}, item{title: "Operators", desc: "🧙 Perform spells with Nuru's operators", filename: "operators.md"}, item{title: "Packages", desc: "📦 Harness the power of packages in Nuru", filename: "packages.md"}, item{title: "Strings", desc: "🎼 Compose stories with strings in Nuru", filename: "strings.md"}, item{title: "Switch", desc: "🧭 Navigate complex scenarios with 'switch' in Nuru", filename: "switch.md"}, item{title: "Time", desc: "⏰ Manage time with ease in Nuru", filename: "time.md"}, item{title: "While", desc: "⌛ Learn the art of patience with 'while' loops in Nuru", filename: "while.md"}, } kiswahiliItems = []list.Item{ item{title: "Maoni Katika Nuru", desc: "💬 Toa mawazo yako na maoni (comments) katika Nuru", filename: "maoni.md"}, item{title: "Vitambulishi", desc: "🔖 Toa utambulisho wa kipekee kwa vigezo vyako katika Nuru", filename: "identifiers.md"}, item{title: "Nambari", desc: "🔢 Gundua uchawi wa nambari katika Nuru", filename: "numbers.md"}, item{title: "Maneno", desc: "🎼 Tunga hadithi kwa kutumia maneno katika Nuru", filename: "strings.md"}, item{title: "Kamusi", desc: "📚 Fungua maarifa ya kamusi katika Nuru", filename: "dictionaries.md"}, item{title: "Buliani", desc: "👍👎 Kuwa mtaalam wa ulimwengu wa 'if' na 'else' kwa kutumia bool", filename: "bools.md"}, item{title: "Tupu", desc: "🌌 Kubali utupu na Null katika Nuru", filename: "null.md"}, item{title: "Safu", desc: "🚀 Fungua nguvu za safu (arrays) katika Nuru", filename: "arrays.md"}, item{title: "Kwa", desc: "🔄 Rudia kama mtaalam kwa kutumia 'kwa' katika Nuru", filename: "for.md"}, item{title: "Wakati", desc: "⌛ Jifunze sanaa ya subira na vitanzi vya 'wakati' katika Nuru", filename: "while.md"}, item{title: "Undo", desc: "🔧 Unda kazi zenye nguvu katika Nuru", filename: "function.md"}, item{title: "Badili", desc: "🧭 Elekeza hali ngumu kwa kutumia 'badili' katika Nuru", filename: "switch.md"}, item{title: "Faili", desc: "💾 Shughulikia faili kwa urahisi katika Nuru", filename: "files.md"}, item{title: "Muda", desc: "⏰ Simamia muda kwa urahisi katika Nuru", filename: "time.md"}, item{title: "JSON", desc: "📄 Kuwa mtaalam wa sanaa ya JSON katika Nuru", filename: "json.md"}, item{title: "Mtandao", desc: "🌐 Chunguza ulimwengu wa mitandao katika Nuru", filename: "net.md"}, item{title: "Vifurushi", desc: "📦 Tumia nguvu za vifurushi katika Nuru", filename: "packages.md"}, item{title: "Vijenzi", desc: "💡 Funua siri za kazi za kujengwa katika Nuru", filename: "builtins.md"}, } ) ================================================ FILE: sh/install.sh ================================================ #!/usr/bin/env sh # Hii ni skripti ya shell ili kusakinisha programu ya Nuru. # Programu zinazohitajika: # - curl/wget: Kupakua faili za 'tar' kutoka 'Github' # - cp: Nakili faili kuenda mahali sahihi # - jq: Kupata uhusiano kwenye fomati ya 'JSON' # - tar: Kufungua faili za tar.gz set -e ARCH="$(uname -m)" OSNAME="$(uname -s)" PREFIX_PATH="/usr" BIN="" VERSION="latest" RELEASE_URL="https://github.com/NuruProgramming/Nuru/releases" TEMP="" # Cleanup function to remove temp directory on exit cleanup() { if [ -n "$TEMP" ] && [ -d "$TEMP" ]; then rm -rf "$TEMP" fi } trap cleanup EXIT # Check if command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Print usage information usage() { echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " -p, --prefix The base path to be used when installing (default: /usr)" echo " -v, --version The version to be downloaded from GitHub (default: latest)" echo " -h, --help Show this help message" echo "" } # Normalize architecture names arch_name() { case "$ARCH" in x86_64) ARCH="amd64" ;; i386|i686) ARCH="i386" ;; arm64|aarch64) ARCH="arm64" ;; *) echo "Unsupported architecture: $ARCH" exit 2 ;; esac } # Validate OS name os_name() { case "$OSNAME" in Darwin|Linux|Android) ;; *) echo "Unsupported Operating System: $OSNAME" exit 2 ;; esac } # Parse command line arguments parse_args() { while [ "$#" -gt 0 ]; do case "$1" in -h|--help) usage exit 0 ;; -p|--prefix) shift if [ -z "$1" ]; then echo "Error: Missing argument for --prefix" exit 1 fi PREFIX_PATH="$1" ;; -v|--version) shift if [ -z "$1" ]; then echo "Error: Missing argument for --version" exit 1 fi VERSION="$1" ;; --) shift break ;; *) echo "Unknown argument: $1" usage exit 1 ;; esac shift done BIN="$PREFIX_PATH/bin" } # Download file using curl or wget download() { URL="$1" if command_exists curl; then curl -fSL "$URL" elif command_exists wget; then wget -qO- "$URL" else echo "Error: Neither curl nor wget is installed." exit 1 fi } main() { os_name arch_name parse_args "$@" # Check required commands for cmd in jq tar cp; do if ! command_exists "$cmd"; then echo "Error: Required command '$cmd' not found." exit 1 fi done if [ "$VERSION" = "latest" ]; then echo "Fetching latest version tag from GitHub..." VERSION="$(download "https://api.github.com/repos/NuruProgramming/Nuru/releases/latest" | jq -r .tag_name)" if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then echo "Error: Unable to determine latest version." exit 1 fi fi TAR_URL="$RELEASE_URL/download/$VERSION/nuru_${OSNAME}_${ARCH}.tar.gz" echo "Downloading Nuru version $VERSION for $OSNAME/$ARCH..." TEMP="$(mktemp -d)" if ! download "$TAR_URL" | tar -xz -C "$TEMP"; then echo "Error: Failed to download or extract archive." exit 1 fi # Ensure bin directory exists if [ ! -d "$BIN" ]; then echo "Creating directory $BIN" mkdir -p "$BIN" fi echo "Installing Nuru to $BIN/nuru" if ! cp "$TEMP/nuru" "$BIN/"; then echo "Error: Failed to copy binary to $BIN" exit 1 fi echo "Installation complete." } main "$@" ================================================ FILE: styles/styles.go ================================================ package styles import "github.com/charmbracelet/lipgloss" var ( TitleStyle = lipgloss.NewStyle().Margin(1, 0).Foreground(lipgloss.Color("#aa6f5a")) VersionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#ff9671")) AuthorStyle = lipgloss.NewStyle().Italic(true).Foreground(lipgloss.Color("#ff9671")) HelpStyle = lipgloss.NewStyle().Italic(true).Faint(true).Foreground(lipgloss.Color("#ffe6d6")) ErrorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("196")).Italic(true) ReplStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("76")).Italic(true) PromptStyle = "" ) ================================================ FILE: third_party/math/README.md ================================================ # Pakeji Hesabu (Math Package) Pakeji Hesabu is a math package written in pure Nuru by [VictorKariuki](https://github.com/VictorKariuki). This package provides various mathematical functions and constants implemented in nuru programming language. It includes methods for `trigonometric functions`, `logarithmic functions`, `array operations`, and `utility functions`. ## Usage To use the `pakeji hesabu` package follow the steps below: 1. Copy the `hesabu.nr` file and any required third-party package files into the same directory as your project. 2. Ensure that the package file names end with the `.nr` extension and match the package names. For example, if the package name is `hesabu`, the corresponding file name should be `hesabu.nr`. 3. You can directly import the `hesabu.nr` package and any required third-party packages in your Nuru code using the `tumia` keyword. For example: ```nuru tumia "hesabu" ``` Example of calling the package methods: ```nuru andika(hesabu.e()) ## What is in This package covers a wide range of mathematical operations, including `basic arithmetic`, `trigonometry`, `exponential and logarithmic functions`, `rounding and comparison operations`, as well as some `utility and array operations`. The methods provided in the `hesabu` package can be classified into different categories based on their functionalities. Here is a classification of the methods: 1. Trigonometric Functions: - `cos(x)` - `sin(x)` - `tan(x)` - `acos(x)` - `asin(x)` - `atan(x)` 2. Hyperbolic Functions: - `cosh(x)` - `sinh(x)` - `tanh(x)` - `acosh(x)` - `asinh(x)` - `atanh(x)` 3. Exponential and Logarithmic Functions: - `exp(x)` - `expm1(x)` - `log(x)` - `log10(x)` - `log1p(x)` 4. Other Mathematical Functions: - `abs(namba)` - `ceil(x)` - `floor(x)` - `sqrt(x)` - `cbrt(x)` - `root(x, n)` - `hypot(values)` - `factorial(n)` 5. Rounding and Comparison Functions: - `round(x, method)` - `max(numbers)` - `min(numbers)` 6. Utility Functions: - `sign(x)` - `isNegative(num)` - `isInteger(num)` - `getIntegerPart(num)` 7. Array and List Operations: - `list(first, last, interval)` - `reduce(iterator, callback, initialValue)` ### 1. Constants: - **PI**: Represents the mathematical constant `π`. - **e**: Represents `Euler's Number`. - **phi**: Represents the `Golden Ratio`. - **ln10**: Represents the `natural logarithm of 10`. - **ln2**: Represents the `natural logarithm of 2`. - **log10e**: Represents the `base 10 logarithms` of Euler's number `(e)`. - **log2e**: Represents the `base 2 logarithm` of Euler's number` (e)`. - **sqrt1_2**: Represents the `square root` of `1/2`. - **sqrt2**: Represents the `square root` of `2`. - **sqrt3**: Represents the `square root` of `3`. - **sqrt5**: Represents the `square root` of `5`. - **EPSILON**: Represents a small value (2.220446049250313e-16). ### 2. Methods: 1. **abs(namba)** - Description: Calculates the absolute value of a number. - Example: `hesabu.abs(-42)` returns `42`. 2. **acos(x)** - Description: Calculates the arccosine of a number. - Example: `hesabu.acos(0.5)` returns `1.0471975511965979`. 3. **acosh(x)** - Description: Calculates the inverse hyperbolic cosine of a number. - Example: `hesabu.acosh(2)` returns `1.3169578969248166`. 4. **asin(x)** - Description: Calculates the arcsine of a number using the Taylor series. - Example: `hesabu.arcsin(0.5)` returns `0.5235987755982989`. 5. **asinh(x)** - Description: Calculates the inverse hyperbolic sine of a number. - Example: `hesabu.arsinh(2)` returns `1.4436354751788103`. 6. **atan(x)** - Description: Calculates the arctangent of a number using the Taylor series. - Example: `hesabu.atan(1)` returns `0.7853981633974485`. 7. **atan2(y, x)** - Description: Calculates the arctangent of the quotient of its arguments. - Example: `hesabu.atan2(1, 1)` returns `0.7853981633974483`. 8. **atanh(x)** - Description: Calculates the inverse hyperbolic tangent of a number. - Example: `hesabu.atanh(0.5)` returns `0.5493061443340549`. 9. **cbrt(x)** - Description: Calculates the cube root of a number. - Example: `hesabu.cbrt(8)` returns `2`. 10. **root(x, n)** - Description: Calculates the nth root of a number using the Newton-Raphson method. - Example: `hesabu.root(27, 3)` returns `3`. 11. **ceil(x)** - Description: Rounds up to the smallest integer greater than or equal to a given number. - Example: `hesabu.ceil(4.3)` returns `5`. 12. **cos(x)** - Description: Calculates the cosine of an angle in radians using the Taylor series. - Example: `hesabu.cos(5)` returns `0.28366218546322464`. 13. **cosh(x)** - Description: Calculates the hyperbolic cosine of a number. - Example: `hesabu.cosh(5)` returns `74.20994842490012`. 14. **exp(x)** - Description: Calculates the value of Euler's number raised to the power of a given number. - Example: `hesabu.exp(2)` returns `7.389056098930649`. 15. **expm1(x)** - Description: Calculates Euler's number raised to the power of a number minus 1. - Example: `hesabu.expm1(1)` returns `1.7182818284590455`. 16. **floor(x)** - Description: Rounds down to the largest integer less than or equal to a given number. - Example: `hesabu.floor(4.7)` returns `4`. 17. **hypot(values)** - Description: Calculates the square root of the sum of squares of the given values. - Example: `hesabu.hypot([3, 4])` returns `5`. 18. **log(x)** - Description: Calculates the natural logarithm of a number. - Example: `hesabu.log(2)` returns `0.69314718056`. 19. **log10(x)** - Description: Calculates the base 10 logarithm of a number. - Example: `hesabu.log10(100)` returns `1.9999999999573126`. 20. **log1p(x)** - Description: Calculates the natural logarithm of 1 plus the given number. - Example: `hesabu.log1p(1)` returns `0.6931471805599451`. 21. **log2(x)** - Description: Calculates the base 2 logarithm of a number. - Example: `hesabu.log2(8)` returns `3`. 22. **max(numbers)** - Description: Finds the maximum value in a list of numbers. - Example: `hesabu.max([4, 2, 9, 5])` returns `9`. 23. **min(numbers)** - Description: Finds the minimum value in a list of numbers. - Example: `hesabu.min([4, 2, 9, 5])` returns `2`. 24. **round(x, method)** - Description: Rounds a number to the nearest integer using the specified method. - supported methods: - "rpi" (round to the nearest integer using the principle of rounding half to the nearest even) - "rni" (round to the nearest integer using the principle of rounding half away from zero) - "ri" (round to the nearest integer using the standard rounding method) - An invalid method results in returning NaN (Not a Number) - Example: `hesabu.round(4.6, "rpi")` returns `5`. 25. **sign(x)** - Description: Determines the sign of a number. - Example: `hesabu.sign(-5)` returns `-1`. 26. **sin(x)** - Description: Calculates the sine of an angle in radians using the Taylor series. - Example: `hesabu.sin(1)` returns `0.8414709848078965`. 27. **sinh(x)** - Description: Calculates the hyperbolic sine of a number. - Example: `hesabu.sinh(0)` returns `0`. 28. **sqrt(x)** - Description: Calculates the square root of a number. - Example: `hesabu.sqrt(4)` returns `2`. 29. **tan(x)** - Description: Calculates the tangent of an angle in radians. - Example: `hesabu.tan(1)` returns `1.557407724654902`. 30. **tanh(x)** - Description: Calculates the hyperbolic tangent of a number. - Example: `hesabu.tanh(0)` returns `0`. 31. **factorial(n)** - Description: Calculates the factorial of a number. - Example: `hesabu.factorial(5)` returns `120`. 32. **isNegative(num)** - Description: Checks if a number is negative. - Example: `hesabu.isNegative(-5)` returns `kweli`. 33. **isInteger(num)** - Description: Checks if a number is an integer. - Example: `hesabu.isInteger(4.5)` returns `sikweli`. 34. **getIntegerPart(num)** - Description: Gets the integer part of a number. - Example: `hesabu.getIntegerPart(4.5)` returns `4`. 35. **list(first, last, interval)** - Description: Creates a list of numbers with the specified interval between them. - Example: `hesabu.list(1, 5, 1)` returns `[1, 2, 3, 4]`. 36. **reduce(iterator, callback, initialValue)** - Description: Reduces the elements of an array to a single value using a specified callback function. - Example: `hesabu.reduce([1, 2, 3, 4], [callback function], 0)` ```s fanya callback = unda(accumulator, currentValue){ rudisha accumulator + currentValue; } andika(hesabu.reduce([1, 2, 3, 4], callback, 0)) \\ returns 10. ### Contributing Contributions to the `pakeji hesabu` package are welcome. If you have any improvements or bug fixes, feel free to create a pull request. ### License This package is available under the MIT License. See the [LICENSE](LICENSE) file for more information. ================================================ FILE: third_party/math/hesabu.nr ================================================ tumia hisabati pakeji hesabu{ //CONSTRUCTOR METHOD andaa = unda() {} // Constants // π (Pi) PI = unda() { rudisha 3.141592653589793; } // e (Euler's Number) e = unda() { rudisha 2.718281828459045; } // φ (Phi, Golden Ratio) phi = unda() { rudisha 1.618033988749895; } // Natural logarithm of 10 ln10 = unda() { rudisha 2.302585092994046; } // Natural logarithm of 2 ln2 = unda() { rudisha 0.6931471805599453; } // Base 10 logarithm of Euler's number (e) log10e = unda() { rudisha 0.4342944819032518; } // Base 2 logarithm of Euler's number (e) log2e = unda() { rudisha 1.4426950408889634; } // √1/2 (equivalent to 1 / sqrt(2)) sqrt1_2 = unda() { rudisha 0.7071067811865476; } // √2 (Square Root of 2) sqrt2 = unda() { rudisha 1.414213562373095; } // √3 (Square Root of 3) sqrt3 = unda() { rudisha 1.732050807568877; } // √5 (Square Root of 5) sqrt5 = unda() { rudisha 2.236067977499790; } // @.EPSILON EPSILON = unda() { rudisha 0.0000000000000002220446049250313; } // Methods //abs(namba), calculates the absolute value of a number. abs = unda(namba){ kama(namba < 0){ rudisha - 1 * namba; } rudisha namba; } //acos(x), calculates the arccosine of a number. acos = unda(x) { kama (x < -1 || x > 1) { rudisha "NaN"; } fanya EPSILON = 1*10.0**-10; // Small value for precision fanya acosRecursive = unda(guess) { fanya f = cos(guess) - x; fanya fPrime = -sin(guess); fanya nextGuess = guess - f / fPrime; kama (abs(nextGuess - guess) < EPSILON) { rudisha nextGuess; } rudisha acosRecursive(nextGuess); } rudisha acosRecursive(hisabati.PI() / 2); // Initial guess for acos } //acosh(x), calculates the inverse hyperbolic cosine of a number. acosh = unda(x) { kama(x < 1) { rudisha 0; } rudisha log(x + sqrt(x * x - 1)); } //asin(x), calculates the arcsine of a number using the Newton Method. asin = unda(x) { kama (x < -1 || x > 1) { rudisha "NaN"; } fanya maxIterations = 50; // Maximum number of iterations fanya newtonAsin = unda(guess, prev, iterations) { fanya next = guess - (sin(guess) - x) / cos(guess); kama (abs(next - prev) < hisabati.EPSILON() || iterations >= maxIterations) { rudisha next; } rudisha newtonAsin(next, guess, iterations + 1); } rudisha newtonAsin(x, 1, 0); } //asinh(x), calculates the inverse hyperbolic sine of a number. asinh = unda(x) { // Calculate arsinh using the formula: arsinh(x) = ln(x + sqrt(x^2 + 1)) kama(x >= 0) { rudisha log(x + sqrt(x * x + 1)); } sivyo { // For negative values, arsinh(x) = -arsinh(-x) rudisha - log(-x + sqrt(x * x + 1)); } } //atan(x), calculates the arctangent of a number using the Taylor series. atan = unda(x) { fanya EPSILON = 1*10.0**-10; // Small value for precision fanya atanRecursive = unda(guess) { fanya f = tan(guess) - x; fanya fPrime = 1 / (cos(guess) * cos(guess)); fanya nextGuess = guess - f / fPrime; kama (abs(nextGuess - guess) < EPSILON) { rudisha nextGuess; } rudisha atanRecursive(nextGuess); } rudisha atanRecursive(x); // Initial guess for atan } //atanh(x), calculates the inverse hyperbolic tangent of a number. atan2 = unda(y, x) { kama(x > 0) { rudisha atan(y / x); } au kama(x < 0 && y >= 0) { rudisha atan(y / x) + hisabati.PI(); } au kama(x < 0 && y < 0) { rudisha atan(y / x) - hisabati.PI(); } au kama(x == 0 && y > 0) { rudisha hisabati.PI() / 2; } au kama(x == 0 && y < 0) { rudisha - hisabati.PI() / 2; } au kama(x == 0 && y == 0) { rudisha "NaN"; // Undefined } } //atanh(x), calculates the inverse hyperbolic tangent of a number. atanh = unda(x) { kama(x < -1 || x > 1) { rudisha 0; } rudisha 0.5 * log((1.0 + x) / (1.0 - x)); } //cbrt(x), calculates the cube root of a number. cbrt = unda(x) { kama(x == 0) { rudisha 0; } kama(x >= 0) { rudisha root(x, 3); } sivyo { rudisha - root(-x, 3); } } //root(x, n), calculates the nth root of a number using the Newton-Raphson method. root = unda(x, n) { fanya guess = x / 2; // Initial guess fanya tolerance = 0.0000000001; // Tolerance for convergence fanya calculateNthRoot = unda(x, n, guess, tolerance) { fanya nextGuess = ((n - 1) * guess + x / (guess ** (n - 1))) / n; fanya ipotolerance = abs(nextGuess - guess); kama (ipotolerance < tolerance) {rudisha nextGuess}; rudisha calculateNthRoot(x, n, nextGuess, tolerance); } rudisha calculateNthRoot(x, n, guess, tolerance) } //ceil(x), rounds up to the smallest integer greater than or equal to a given number. ceil = unda(x) { kama(x >= 0) { kama(x % 1 == 0) { rudisha x; // x is already an integer } rudisha floor(x) + 1; } sivyo { rudisha - floor(abs(x)); } } //cos(x), calculates the cosine of an angle. cos = unda(x) { fanya result = 1; // Initialize the result fanya term = 1; kwa i ktk list(2,101,2) { term = (-term * x * x) / (i * (i - 1)); result += term; } rudisha result; } //cosh(x), calculates the hyperbolic cosine of a number. cosh = unda(x) { fanya eToX = exp(x); fanya eToMinusX = exp(-x); rudisha(eToX + eToMinusX) / 2; } //exp(x), calculates the value of Euler's number raised to the power of a given number. exp = unda(n) { fanya result = 1; fanya term = 1; kwa i, v ktk list(1,23,1) { term = term*(n/v); result = result + term; } rudisha result; } //expm1(x), calculates the value of Euler's number raised to the power of a given number minus 1. expm1 = unda(x) { kama (x == -1) { rudisha -0.6321205588285577; // Handling the special case for -1 } au kama (x == 0) { rudisha 0; // Handling the special case for 0 } au kama (abs(x) < hisabati.EPSILON()) { rudisha x + 0.5 * x * x; // Approximation for very small x } sivyo { rudisha exp(x) - 1; } } //floor(x), rounds down to the largest integer less than or equal to a given number. floor = unda(x) { kama(x >= 0) { rudisha x - (x % 1); } sivyo { rudisha x - (1 + x % 1); } } //hypot(values), calculates the square root of the sum of squares of the given values. hypot = unda(values) { // Calculate the sum of squares of the values fanya exp = unda(acc, value){ rudisha acc + value ** 2; } fanya sumOfSquares = reduce(values, exp, 0); // Calculate the square root of the sum of squares fanya result = sqrt(sumOfSquares); rudisha result; } //log(x), calculates the natural logarithm of a number. log = unda(x) { kama (x <= 0) { rudisha "NaN"; } kama (x == 1) { rudisha 0; } kama (x < 0) { rudisha -log(-x); } fanya n = 1000; // Number of iterations fanya y = (x - 1) / (x + 1); fanya ySquared = y * y; fanya result = 0; kwa i ktk list(1,n+1,2) { result += (1 / i) * y**i; } rudisha 2 * result; } //log10(x), calculates the base 10 logarithm of a number. log10 = unda(x) { kama(x <= 0) { rudisha 0; } // Calculate natural logarithm and divide by the natural logarithm of 10 rudisha log(x) / log(10.0); } //log1p(x), calculates the natural logarithm of 1 plus the given number. log1p = unda(x) { kama (x <= -1) { rudisha NaN; // Not a Number } au kama (abs(x) < hisabati.EPSILON()) { rudisha x - 0.5 * x * x; // Series expansion for small x } sivyo { rudisha log(1.0 + x); } } //log2(x), calculates the base 2 logarithm of a number. log2 = unda(x) { kama(x <= 0) { rudisha 0; } fanya result = 0; fanya currentValue = x; wakati(currentValue > 1) { currentValue /= 2; result++; } rudisha result; } //max(numbers), finds the maximum value in a list of numbers. max = unda(numbers) { // Initialize a variable to store the largest number fanya largest = 0; // Iterate through the numbers and update 'largest' kama a larger number is found kwa num ktk numbers { kama(num > largest) { largest = num; } } // rudisha the largest number (or 0 kama there are no parameters) rudisha largest; } //min(numbers), finds the minimum value in a list of numbers. min = unda(numbers) { kama(numbers.idadi() == 0) { rudisha 0; } fanya minVal = numbers[0]; fanya i = 1; wakati(i < numbers.idadi()) { kama(numbers[i] < minVal) { minVal = numbers[i]; } i++; } rudisha minVal; } //round(x, method), rounds a number to the nearest integer using the specified method. round = unda(x, method = "ri") { kama(method == "rpi") { rudisha floor(x + 0.5); } au kama(method == "rni") { rudisha ceiling(x - 0.5); } au kama(method == "ri") { kama(x >= 0){ rudisha floor(x + 0.5); }sivyo{ rudisha ceiling(x - 0.5); } } sivyo { rudisha NaN; // Invalid method } } //sign(x), determines the sign of a number. sign = unda(x) { kama(x == 0 || x == -0) { rudisha x; } au kama(x > 0) { rudisha 1; } sivyo { rudisha - 1; } } //sin(x), calculates the sine of an angle in radians using the Taylor series. sin = unda(x) { fanya result = x; // Initialize the result with the angle fanya term = x; // Using Maclaurin series expansion for sine kwa i ktk list(3,101,2) { term = (-term * x * x) / (i * (i - 1)); result += term; } rudisha result; } //sinh(x), calculates the hyperbolic sine of a number. sinh = unda(x) { // sinh(x) = (e^x - e^(-x)) / 2 fanya eToX = exp(x); fanya eToMinusX = exp(-x); rudisha(eToX - eToMinusX) / 2; } //sqrt(x), calculates the square root of a number. sqrt = unda(x) { kama(x < 0) { rudisha 0; } kama(x >= 0) { rudisha root(x, 2); } sivyo { rudisha - root(-x, 2); } } //tan(x), calculates the tangent of an angle in radians. tan = unda(x) { fanya sineX = sin(x); fanya cosineX = sqrt(1 - sineX * sineX); kama(cosineX == 0) { rudisha 0; } rudisha sineX / cosineX; } //tanh(x), calculates the hyperbolic tangent of a number. tanh = unda(x) { fanya expX = exp(x); fanya expNegX = exp(-x); rudisha(expX - expNegX) / (expX + expNegX); } // utility methods //factorial(n), calculates the factorial of a number. factorial = unda(n) { kama(n == 0){ rudisha 1; }; fanya result = 1; fanya i = 1; wakati(i <= n) { result *= i; i++; } rudisha result; } //isNegative(num), checks if a number is negative. isNegative = unda(num) { rudisha sign(num)==-1; } //isInteger(num), checks if a number is an integer. isInteger = unda(num) { rudisha num == floor(num); } //getIntegerPart(num), gets the integer part of a number. getIntegerPart = unda(num) { // Handle negative numbers separately kama(isNegative(num)) { // For negative numbers, we subtract the absolute value of the fractional part from 1 rudisha - (ceil(-num) - 1); } sivyo { // For positive numbers, we simply truncate the fractional part rudisha floor(num); } } //Arrray Methods //list(first, last, interval), creates a list of numbers with the specified interval between theM. list = unda(first, last, interval){ fanya list = [first]; fanya i = first + interval; wakati(i < last){ list.sukuma(i); i += interval; } rudisha list; } //reduce(iterator, callback, initialValue), reduces the elements of an array to a single value using a specified callback function. reduce = unda(iterator, callback, initialValue) { fanya accumulator = initialValue; kwa thamani ktk iterator { accumulator = callback(accumulator, thamani); } rudisha accumulator; } } ================================================ FILE: third_party/math/test.nr ================================================ tumia "hesabu" andika("abs: ",hesabu.abs(-42)); andika("acos: ",hesabu.acos(0.5)); andika("acosh: ",hesabu.acosh(2)); andika("asin: ", hesabu.asin(0.5)); andika("asinh: ",hesabu.asinh(2)); andika("atan: ",hesabu.atan(1)); andika("atan2: ",hesabu.atan2(1, 1)); andika("atanh: ",hesabu.atanh(0.5)); andika("cbrt: ",hesabu.cbrt(8)); andika("root: ",hesabu.root(27, 3)); andika("ceil: ",hesabu.ceil(4.3)); andika("cos: ",hesabu.cos(5)); andika("cosh: ",hesabu.cosh(5)); andika("exp: ",hesabu.exp(2)); andika("expm1: ",hesabu.expm1(1)); andika("floor: ",hesabu.floor(4.7)); andika("hypot: ",hesabu.hypot([3, 4])); andika("log: ",hesabu.log(2)); andika("log10: ",hesabu.log10(100)); andika("log1p: ",hesabu.log1p(1)); andika("log2: ",hesabu.log2(8)); andika("max: ",hesabu.max([4, 2, 9, 5])); andika("min: ",hesabu.min([4, 2, 9, 5])); andika("round: ",hesabu.round(4.6, "rpi")); andika("sign: ",hesabu.sign(-5)); andika("sin: ",hesabu.sin(1)); andika("sinh: ",hesabu.sinh(0)); andika("sqrt: ",hesabu.sqrt(4)); andika("tan: ",hesabu.tan(1)); andika("tanh: ",hesabu.tanh(0)); andika("factorial: ",hesabu.factorial(5)); andika("isNegative: ",hesabu.isNegative(-5)); andika("isInteger: ",hesabu.isInteger(4.5)); andika("getIntegerPart: ",hesabu.getIntegerPart(4.5)); andika("list: ",hesabu.list(1, 5, 1)); fanya callback = unda(accumulator, currentValue){ rudisha accumulator + currentValue; } andika("reduce: ",hesabu.reduce([1, 2, 3, 4],callback,0) ); ================================================ FILE: token/token.go ================================================ // This is where we define our tokens package token type TokenType string type Token struct { Type TokenType Literal string Line int } const ( ILLEGAL = "HARAMU" EOF = "MWISHO" // Identifiers + literals IDENT = "KITAMBULISHI" INT = "NAMBA" STRING = "NENO" FLOAT = "DESIMALI" // Operators ASSIGN = "=" PLUS = "+" MINUS = "-" BANG = "!" ASTERISK = "*" POW = "**" SLASH = "/" MODULUS = "%" LT = "<" LTE = "<=" GT = ">" GTE = ">=" EQ = "==" NOT_EQ = "!=" AND = "&&" OR = "||" PLUS_ASSIGN = "+=" PLUS_PLUS = "++" MINUS_ASSIGN = "-=" MINUS_MINUS = "--" ASTERISK_ASSIGN = "*=" SLASH_ASSIGN = "/=" MODULUS_ASSIGN = "%=" SHEBANG = "#!" //Delimiters COMMA = "," SEMICOLON = ";" LPAREN = "(" RPAREN = ")" LBRACE = "{" RBRACE = "}" LBRACKET = "[" RBRACKET = "]" COLON = ":" DOT = "." AT = "@" // Keywords FUNCTION = "FUNCTION" LET = "FANYA" TRUE = "KWELI" FALSE = "SIKWELI" IF = "KAMA" ELSE = "SIVYO" RETURN = "RUDISHA" WHILE = "WAKATI" NULL = "TUPU" BREAK = "VUNJA" CONTINUE = "ENDELEA" IN = "KTK" FOR = "KWA" SWITCH = "BADILI" CASE = "IKIWA" DEFAULT = "KAWAIDA" IMPORT = "TUMIA" PACKAGE = "PAKEJI" ) var keywords = map[string]TokenType{ "unda": FUNCTION, "fanya": LET, "kweli": TRUE, "sikweli": FALSE, "kama": IF, "au": ELSE, "sivyo": ELSE, "wakati": WHILE, "rudisha": RETURN, "vunja": BREAK, "endelea": CONTINUE, "tupu": NULL, "ktk": IN, "kwa": FOR, "badili": SWITCH, "ikiwa": CASE, "kawaida": DEFAULT, "tumia": IMPORT, "pakeji": PACKAGE, "@": AT, } func LookupIdent(ident string) TokenType { if tok, ok := keywords[ident]; ok { return tok } return IDENT }