├── .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 ├── assets ├── Nuru.png ├── Nuru.svg └── nuru-logo (Transparent).png ├── ast ├── ast.go └── ast_test.go ├── evaluator ├── assign.go ├── assignEqual.go ├── at.go ├── bang.go ├── block.go ├── builtins.go ├── call.go ├── dict.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 │ ├── assets │ └── screenshot.png │ └── nuru │ ├── language-configuration.json │ ├── package.json │ └── syntaxes │ └── nuru.tmLanguage.json ├── go.mod ├── go.sum ├── gotest ├── lexer ├── lexer.go └── lexer_test.go ├── main.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 ├── 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.go ├── 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 └── repl.go ├── styles └── styles.go ├── third_party └── math │ ├── README.md │ ├── hesabu.nr │ └── test.nr ├── token └── token.go └── upx /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | description: Report a bug 3 | title: (bug report summary) 4 | labels: Bug 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Describe the bug 10 | description: What is the problem? A clear and concise description of the bug. 11 | validations: 12 | required: true 13 | 14 | - type: textarea 15 | id: expected 16 | attributes: 17 | label: Expected Behavior 18 | description: What did you expect to happen? 19 | validations: 20 | required: true 21 | 22 | - type: textarea 23 | id: current 24 | attributes: 25 | label: Current Behavior 26 | description: | 27 | What actually happened? 28 | 29 | Please include full errors, uncaught exceptions, stack traces, and relevant logs. 30 | If service/functions responses are relevant, please include wire logs. 31 | validations: 32 | required: true 33 | 34 | - type: textarea 35 | id: reproduction 36 | attributes: 37 | label: Reproduction Steps 38 | description: | 39 | Provide a self-contained, concise snippet of code that can be used to reproduce the issue. 40 | For more complex issues provide a repo with the smallest sample that reproduces the bug. 41 | 42 | Avoid including business logic or unrelated code, it makes diagnosis more difficult. 43 | The code sample should be an SSCCE. See http://sscce.org/ for details. 44 | In short, please provide a code sample that we can copy/paste, run and reproduce. 45 | validations: 46 | required: true 47 | 48 | - type: textarea 49 | id: solution 50 | attributes: 51 | label: Possible Solution 52 | description: Suggest a fix/reason for the bug 53 | validations: 54 | required: false 55 | 56 | - type: textarea 57 | id: context 58 | attributes: 59 | label: Additional Information/Context 60 | description: | 61 | Anything else that might be relevant for troubleshooting this bug. 62 | Providing context helps us come up with a solution that is most useful in the real world. 63 | validations: 64 | required: false 65 | 66 | - type: input 67 | id: version 68 | attributes: 69 | label: Nuru version 70 | description: | 71 | Please make sure to use the latest version of Nuru before reporting any issues as it may have already been fixed. 72 | validations: 73 | required: true 74 | 75 | - type: textarea 76 | id: environment 77 | attributes: 78 | label: Environment details (OS name and version, etc.) 79 | description: Your operating system (Windows, Linux, Android or MacOS) 80 | validations: 81 | required: true 82 | 83 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request 2 | description: Suggest an idea for this project 3 | title: (feature request summary) 4 | labels: Feature Request 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Describe the feature 10 | description: A clear and concise description of the feature you are proposing. 11 | validations: 12 | required: true 13 | 14 | - type: textarea 15 | id: use-case 16 | attributes: 17 | label: Use Case 18 | description: | 19 | Why do you need this feature? For example: "I'm always frustrated when..." 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | id: solution 25 | attributes: 26 | label: Proposed Solution 27 | description: Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. 28 | validations: 29 | required: false 30 | 31 | - type: textarea 32 | id: other 33 | attributes: 34 | label: Other Information 35 | description: Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. 36 | validations: 37 | required: false 38 | 39 | - type: checkboxes 40 | id: ack 41 | attributes: 42 | label: Acknowledgements 43 | options: 44 | - label: I may be able to implement this feature request 45 | required: false 46 | 47 | - label: This feature might incur a breaking change 48 | required: false 49 | 50 | - type: input 51 | id: version 52 | attributes: 53 | label: Version used 54 | description: Please provide the version of the repository or tool you are using. 55 | validations: 56 | required: true 57 | 58 | - type: textarea 59 | id: environment 60 | attributes: 61 | label: Environment details (OS name and version, etc.) 62 | description: Your operating system (Linux, Windows, Android or Mac) 63 | validations: 64 | required: true 65 | 66 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 2 | 3 | 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.21 20 | id: go 21 | 22 | - name: Test 23 | run: go mod tidy && make test 24 | 25 | - name: Run GoReleaser 26 | uses: goreleaser/goreleaser-action@v5 27 | with: 28 | distribution: goreleaser 29 | version: latest 30 | args: release --clean 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main, dev ] 6 | 7 | jobs: 8 | 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Set up Go 15 | uses: actions/setup-go@v3 16 | with: 17 | go-version: 1.21 18 | 19 | - name: Test 20 | run: go mod tidy && make test 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.[56789ao] 3 | *.a[56789o] 4 | *.so 5 | *.pyc 6 | ._* 7 | .nfs.* 8 | [56789a].out 9 | *~ 10 | *.orig 11 | *.rej 12 | *.exe 13 | .*.swp 14 | core 15 | *.cgo*.go 16 | *.cgo*.c 17 | _cgo_* 18 | _obj 19 | _test 20 | _testmain.go 21 | /VERSION.cache 22 | /bin/ 23 | /build.out 24 | /doc/articles/wiki/*.bin 25 | /goinstall.log 26 | /last-change 27 | /misc/cgo/life/run.out 28 | /misc/cgo/stdio/run.out 29 | /misc/cgo/testso/main 30 | /pkg/ 31 | /src/*.*/ 32 | /src/cmd/cgo/zdefaultcc.go 33 | /src/cmd/dist/dist 34 | /src/cmd/go/internal/cfg/zdefaultcc.go 35 | /src/cmd/go/internal/cfg/zosarch.go 36 | /src/cmd/internal/objabi/zbootstrap.go 37 | /src/go/build/zcgo.go 38 | /src/go/doc/headscan 39 | /src/runtime/internal/sys/zversion.go 40 | /src/unicode/maketables 41 | /test.out 42 | /test/garbage/*.out 43 | /test/pass.out 44 | /test/run.out 45 | /test/times.out 46 | 47 | #Personal 48 | 49 | testbinaries/ 50 | tests_random/ 51 | nuru 52 | Notes.md 53 | tutorials/en/* 54 | config.json 55 | *local* 56 | 57 | # For Nuru executables 58 | /nuru 59 | /Nuru 60 | 61 | dist/ 62 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | project_name: nuru 2 | before: 3 | hooks: 4 | - go mod tidy 5 | - go generate ./... 6 | 7 | builds: 8 | - env: 9 | - CGO_ENABLED=0 10 | goos: 11 | - linux 12 | - windows 13 | - darwin 14 | - android 15 | ldflags: 16 | - "-s -w" 17 | ignore: 18 | - goos: android 19 | goarch: 386 20 | 21 | archives: 22 | - format: tar.gz 23 | name_template: >- 24 | nuru_ 25 | {{- title .Os }}_ 26 | {{- if eq .Arch "amd64" }}amd64 27 | {{- else if eq .Arch "386" }}i386 28 | {{- else }}{{ .Arch }}{{ end }} 29 | {{- if .Arm }}v{{ .Arm }}{{ end }} 30 | format_overrides: 31 | - goos: windows 32 | format: zip 33 | 34 | changelog: 35 | sort: asc 36 | filters: 37 | exclude: 38 | - "^docs:" 39 | - "^test:" 40 | 41 | nfpms: 42 | - maintainer: "AvicennaJr" 43 | homepage: "https://nuruprogramming.org" 44 | description: "Nuru is a programming language built from the ground up" 45 | formats: 46 | - deb 47 | file_name_template: "{{ .ProjectName }}.{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 48 | -------------------------------------------------------------------------------- /ABOUT.md: -------------------------------------------------------------------------------- 1 | # NURU PROGRAMMING LANGUAGE 2 | 3 | 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. 4 | 5 | ## Background 6 | 7 | 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. 8 | 9 | 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. 10 | 11 | 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: 12 | - 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. 13 | - 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. 14 | 15 | ## Purpose 16 | 17 | 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. 18 | 19 | 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. 20 | 21 | 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. 22 | 23 | ## Philosophy 24 | 25 | 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. 26 | 27 | Nuru is also community driven. We listen to our community and do our best to implement on the feedback we get from them. 28 | 29 | ## Where Not To Use Nuru 30 | 31 | 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. 32 | 33 | ## Challenges 34 | 35 | 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. 36 | 37 | ## Future Of Nuru 38 | 39 | 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. 40 | 41 | We also hope to see games and GUI applications written in Nuru in the near future... God willing. 42 | 43 | ## Final Words 44 | 45 | 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. 46 | 47 | And finally, I thank Allah for granting us the ability to learn and giving me the ability to make such a project. 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=0.5.1 2 | 3 | build_linux: 4 | @echo 'building linux binary...' 5 | env GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o nuru 6 | @echo 'shrinking binary...' 7 | ./upx --brute nuru 8 | @echo 'zipping build....' 9 | tar -zcvf nuru_linux_amd64_v${VERSION}.tar.gz nuru 10 | @echo 'cleaning up...' 11 | rm nuru 12 | 13 | build_windows: 14 | @echo 'building windows executable...' 15 | env GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o nuru_windows_amd64_v${VERSION}.exe 16 | @echo 'shrinking build...' 17 | ./upx --brute nuru_windows_amd64_v${VERSION}.exe 18 | 19 | build_mac: 20 | @echo 'building mac binary...' 21 | env GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o nuru 22 | @echo 'shrinking binary...' 23 | ./upx --brute nuru 24 | @echo 'zipping build...' 25 | tar -zcvf nuru_mac_amd64_v${VERSION}.tar.gz nuru 26 | @echo 'cleaning up...' 27 | rm nuru 28 | 29 | build_android: 30 | @echo 'building android binary' 31 | env GOOS=android GOARCH=arm64 go build -ldflags="-s -w" -o nuru 32 | @echo 'zipping build...' 33 | tar -zcvf nuru_android_arm64_v${VERSION}.tar.gz nuru 34 | @echo 'cleaning up...' 35 | rm nuru 36 | 37 | build_test: 38 | go build -ldflags="-s -w" -o nuru 39 | 40 | dependencies: 41 | @echo 'checking dependencies...' 42 | go mod tidy 43 | 44 | test: 45 | @echo -e '\nTesting Lexer...' 46 | @./gotest --format testname ./lexer/ 47 | @echo -e '\nTesting Parser...' 48 | @./gotest --format testname ./parser/ 49 | @echo -e '\nTesting AST...' 50 | @./gotest --format testname ./ast/ 51 | @echo -e '\nTesting Object...' 52 | @./gotest --format testname ./object/ 53 | @echo -e '\nTesting Evaluator...' 54 | @./gotest --format testname ./evaluator/ 55 | 56 | clean: 57 | go clean 58 | -------------------------------------------------------------------------------- /assets/Nuru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NuruProgramming/Nuru/0805bb2df91dda4040be58160f8db114cc84764f/assets/Nuru.png -------------------------------------------------------------------------------- /assets/nuru-logo (Transparent).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NuruProgramming/Nuru/0805bb2df91dda4040be58160f8db114cc84764f/assets/nuru-logo (Transparent).png -------------------------------------------------------------------------------- /ast/ast_test.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/NuruProgramming/Nuru/token" 7 | ) 8 | 9 | func TestString(t *testing.T) { 10 | program := &Program{ 11 | Statements: []Statement{ 12 | &LetStatement{ 13 | Token: token.Token{Type: token.LET, Literal: "fanya"}, 14 | Name: &Identifier{ 15 | Token: token.Token{Type: token.IDENT, Literal: "myVar"}, 16 | Value: "myVar", 17 | }, 18 | Value: &Identifier{ 19 | Token: token.Token{Type: token.IDENT, Literal: "anotherVar"}, 20 | Value: "anotherVar", 21 | }, 22 | }, 23 | }, 24 | } 25 | 26 | if program.String() != "fanya myVar = anotherVar;" { 27 | t.Errorf("program.String() wrong. got=%q", program.String()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /evaluator/assign.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalAssign(node *ast.Assign, env *object.Environment) object.Object { 9 | val := Eval(node.Value, env) 10 | if isError(val) { 11 | return val 12 | } 13 | 14 | obj := env.Set(node.Name.Value, val) 15 | return obj 16 | } 17 | -------------------------------------------------------------------------------- /evaluator/assignEqual.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/NuruProgramming/Nuru/ast" 7 | "github.com/NuruProgramming/Nuru/object" 8 | ) 9 | 10 | func evalAssignEqual(node *ast.AssignEqual, env *object.Environment) object.Object { 11 | left := Eval(node.Left, env) 12 | if isError(left) { 13 | return left 14 | } 15 | 16 | value := Eval(node.Value, env) 17 | if isError(value) { 18 | return value 19 | } 20 | 21 | switch node.Token.Literal { 22 | case "+=": 23 | switch arg := left.(type) { 24 | case *object.Integer: 25 | switch val := value.(type) { 26 | case *object.Integer: 27 | v := arg.Value + val.Value 28 | return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) 29 | case *object.Float: 30 | v := float64(arg.Value) + val.Value 31 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 32 | default: 33 | return newError("Mstari %d: Huwezi kutumia '+=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 34 | } 35 | case *object.Float: 36 | switch val := value.(type) { 37 | case *object.Integer: 38 | v := arg.Value + float64(val.Value) 39 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 40 | case *object.Float: 41 | v := arg.Value + val.Value 42 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 43 | default: 44 | return newError("Mstari %d: Huwezi kutumia '+=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 45 | } 46 | case *object.String: 47 | switch val := value.(type) { 48 | case *object.String: 49 | v := arg.Value + val.Value 50 | return env.Set(node.Left.Token.Literal, &object.String{Value: v}) 51 | default: 52 | return newError("Mstari %d: Huwezi kutumia '+=' kwa %v na %v", node.Token.Line, arg.Type(), val.Type()) 53 | } 54 | default: 55 | return newError("Mstari %d: Huwezi kutumia '+=' na %v", node.Token.Line, arg.Type()) 56 | } 57 | case "-=": 58 | switch arg := left.(type) { 59 | case *object.Integer: 60 | switch val := value.(type) { 61 | case *object.Integer: 62 | v := arg.Value - val.Value 63 | return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) 64 | case *object.Float: 65 | v := float64(arg.Value) - val.Value 66 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 67 | default: 68 | return newError("Mstari %d: Huwezi kutumia '-=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 69 | } 70 | case *object.Float: 71 | switch val := value.(type) { 72 | case *object.Integer: 73 | v := arg.Value - float64(val.Value) 74 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 75 | case *object.Float: 76 | v := arg.Value - val.Value 77 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 78 | default: 79 | return newError("Mstari %d: Huwezi kutumia '-=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 80 | } 81 | default: 82 | return newError("Mstari %d: Huwezi kutumia '-=' na %v", node.Token.Line, arg.Type()) 83 | } 84 | case "*=": 85 | switch arg := left.(type) { 86 | case *object.Integer: 87 | switch val := value.(type) { 88 | case *object.Integer: 89 | v := arg.Value * val.Value 90 | return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) 91 | case *object.Float: 92 | v := float64(arg.Value) * val.Value 93 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 94 | case *object.String: 95 | v := strings.Repeat(val.Value, int(arg.Value)) 96 | return env.Set(node.Left.Token.Literal, &object.String{Value: v}) 97 | default: 98 | return newError("Mstari %d: Huwezi kutumia '*=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 99 | } 100 | case *object.Float: 101 | switch val := value.(type) { 102 | case *object.Integer: 103 | v := arg.Value * float64(val.Value) 104 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 105 | case *object.Float: 106 | v := arg.Value * val.Value 107 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 108 | default: 109 | return newError("Mstari %d: Huwezi kutumia '*=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 110 | } 111 | case *object.String: 112 | switch val := value.(type) { 113 | case *object.Integer: 114 | v := strings.Repeat(arg.Value, int(val.Value)) 115 | return env.Set(node.Left.Token.Literal, &object.String{Value: v}) 116 | default: 117 | return newError("Mstari %d: Huwezi kutumia '+=' kwa %v na %v", node.Token.Line, arg.Type(), val.Type()) 118 | } 119 | default: 120 | return newError("Mstari %d: Huwezi kutumia '*=' na %v", node.Token.Line, arg.Type()) 121 | } 122 | case "/=": 123 | switch arg := left.(type) { 124 | case *object.Integer: 125 | switch val := value.(type) { 126 | case *object.Integer: 127 | v := arg.Value / val.Value 128 | return env.Set(node.Left.Token.Literal, &object.Integer{Value: v}) 129 | case *object.Float: 130 | v := float64(arg.Value) / val.Value 131 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 132 | default: 133 | return newError("Mstari %d: Huwezi kutumia '/=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 134 | } 135 | case *object.Float: 136 | switch val := value.(type) { 137 | case *object.Integer: 138 | v := arg.Value / float64(val.Value) 139 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 140 | case *object.Float: 141 | v := arg.Value / val.Value 142 | return env.Set(node.Left.Token.Literal, &object.Float{Value: v}) 143 | default: 144 | return newError("Mstari %d: Huwezi kutumia '/=' kujumlisha %v na %v", node.Token.Line, arg.Type(), val.Type()) 145 | } 146 | default: 147 | return newError("Mstari %d: Huwezi kutumia '/=' na %v", node.Token.Line, arg.Type()) 148 | } 149 | default: 150 | return newError("Mstari %d: Operesheni Haifahamiki %s", node.Token.Line, node.Token.Literal) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /evaluator/at.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalAt(node *ast.At, env *object.Environment) object.Object { 9 | if at, ok := env.Get("@"); ok { 10 | return at 11 | } 12 | return newError("Iko nje ya scope") 13 | } 14 | -------------------------------------------------------------------------------- /evaluator/bang.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import "github.com/NuruProgramming/Nuru/object" 4 | 5 | func evalBangOperatorExpression(right object.Object) object.Object { 6 | switch right { 7 | case TRUE: 8 | return FALSE 9 | case FALSE: 10 | return TRUE 11 | case NULL: 12 | return TRUE 13 | default: 14 | return FALSE 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /evaluator/block.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object { 9 | var result object.Object 10 | 11 | for _, statement := range block.Statements { 12 | result = Eval(statement, env) 13 | 14 | if result != nil { 15 | rt := result.Type() 16 | if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ || rt == object.CONTINUE_OBJ || rt == object.BREAK_OBJ { 17 | return result 18 | } 19 | } 20 | } 21 | 22 | return result 23 | } 24 | -------------------------------------------------------------------------------- /evaluator/call.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalCall(node *ast.CallExpression, env *object.Environment) object.Object { 9 | function := Eval(node.Function, env) 10 | 11 | if isError(function) { 12 | return function 13 | } 14 | 15 | var args []object.Object 16 | 17 | switch fn := function.(type) { 18 | case *object.Function: 19 | args = evalArgsExpressions(node, fn, env) 20 | case *object.Package: 21 | obj, ok := fn.Scope.Get("andaa") 22 | if !ok { 23 | return newError("Pakeji haina 'andaa'") 24 | } 25 | args = evalArgsExpressions(node, obj.(*object.Function), env) 26 | default: 27 | args = evalExpressions(node.Arguments, env) 28 | } 29 | 30 | if len(args) == 1 && isError(args[0]) { 31 | return args[0] 32 | } 33 | 34 | return applyFunction(function, args, node.Token.Line) 35 | } 36 | 37 | func evalArgsExpressions(node *ast.CallExpression, fn *object.Function, env *object.Environment) []object.Object { 38 | argsList := &object.Array{} 39 | argsHash := &object.Dict{} 40 | argsHash.Pairs = make(map[object.HashKey]object.DictPair) 41 | for _, exprr := range node.Arguments { 42 | switch exp := exprr.(type) { 43 | case *ast.Assign: 44 | val := Eval(exp.Value, env) 45 | if isError(val) { 46 | return []object.Object{val} 47 | } 48 | var keyHash object.HashKey 49 | key := &object.String{Value: exp.Name.Value} 50 | keyHash = key.HashKey() 51 | pair := object.DictPair{Key: key, Value: val} 52 | argsHash.Pairs[keyHash] = pair 53 | default: 54 | evaluated := Eval(exp, env) 55 | if isError(evaluated) { 56 | return []object.Object{evaluated} 57 | } 58 | argsList.Elements = append(argsList.Elements, evaluated) 59 | } 60 | } 61 | 62 | var result []object.Object 63 | var params = map[string]bool{} 64 | for _, exp := range fn.Parameters { 65 | params[exp.Value] = true 66 | if len(argsList.Elements) > 0 { 67 | result = append(result, argsList.Elements[0]) 68 | argsList.Elements = argsList.Elements[1:] 69 | } else { 70 | keyParam := &object.String{Value: exp.Value} 71 | keyParamHash := keyParam.HashKey() 72 | if valParam, ok := argsHash.Pairs[keyParamHash]; ok { 73 | result = append(result, valParam.Value) 74 | delete(argsHash.Pairs, keyParamHash) 75 | } else { 76 | if _e, _ok := fn.Defaults[exp.Value]; _ok { 77 | evaluated := Eval(_e, env) 78 | if isError(evaluated) { 79 | return []object.Object{evaluated} 80 | } 81 | result = append(result, evaluated) 82 | } else { 83 | return []object.Object{&object.Error{Message: "Tumekosa Hoja"}} 84 | } 85 | } 86 | } 87 | } 88 | 89 | for _, pair := range argsHash.Pairs { 90 | if _, ok := params[pair.Key.(*object.String).Value]; ok { 91 | return []object.Object{&object.Error{Message: "Tumepewa hoja nyingi kwa parameter moja"}} 92 | } 93 | } 94 | 95 | return result 96 | 97 | } 98 | -------------------------------------------------------------------------------- /evaluator/dict.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalDictLiteral(node *ast.DictLiteral, env *object.Environment) object.Object { 9 | pairs := make(map[object.HashKey]object.DictPair) 10 | 11 | for keyNode, valueNode := range node.Pairs { 12 | key := Eval(keyNode, env) 13 | if isError(key) { 14 | return key 15 | } 16 | 17 | hashKey, ok := key.(object.Hashable) 18 | if !ok { 19 | return newError("Mstari %d: Hashing imeshindikana: %s", node.Token.Line, key.Type()) 20 | } 21 | 22 | value := Eval(valueNode, env) 23 | if isError(value) { 24 | return value 25 | } 26 | 27 | hashed := hashKey.HashKey() 28 | pairs[hashed] = object.DictPair{Key: key, Value: value} 29 | } 30 | 31 | return &object.Dict{Pairs: pairs} 32 | } 33 | -------------------------------------------------------------------------------- /evaluator/forin.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalForInExpression(fie *ast.ForIn, env *object.Environment, line int) object.Object { 9 | iterable := Eval(fie.Iterable, env) 10 | existingKeyIdentifier, okk := env.Get(fie.Key) // again, stay safe 11 | existingValueIdentifier, okv := env.Get(fie.Value) 12 | defer func() { // restore them later on 13 | if okk { 14 | env.Set(fie.Key, existingKeyIdentifier) 15 | } 16 | if okv { 17 | env.Set(fie.Value, existingValueIdentifier) 18 | } 19 | }() 20 | switch i := iterable.(type) { 21 | case object.Iterable: 22 | defer func() { 23 | i.Reset() 24 | }() 25 | return loopIterable(i.Next, env, fie) 26 | default: 27 | return newError("Mstari %d: Huwezi kufanya operesheni hii na %s", line, i.Type()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /evaluator/function.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalFunction(node *ast.FunctionLiteral, env *object.Environment) object.Object { 9 | function := &object.Function{ 10 | Name: node.Name, 11 | Parameters: node.Parameters, 12 | Defaults: node.Defaults, 13 | Body: node.Body, 14 | Env: env, 15 | } 16 | 17 | return function 18 | } 19 | -------------------------------------------------------------------------------- /evaluator/identifier.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object { 9 | if val, ok := env.Get(node.Value); ok { 10 | return val 11 | } 12 | if builtin, ok := builtins[node.Value]; ok { 13 | return builtin 14 | } 15 | 16 | return newError("Mstari %d: Neno Halifahamiki: %s", node.Token.Line, node.Value) 17 | } 18 | -------------------------------------------------------------------------------- /evaluator/if.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object { 9 | condition := Eval(ie.Condition, env) 10 | 11 | if isError(condition) { 12 | return condition 13 | } 14 | 15 | if isTruthy(condition) { 16 | return Eval(ie.Consequence, env) 17 | } else if ie.Alternative != nil { 18 | return Eval(ie.Alternative, env) 19 | } else { 20 | return NULL 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /evaluator/import.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/NuruProgramming/Nuru/ast" 10 | "github.com/NuruProgramming/Nuru/lexer" 11 | "github.com/NuruProgramming/Nuru/module" 12 | "github.com/NuruProgramming/Nuru/object" 13 | "github.com/NuruProgramming/Nuru/parser" 14 | ) 15 | 16 | var searchPaths []string 17 | 18 | func evalImport(node *ast.Import, env *object.Environment) object.Object { 19 | for k, v := range node.Identifiers { 20 | if mod, ok := module.Mapper[v.Value]; ok { 21 | env.Set(k, mod) 22 | } else { 23 | return evalImportFile(k, v, env) 24 | } 25 | } 26 | return NULL 27 | } 28 | 29 | func evalImportFile(name string, ident *ast.Identifier, env *object.Environment) object.Object { 30 | addSearchPath("") 31 | filename := findFile(name) 32 | if filename == "" { 33 | return newError("Moduli %s haipo", name) 34 | } 35 | var scope *object.Environment 36 | scope, err := evaluateFile(filename, env) 37 | if err != nil { 38 | return err 39 | } 40 | return importFile(name, ident, env, scope) 41 | } 42 | 43 | func addSearchPath(path string) { 44 | searchPaths = append(searchPaths, path) 45 | } 46 | 47 | func findFile(name string) string { 48 | basename := fmt.Sprintf("%s.nr", name) 49 | for _, path := range searchPaths { 50 | file := filepath.Join(path, basename) 51 | if fileExists(file) { 52 | return file 53 | } 54 | } 55 | return "" 56 | } 57 | 58 | func fileExists(file string) bool { 59 | _, err := os.Stat(file) 60 | return err == nil 61 | } 62 | 63 | func evaluateFile(file string, env *object.Environment) (*object.Environment, object.Object) { 64 | source, err := os.ReadFile(file) 65 | if err != nil { 66 | return nil, &object.Error{Message: fmt.Sprintf("Tumeshindwa kufungua pakeji: %s", file)} 67 | } 68 | l := lexer.New(string(source)) 69 | p := parser.New(l) 70 | program := p.ParseProgram() 71 | if len(p.Errors()) != 0 { 72 | return nil, &object.Error{Message: fmt.Sprintf("Pakeji %s ina makosa yafuatayo:\n%s", file, strings.Join(p.Errors(), "\n"))} 73 | } 74 | 75 | scope := object.NewEnvironment() 76 | result := Eval(program, scope) 77 | if isError(result) { 78 | return nil, result 79 | } 80 | return scope, nil 81 | } 82 | 83 | func importFile(name string, ident *ast.Identifier, env *object.Environment, scope *object.Environment) object.Object { 84 | value, ok := scope.Get(ident.Value) 85 | if !ok { 86 | return newError("%s sio pakeji", name) 87 | } 88 | env.Set(name, value) 89 | return NULL 90 | } 91 | -------------------------------------------------------------------------------- /evaluator/in.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/NuruProgramming/Nuru/object" 7 | ) 8 | 9 | func evalInExpression(left, right object.Object, line int) object.Object { 10 | switch right.(type) { 11 | case *object.String: 12 | return evalInStringExpression(left, right) 13 | case *object.Array: 14 | return evalInArrayExpression(left, right) 15 | case *object.Dict: 16 | return evalInDictExpression(left, right, line) 17 | default: 18 | return FALSE 19 | } 20 | } 21 | 22 | func evalInStringExpression(left, right object.Object) object.Object { 23 | if left.Type() != object.STRING_OBJ { 24 | return FALSE 25 | } 26 | leftVal := left.(*object.String) 27 | rightVal := right.(*object.String) 28 | found := strings.Contains(rightVal.Value, leftVal.Value) 29 | return nativeBoolToBooleanObject(found) 30 | } 31 | 32 | func evalInDictExpression(left, right object.Object, line int) object.Object { 33 | leftVal, ok := left.(object.Hashable) 34 | if !ok { 35 | return newError("Mstari %d: Huwezi kutumia kama 'key': %s", line, left.Type()) 36 | } 37 | key := leftVal.HashKey() 38 | rightVal := right.(*object.Dict).Pairs 39 | _, ok = rightVal[key] 40 | return nativeBoolToBooleanObject(ok) 41 | } 42 | 43 | func evalInArrayExpression(left, right object.Object) object.Object { 44 | rightVal := right.(*object.Array) 45 | switch leftVal := left.(type) { 46 | case *object.Null: 47 | for _, v := range rightVal.Elements { 48 | if v.Type() == object.NULL_OBJ { 49 | return TRUE 50 | } 51 | } 52 | case *object.String: 53 | for _, v := range rightVal.Elements { 54 | if v.Type() == object.STRING_OBJ { 55 | elem := v.(*object.String) 56 | if elem.Value == leftVal.Value { 57 | return TRUE 58 | } 59 | } 60 | } 61 | case *object.Integer: 62 | for _, v := range rightVal.Elements { 63 | if v.Type() == object.INTEGER_OBJ { 64 | elem := v.(*object.Integer) 65 | if elem.Value == leftVal.Value { 66 | return TRUE 67 | } 68 | } 69 | } 70 | case *object.Float: 71 | for _, v := range rightVal.Elements { 72 | if v.Type() == object.FLOAT_OBJ { 73 | elem := v.(*object.Float) 74 | if elem.Value == leftVal.Value { 75 | return TRUE 76 | } 77 | } 78 | } 79 | } 80 | return FALSE 81 | } 82 | -------------------------------------------------------------------------------- /evaluator/index.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import "github.com/NuruProgramming/Nuru/object" 4 | 5 | func evalIndexExpression(left, index object.Object, line int) object.Object { 6 | switch { 7 | case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ: 8 | return evalArrayIndexExpression(left, index) 9 | case left.Type() == object.ARRAY_OBJ && index.Type() != object.INTEGER_OBJ: 10 | return newError("Mstari %d: Tafadhali tumia number, sio: %s", line, index.Type()) 11 | case left.Type() == object.DICT_OBJ: 12 | return evalDictIndexExpression(left, index, line) 13 | default: 14 | return newError("Mstari %d: Operesheni hii haiwezekani kwa: %s", line, left.Type()) 15 | } 16 | } 17 | 18 | func evalArrayIndexExpression(array, index object.Object) object.Object { 19 | arrayObject := array.(*object.Array) 20 | idx := index.(*object.Integer).Value 21 | max := int64(len(arrayObject.Elements) - 1) 22 | 23 | if idx < 0 || idx > max { 24 | return NULL 25 | } 26 | 27 | return arrayObject.Elements[idx] 28 | } 29 | 30 | func evalDictIndexExpression(dict, index object.Object, line int) object.Object { 31 | dictObject := dict.(*object.Dict) 32 | 33 | key, ok := index.(object.Hashable) 34 | if !ok { 35 | return newError("Mstari %d: Samahani, %s haitumiki kama ufunguo", line, index.Type()) 36 | } 37 | 38 | pair, ok := dictObject.Pairs[key.HashKey()] 39 | if !ok { 40 | return NULL 41 | } 42 | 43 | return pair.Value 44 | } 45 | -------------------------------------------------------------------------------- /evaluator/method.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalMethodExpression(node *ast.MethodExpression, env *object.Environment) object.Object { 9 | obj := Eval(node.Object, env) 10 | if isError(obj) { 11 | return obj 12 | } 13 | args := evalExpressions(node.Arguments, env) 14 | if len(args) == 1 && isError(args[0]) { 15 | return args[0] 16 | } 17 | 18 | defs := make(map[string]object.Object) 19 | 20 | for k, v := range node.Defaults { 21 | defs[k] = Eval(v, env) 22 | } 23 | return applyMethod(obj, node.Method, args, defs, node.Token.Line) 24 | } 25 | 26 | func applyMethod(obj object.Object, method ast.Expression, args []object.Object, defs map[string]object.Object, l int) object.Object { 27 | switch obj := obj.(type) { 28 | case *object.String: 29 | return obj.Method(method.(*ast.Identifier).Value, args) 30 | case *object.File: 31 | return obj.Method(method.(*ast.Identifier).Value, args) 32 | case *object.Time: 33 | return obj.Method(method.(*ast.Identifier).Value, args, defs) 34 | case *object.Array: 35 | switch method.(*ast.Identifier).Value { 36 | case "map": 37 | return maap(obj, args) 38 | case "chuja": 39 | return filter(obj, args) 40 | default: 41 | return obj.Method(method.(*ast.Identifier).Value, args) 42 | } 43 | case *object.Module: 44 | if fn, ok := obj.Functions[method.(*ast.Identifier).Value]; ok { 45 | return fn(args, defs) 46 | } 47 | case *object.Instance: 48 | if fn, ok := obj.Package.Scope.Get(method.(*ast.Identifier).Value); ok { 49 | fn.(*object.Function).Env.Set("@", obj) 50 | ret := applyFunction(fn, args, l) 51 | fn.(*object.Function).Env.Del("@") 52 | return ret 53 | } 54 | case *object.Package: 55 | if fn, ok := obj.Scope.Get(method.(*ast.Identifier).Value); ok { 56 | fn.(*object.Function).Env.Set("@", obj) 57 | ret := applyFunction(fn, args, l) 58 | fn.(*object.Function).Env.Del("@") 59 | return ret 60 | } 61 | } 62 | return newError("Samahani, %s haina function '%s()'", obj.Inspect(), method.(*ast.Identifier).Value) 63 | } 64 | 65 | // /////////////////////////////////////////////////////////////// 66 | // //////// Some methods here because of loop dependency //////// 67 | // ///////////////////////////////////////////////////////////// 68 | func maap(a *object.Array, args []object.Object) object.Object { 69 | if len(args) != 1 && args[0].Type() != object.FUNCTION_OBJ { 70 | return newError("Samahani, hoja sii sahihi") 71 | } 72 | 73 | fn, ok := args[0].(*object.Function) 74 | if !ok { 75 | return newError("Samahani, hoja sii sahihi") 76 | } 77 | env := object.NewEnvironment() 78 | newArr := object.Array{Elements: []object.Object{}} 79 | for _, obj := range a.Elements { 80 | env.Set(fn.Parameters[0].Value, obj) 81 | r := Eval(fn.Body, env) 82 | if o, ok := r.(*object.ReturnValue); ok { 83 | r = o.Value 84 | } 85 | newArr.Elements = append(newArr.Elements, r) 86 | } 87 | return &newArr 88 | } 89 | 90 | func filter(a *object.Array, args []object.Object) object.Object { 91 | if len(args) != 1 && args[0].Type() != object.FUNCTION_OBJ { 92 | return newError("Samahani, hoja sii sahihi") 93 | } 94 | 95 | fn, ok := args[0].(*object.Function) 96 | if !ok { 97 | return newError("Samahani, hoja sii sahihi") 98 | } 99 | env := object.NewEnvironment() 100 | newArr := object.Array{Elements: []object.Object{}} 101 | for _, obj := range a.Elements { 102 | env.Set(fn.Parameters[0].Value, obj) 103 | cond := Eval(fn.Body, env) 104 | if cond.Inspect() == "kweli" { 105 | newArr.Elements = append(newArr.Elements, obj) 106 | } 107 | } 108 | return &newArr 109 | } 110 | -------------------------------------------------------------------------------- /evaluator/package.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalPackage(node *ast.Package, env *object.Environment) object.Object { 9 | pakeji := &object.Package{ 10 | Name: node.Name, 11 | Env: env, 12 | Scope: object.NewEnclosedEnvironment(env), 13 | } 14 | 15 | Eval(node.Block, pakeji.Scope) 16 | env.Set(node.Name.Value, pakeji) 17 | return pakeji 18 | } 19 | -------------------------------------------------------------------------------- /evaluator/postfix.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalPostfixExpression(env *object.Environment, operator string, node *ast.PostfixExpression) object.Object { 9 | val, ok := env.Get(node.Token.Literal) 10 | if !ok { 11 | return newError("Tumia KITAMBULISHI CHA NAMBA AU DESIMALI, sio %s", node.Token.Type) 12 | } 13 | switch operator { 14 | case "++": 15 | switch arg := val.(type) { 16 | case *object.Integer: 17 | v := arg.Value + 1 18 | return env.Set(node.Token.Literal, &object.Integer{Value: v}) 19 | case *object.Float: 20 | v := arg.Value + 1 21 | return env.Set(node.Token.Literal, &object.Float{Value: v}) 22 | default: 23 | 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) 24 | 25 | } 26 | case "--": 27 | switch arg := val.(type) { 28 | case *object.Integer: 29 | v := arg.Value - 1 30 | return env.Set(node.Token.Literal, &object.Integer{Value: v}) 31 | case *object.Float: 32 | v := arg.Value - 1 33 | return env.Set(node.Token.Literal, &object.Float{Value: v}) 34 | default: 35 | 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) 36 | } 37 | default: 38 | return newError("Haifahamiki: %s", operator) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /evaluator/prefix.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import "github.com/NuruProgramming/Nuru/object" 4 | 5 | func evalMinusPrefixOperatorExpression(right object.Object, line int) object.Object { 6 | switch obj := right.(type) { 7 | 8 | case *object.Integer: 9 | return &object.Integer{Value: -obj.Value} 10 | 11 | case *object.Float: 12 | return &object.Float{Value: -obj.Value} 13 | 14 | default: 15 | return newError("Mstari %d: Operesheni Haieleweki: -%s", line, right.Type()) 16 | } 17 | } 18 | func evalPlusPrefixOperatorExpression(right object.Object, line int) object.Object { 19 | switch obj := right.(type) { 20 | 21 | case *object.Integer: 22 | return &object.Integer{Value: obj.Value} 23 | 24 | case *object.Float: 25 | return &object.Float{Value: obj.Value} 26 | 27 | default: 28 | return newError("Mstari %d: Operesheni Haieleweki: +%s", line, right.Type()) 29 | } 30 | } 31 | 32 | func evalPrefixExpression(operator string, right object.Object, line int) object.Object { 33 | switch operator { 34 | case "!": 35 | return evalBangOperatorExpression(right) 36 | case "-": 37 | return evalMinusPrefixOperatorExpression(right, line) 38 | case "+": 39 | return evalPlusPrefixOperatorExpression(right, line) 40 | default: 41 | return newError("Mstari %d: Operesheni Haieleweki: %s%s", line, operator, right.Type()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /evaluator/property.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalPropertyExpression(node *ast.PropertyExpression, env *object.Environment) object.Object { 9 | left := Eval(node.Object, env) 10 | if isError(left) { 11 | return left 12 | } 13 | switch left.(type) { 14 | case *object.Instance: 15 | obj := left.(*object.Instance) 16 | prop := node.Property.(*ast.Identifier).Value 17 | if val, ok := obj.Env.Get(prop); ok { 18 | return val 19 | } 20 | case *object.Package: 21 | obj := left.(*object.Package) 22 | prop := node.Property.(*ast.Identifier).Value 23 | if val, ok := obj.Env.Get(prop); ok { 24 | return val 25 | } 26 | // case *object.Module: 27 | // mod := left.(*object.Module) 28 | // prop := node.Property.(*ast.Identifier).Value 29 | // if val, ok := mod.Properties[prop]; ok { 30 | // return val() 31 | // } 32 | } 33 | return newError("Value %s sii sahihi kwenye %s", node.Property.(*ast.Identifier).Value, left.Inspect()) 34 | } 35 | 36 | func evalPropertyAssignment(name *ast.PropertyExpression, val object.Object, env *object.Environment) object.Object { 37 | left := Eval(name.Object, env) 38 | if isError(left) { 39 | return left 40 | } 41 | switch left.(type) { 42 | case *object.Instance: 43 | obj := left.(*object.Instance) 44 | prop := name.Property.(*ast.Identifier).Value 45 | if _, ok := obj.Env.Get(prop); ok { 46 | obj.Env.Set(prop, val) 47 | return NULL 48 | } 49 | obj.Env.Set(prop, val) 50 | return NULL 51 | case *object.Package: 52 | obj := left.(*object.Package) 53 | prop := name.Property.(*ast.Identifier).Value 54 | if _, ok := obj.Env.Get(prop); ok { 55 | obj.Env.Set(prop, val) 56 | return NULL 57 | } 58 | obj.Env.Set(prop, val) 59 | return NULL 60 | default: 61 | return newError("Imeshindikana kuweka kwenye pakiti %s", left.Type()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /evaluator/switch.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalSwitchStatement(se *ast.SwitchExpression, env *object.Environment) object.Object { 9 | obj := Eval(se.Value, env) 10 | for _, opt := range se.Choices { 11 | 12 | if opt.Default { 13 | continue 14 | } 15 | for _, val := range opt.Expr { 16 | out := Eval(val, env) 17 | if obj.Type() == out.Type() && obj.Inspect() == out.Inspect() { 18 | blockOut := evalBlockStatement(opt.Block, env) 19 | return blockOut 20 | } 21 | } 22 | } 23 | for _, opt := range se.Choices { 24 | if opt.Default { 25 | out := evalBlockStatement(opt.Block, env) 26 | return out 27 | } 28 | } 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /evaluator/type.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/NuruProgramming/Nuru/object" 7 | ) 8 | 9 | func convertToInteger(obj object.Object) object.Object { 10 | switch obj := obj.(type) { 11 | case *object.Integer: 12 | return obj 13 | case *object.Float: 14 | return &object.Integer{Value: int64(obj.Value)} 15 | case *object.String: 16 | i, err := strconv.ParseInt(obj.Value, 10, 64) 17 | if err != nil { 18 | return newError("Haiwezi kubadilisha '%s' kuwa NAMBA", obj.Value) 19 | } 20 | return &object.Integer{Value: i} 21 | case *object.Boolean: 22 | if obj.Value { 23 | return &object.Integer{Value: 1} 24 | } 25 | return &object.Integer{Value: 0} 26 | default: 27 | return newError("Haiwezi kubadilisha %s kuwa NAMBA", obj.Type()) 28 | } 29 | } 30 | 31 | func convertToFloat(obj object.Object) object.Object { 32 | switch obj := obj.(type) { 33 | case *object.Float: 34 | return obj 35 | case *object.Integer: 36 | return &object.Float{Value: float64(obj.Value)} 37 | case *object.String: 38 | f, err := strconv.ParseFloat(obj.Value, 64) 39 | if err != nil { 40 | return newError("Haiwezi kubadilisha '%s' kuwa DESIMALI", obj.Value) 41 | } 42 | return &object.Float{Value: f} 43 | case *object.Boolean: 44 | if obj.Value { 45 | return &object.Float{Value: 1.0} 46 | } 47 | return &object.Float{Value: 0.0} 48 | default: 49 | return newError("Haiwezi kubadilisha %s kuwa DESIMALI", obj.Type()) 50 | } 51 | } 52 | 53 | func convertToString(obj object.Object) object.Object { 54 | return &object.String{Value: obj.Inspect()} 55 | } 56 | 57 | func convertToBoolean(obj object.Object) object.Object { 58 | switch obj := obj.(type) { 59 | case *object.Boolean: 60 | return obj 61 | case *object.Integer: 62 | return &object.Boolean{Value: obj.Value != 0} 63 | case *object.Float: 64 | return &object.Boolean{Value: obj.Value != 0} 65 | case *object.String: 66 | return &object.Boolean{Value: len(obj.Value) > 0} 67 | case *object.Null: 68 | return &object.Boolean{Value: false} 69 | default: 70 | return &object.Boolean{Value: true} 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /evaluator/while.go: -------------------------------------------------------------------------------- 1 | package evaluator 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/object" 6 | ) 7 | 8 | func evalWhileExpression(we *ast.WhileExpression, env *object.Environment) object.Object { 9 | condition := Eval(we.Condition, env) 10 | var evaluated object.Object 11 | if isError(condition) { 12 | return condition 13 | } 14 | if isTruthy(condition) { 15 | evaluated = Eval(we.Consequence, env) 16 | if isError(evaluated) { 17 | return evaluated 18 | } 19 | if evaluated != nil && evaluated.Type() == object.BREAK_OBJ { 20 | return evaluated 21 | } 22 | evaluated = evalWhileExpression(we, env) 23 | } 24 | return evaluated 25 | } 26 | -------------------------------------------------------------------------------- /examples/Astart.nr: -------------------------------------------------------------------------------- 1 | /*############ A*(A-star) Algorithm ############## 2 | 3 | By @VictorKariuki 4 | 5 | https://github.com/VictorKariuki 6 | 7 | ################################################*/ 8 | 9 | 10 | // create a list of numbers 11 | fanya list = unda(first,last,interval){ 12 | fanya list = [first]; 13 | fanya i = first + interval; 14 | wakati(i < last){ 15 | list.sukuma(i) 16 | i+=interval; 17 | } 18 | rudisha list; 19 | } 20 | 21 | // Maths functions 22 | // find the absolute value of a number 23 | fanya abs_namba = unda(namba){ 24 | kama(namba < 0){ 25 | rudisha -1 * namba; 26 | } 27 | 28 | rudisha namba; 29 | } 30 | 31 | // square a number 32 | fanya square = unda(n, i, j){ 33 | fanya kati = (i+j)/2; 34 | fanya mul = kati * kati; 35 | fanya abs_diff = abs_namba(mul-n); 36 | 37 | kama (mul == n || abs_diff < 0.00001){ 38 | rudisha kati; 39 | }au kama(mul < n){ 40 | rudisha square(n,kati,j) 41 | }au{ 42 | rudisha square(n,i,kati) 43 | } 44 | } 45 | 46 | // find the square root of a number 47 | fanya sqrt = unda(namba){ 48 | kwa i ktk list(0,namba,1) { 49 | kama((i*i )== namba){ 50 | rudisha i; 51 | }au kama ((i*i )> namba){ 52 | rudisha square(namba,i-1,i) 53 | } 54 | } 55 | } 56 | 57 | // Main function 58 | fanya aStar = unda(start, goal) { 59 | // Initialize the open and closed lists 60 | fanya openList = [start]; 61 | fanya closedList = []; 62 | 63 | fanya reconstructPath = unda(node) { 64 | fanya path = [node]; 65 | wakati (node["parent"]) { 66 | path = [node["parent"]] + path; 67 | node = node["parent"]; 68 | } 69 | rudisha path; 70 | } 71 | 72 | fanya heuristic = unda(node1, node2) { 73 | // Calculate the Euclidean distance between the nodes' positions 74 | fanya dx = node1["x"] - node2["x"]; 75 | fanya dy = node1["y"] - node2["y"]; 76 | rudisha sqrt(dx * dx + dy * dy); 77 | } 78 | 79 | fanya findMinNode = unda(openList) { 80 | fanya i = 1; 81 | fanya minNode = openList[0]; 82 | 83 | wakati (i < openList.idadi()) { 84 | fanya node = openList[i]; 85 | kama (node["f"] < minNode["f"]) { 86 | minNode = node; 87 | } 88 | i++ 89 | } 90 | 91 | rudisha minNode; 92 | } 93 | 94 | fanya removeNodeFromArray = unda(array, node) { 95 | fanya newArray = []; 96 | fanya i = 1; 97 | wakati (i < array.idadi()) { 98 | kama (array[i] != node) { 99 | newArray.sukuma(array[i]); 100 | } 101 | i++; 102 | } 103 | rudisha newArray; 104 | } 105 | 106 | fanya urefu = unda(node1, node2) { 107 | // Assume all edges have a cost of 1 108 | rudisha 1; 109 | } 110 | 111 | // Initialize the g and f scores of the starting node 112 | start["g"] = 0; 113 | start["f"] = start["g"] + heuristic(start, goal); 114 | 115 | 116 | 117 | // Start the search loop 118 | wakati (openList.idadi() > 0) { 119 | // Find the node with the lowest f score in the open list 120 | fanya current = findMinNode(openList); 121 | 122 | // Check kama the goal node has been reached 123 | kama (current == goal) { 124 | rudisha reconstructPath(current); 125 | } 126 | 127 | // Move the current node from the open to the closed list 128 | openList = removeNodeFromArray(openList, current); 129 | 130 | closedList.sukuma(current); 131 | 132 | // Explore the neighbors of the current node 133 | kwa neighbor ktk current["neighbors"] { 134 | // Skip neighbors that are in the closed list 135 | kama (neighbor ktk closedList) { 136 | endelea 137 | } 138 | 139 | // Calculate the tentative g score of the neighbor 140 | fanya tentativeG = start["g"] + urefu(current, neighbor); 141 | 142 | // Check kama the neighbor is in the open list 143 | fanya tentativeIsBetter = sikweli; 144 | kama (!(neighbor ktk openList)) { 145 | openList.sukuma(neighbor); 146 | tentativeIsBetter = kweli; 147 | } au kama (tentativeG < neighbor["g"]) { 148 | tentativeIsBetter = kweli; 149 | } 150 | 151 | // Update the neighbor's g score kama the tentative score is better 152 | kama (tentativeIsBetter) { 153 | neighbor["g"] = tentativeG; 154 | neighbor["f"] = neighbor["g"] + heuristic(neighbor, goal); 155 | neighbor["parent"] = current; 156 | } 157 | } 158 | } 159 | 160 | // kama the open list is empty, no path was found 161 | rudisha tupu; 162 | } 163 | 164 | // Define the nodes of the graph 165 | fanya nodeA = { "x": 0, "y": 0, "neighbors": [] }; 166 | fanya nodeB = { "x": 1, "y": 2, "neighbors": [] }; 167 | fanya nodeC = { "x": 3, "y": 1, "neighbors": [] }; 168 | fanya nodeD = { "x": 4, "y": 3, "neighbors": [] }; 169 | 170 | // Define the edges between the nodes 171 | nodeA["neighbors"] = [nodeB]; 172 | nodeB["neighbors"] = [nodeA, nodeC]; 173 | nodeC["neighbors"] = [nodeB, nodeD]; 174 | nodeD["neighbors"] = [nodeC]; 175 | 176 | // Call the A* function with the start and goal nodes and the heuristic and distance functions 177 | //fanya path = aStar(nodeA, nodeC); 178 | 179 | andika(nodeA); -------------------------------------------------------------------------------- /examples/reduce.nr: -------------------------------------------------------------------------------- 1 | fanya reduce = unda(iterator, callback, initialValue) { 2 | fanya accumulator = initialValue; 3 | 4 | kwa thamani ktk iterator { 5 | accumulator = callback(accumulator, thamani); 6 | } 7 | 8 | rudisha accumulator; 9 | } 10 | 11 | fanya list = [1,2,3,4,5]; 12 | fanya employees = [{"salary":120},{"salary":135},{"salary":140}] 13 | 14 | fanya sum = unda(acc,value){ 15 | rudisha acc + value; 16 | } 17 | 18 | fanya mul = unda(acc,value){ 19 | rudisha acc * value; 20 | } 21 | 22 | fanya sumSalo = unda(acc,value){ 23 | rudisha acc + value["salary"]; 24 | } 25 | 26 | fanya sumSaloWithTax = unda(acc,value){ 27 | rudisha acc + (value["salary"] * (1-0.34)); 28 | } 29 | 30 | andika(reduce(list,sum,0)) 31 | andika(reduce(list,mul,1)) 32 | 33 | andika(reduce(employees,sumSalo,0)) 34 | andika(reduce(employees,sumSaloWithTax,0)) -------------------------------------------------------------------------------- /examples/sarufi.nr: -------------------------------------------------------------------------------- 1 | tumia mtandao 2 | tumia jsoni 3 | pakeji sarufi { 4 | andaa = unda(file) { 5 | config = fungua(file) 6 | configString = config.soma() 7 | configDict = jsoni.dikodi(configString) 8 | clientID = configDict["client_id"] 9 | clientSecret = configDict["client_secret"] 10 | params = {"client_id": clientID, "client_secret": clientSecret} 11 | tokenString = mtandao.tuma(yuareli="https://api.sarufi.io/api/access_token", mwili=params) 12 | tokenDict = jsoni.dikodi(tokenString) 13 | @.token = tokenDict["access_token"] 14 | @.Auth = "Bearer " + @.token 15 | } 16 | 17 | tokenYangu = unda() { 18 | rudisha @.token 19 | } 20 | 21 | tengenezaChatbot = unda(data) { 22 | majibu = mtandao.tuma(yuareli="https://api.sarufi.io/chatbot", vichwa={"Authorization": @.Auth}, mwili = data) 23 | rudisha majibu 24 | } 25 | 26 | pataChatbotZote = unda() { 27 | majibu = mtandao.peruzi(yuareli="https://api.sarufi.io/chatbots", vichwa={"Authorization": @.Auth}) 28 | rudisha majibu 29 | } 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/sorting_algorithm.nr: -------------------------------------------------------------------------------- 1 | /* 2 | ############ Sorting Algorithm ############## 3 | 4 | By @VictorKariuki 5 | 6 | https://github.com/VictorKariuki 7 | 8 | ############################################# 9 | */ 10 | 11 | slice = unda(arr,start, end) { 12 | result = [] 13 | wakati (start < end) { 14 | result = result + [arr[start]] 15 | start = start + 1 16 | } 17 | rudisha result 18 | } 19 | 20 | merge = unda(left, right) { 21 | result = [] 22 | lLen = left.idadi() 23 | rLen = right.idadi() 24 | l = 0 25 | r = 0 26 | wakati (l < lLen && r < rLen) { 27 | kama (left[l] < right[r]) { 28 | result = result + [left[l]] 29 | l = l + 1 30 | } sivyo { 31 | result = result + [right[r]] 32 | r = r + 1 33 | } 34 | } 35 | andika(result) 36 | } 37 | 38 | 39 | mergeSort = unda(arr){ 40 | len = arr.idadi() 41 | andika("arr is ", arr," of length ", len) 42 | kama (len < 2) { 43 | rudisha arr 44 | } 45 | andika("len is greater than or == to 2", len > 1) 46 | 47 | mid = (len / 2) 48 | andika("arr has a mid point of ", mid) 49 | 50 | left = slice(arr, 0, mid) 51 | right = slice(arr, mid, len) 52 | andika("left slice is ", left) 53 | andika("right slice is ", right) 54 | sortedLeft = mergeSort(left) 55 | sortedRight = mergeSort(right) 56 | andika("sortedLeft is ", sortedLeft) 57 | andika("sortedRight is ", sortedRight) 58 | rudisha merge(sortedLeft, sortedRight) 59 | } 60 | 61 | arr = [6, 5, 3, 1, 8, 7, 2, 4] 62 | sortedArray = mergeSort(arr) 63 | andika(sortedArray) 64 | -------------------------------------------------------------------------------- /examples/sudoku_solver.nr: -------------------------------------------------------------------------------- 1 | /*########### Backtracking Algorithm ############## 2 | 3 | By @VictorKariuki 4 | 5 | https://github.com/VictorKariuki 6 | 7 | NURU program to solve Sudoku using Backtracking Algorithm 8 | 9 | The sudoku puzzle is represented as a 2D array. The empty 10 | cells are represented by 0. The algorithm works by trying 11 | out all possible numbers for an empty cell. If the number 12 | is valid, it is placed in the cell. If the number is invalid, 13 | the algorithm backtracks to the previous cell and tries 14 | another number. The algorithm terminates when all cells 15 | are filled. The algorithm is implemented in the solveSudoku 16 | function. The isValid function checks kama a number is 17 | valid in a given cell. The printSudoku function prints 18 | the sudoku puzzle. The solveSudoku function solves the 19 | sudoku puzzle. The main function initializes the sudoku 20 | puzzle and calls the solveSudoku function. 21 | 22 | #################################################*/ 23 | 24 | 25 | fanya printing = unda(sudoku) { 26 | fanya row = 0 27 | wakati (row < 9){ 28 | andika(sudoku[row]) 29 | row++ 30 | } 31 | } 32 | 33 | 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]] 34 | 35 | 36 | 37 | fanya isSafe = unda(grid, row, col, num) { 38 | kwa x ktk [0,1,2,3,4,5,6,7,8] { 39 | kama (grid[row][x] == num) { 40 | rudisha sikweli 41 | } 42 | } 43 | 44 | kwa x ktk [0,1,2,3,4,5,6,7,8] { 45 | kama (grid[x][col] == num) { 46 | rudisha sikweli 47 | } 48 | } 49 | 50 | fanya startRow = row - row % 3 51 | fanya startCol = col - col % 3 52 | 53 | kwa i ktk [0, 1, 2] { 54 | kwa j ktk [0, 1, 2] { 55 | kama (grid[i + startRow][j + startCol] == num) { 56 | rudisha sikweli 57 | } 58 | } 59 | } 60 | 61 | rudisha kweli 62 | } 63 | 64 | fanya solveSudoku = unda(grid, row, col) { 65 | kama (row == 8 && col == 9) { 66 | rudisha kweli 67 | } 68 | 69 | kama (col == 9) { 70 | row += 1 71 | col = 0 72 | } 73 | 74 | kama (grid[row][col] > 0) { 75 | rudisha solveSudoku(grid, row, col + 1) 76 | } 77 | 78 | kwa num ktk [1,2,3,4,5,6,7,8,9] { 79 | kama (isSafe(grid, row, col, num)) { 80 | grid[row][col] = num 81 | kama (solveSudoku(grid, row, col + 1)) { 82 | rudisha kweli 83 | } 84 | } 85 | 86 | grid[row][col] = 0 87 | } 88 | 89 | rudisha sikweli 90 | } 91 | andika() 92 | andika("----- PUZZLE TO SOLVE -----") 93 | printing(sudoku) 94 | kama (solveSudoku(sudoku, 0, 0)){ 95 | andika() 96 | andika("--------- SOLUTION --------") 97 | printing(sudoku) 98 | andika() 99 | } sivyo { 100 | andika("imeshindikana") 101 | } -------------------------------------------------------------------------------- /extensions/README.md: -------------------------------------------------------------------------------- 1 | # Nuru Extensions For Various Editors 2 | 3 | ## [VSCODE](./vscode/) 4 | 5 | Nuru syntax highlighting on VSCode 6 | 7 | ## [VIM](./vim) 8 | 9 | The file contained herein has a basic syntax highlight for vim. 10 | The file should be saved in `$HOME/.vim/syntax/nuru.vim`. 11 | You should add the following line to your `.vimrc` or the appropriate location: 12 | 13 | ```vim 14 | au BufRead,BufNewFile *.nr set filetype=nuru 15 | ``` 16 | 17 | Only basic syntax highlighting is provided by the script. 18 | -------------------------------------------------------------------------------- /extensions/vim/syntax/nuru.vim: -------------------------------------------------------------------------------- 1 | " Sintaksia ya nuru kwenye programu ya "vim" 2 | " Lugha: Nuru 3 | 4 | " Maneno tengwa 5 | syntax keyword nuruKeyword unda pakeji rudisha vunja endelea tupu 6 | syntax keyword nuruType fanya 7 | syntax keyword nuruBool kweli sikweli 8 | syntax keyword nuruConditional kama sivyo au 9 | syntax match nuruComparision /[!\|<>]/ 10 | syntax keyword nuruLoop ktk while badili 11 | syntax keyword nuruLabel ikiwa kawaida 12 | 13 | " Nambari 14 | syntax match nuruInt '[+-]\d\+' contained display 15 | syntax match nuruFloat '[+-]\d+\.\d*' contained display 16 | 17 | " Viendeshaji 18 | syntax match nuruAssignment '=' 19 | syntax match nuruLogicalOP /[\&!|]/ 20 | 21 | " Vitendakazi 22 | syntax keyword nuruFunction andika aina jaza fungua 23 | 24 | " Tungo 25 | syntax region nuruString start=/"/ skip=/\\"/ end=/"/ 26 | syntax region nuruString start=/'/ skip=/\\'/ end=/'/ 27 | 28 | " Maoni 29 | syntax match nuruComment "//.*" 30 | syntax region nuruComment start="/\*" end="\*/" 31 | 32 | " Fafanua sintaksia 33 | let b:current_syntax = "nuru" 34 | 35 | highlight def link nuruComment Comment 36 | highlight def link nuruBool Boolean 37 | highlight def link nuruFunction Function 38 | highlight def link nuruComparision Conditional 39 | highlight def link nuruConditional Conditional 40 | highlight def link nuruKeyword Keyword 41 | highlight def link nuruString String 42 | highlight def link nuruVariable Identifier 43 | highlight def link nuruLoop Repeat 44 | highlight def link nuruInt Number 45 | highlight def link nuruFloat Float 46 | highlight def link nuruAssignment Operator 47 | highlight def link nuruLogicalOP Operator 48 | highlight def link nuruAriOP Operator 49 | highlight def link nuruType Type 50 | highlight def link nuruLabel Label 51 | 52 | -------------------------------------------------------------------------------- /extensions/vscode/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "nuru" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /extensions/vscode/README.md: -------------------------------------------------------------------------------- 1 | # Nuru VSCode Extension 2 | 3 | This is a syntax highliting extension for Nuru on vscode. It detects `.nr` and `.sw` files. 4 | 5 | ## Screenshots 6 |

7 | Nuru Programming Language 8 |

9 | 10 | ## How To Install 11 | 12 | ### Download From Market Place 13 | 14 | - Simply download the Nuru Extension from VSCode Market Place 15 | 16 | ### Windows 17 | 18 | - 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` 19 | - Restart VSCode 20 | 21 | ### Linux and MacOS 22 | 23 | - 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` 24 | - Restart VSCode 25 | -------------------------------------------------------------------------------- /extensions/vscode/assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NuruProgramming/Nuru/0805bb2df91dda4040be58160f8db114cc84764f/extensions/vscode/assets/screenshot.png -------------------------------------------------------------------------------- /extensions/vscode/nuru/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "//", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ 7 | "/*", 8 | "*/" 9 | ] 10 | }, 11 | // symbols used as brackets 12 | "brackets": [ 13 | [ 14 | "{", 15 | "}" 16 | ], 17 | [ 18 | "[", 19 | "]" 20 | ], 21 | [ 22 | "(", 23 | ")" 24 | ] 25 | ], 26 | // symbols that are auto closed when typing 27 | "autoClosingPairs": [ 28 | [ 29 | "{", 30 | "}" 31 | ], 32 | [ 33 | "[", 34 | "]" 35 | ], 36 | [ 37 | "(", 38 | ")" 39 | ], 40 | [ 41 | "\"", 42 | "\"" 43 | ], 44 | [ 45 | "'", 46 | "'" 47 | ] 48 | ], 49 | // symbols that can be used to surround a selection 50 | "surroundingPairs": [ 51 | [ 52 | "{", 53 | "}" 54 | ], 55 | [ 56 | "[", 57 | "]" 58 | ], 59 | [ 60 | "(", 61 | ")" 62 | ], 63 | [ 64 | "\"", 65 | "\"" 66 | ], 67 | [ 68 | "'", 69 | "'" 70 | ] 71 | ] 72 | } -------------------------------------------------------------------------------- /extensions/vscode/nuru/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuru", 3 | "displayName": "Nuru", 4 | "description": "Nuru Programming Language", 5 | "version": "0.0.1", 6 | "engines": { 7 | "vscode": "^1.74.0" 8 | }, 9 | "categories": [ 10 | "Programming Languages" 11 | ], 12 | "contributes": { 13 | "languages": [ 14 | { 15 | "id": "nuru", 16 | "aliases": [ 17 | "Nuru Programming Language", 18 | "nuru" 19 | ], 20 | "extensions": [ 21 | ".nr", 22 | ".sw" 23 | ], 24 | "configuration": "./language-configuration.json" 25 | } 26 | ], 27 | "grammars": [ 28 | { 29 | "language": "nuru", 30 | "scopeName": "source.nr", 31 | "path": "./syntaxes/nuru.tmLanguage.json" 32 | } 33 | ] 34 | } 35 | } -------------------------------------------------------------------------------- /extensions/vscode/nuru/syntaxes/nuru.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "Nuru Programming Language", 4 | "scopeName": "source.nr", 5 | "fileTypes": [ 6 | "nr" 7 | ], 8 | "patterns": [ 9 | { 10 | "include": "#linecomments" 11 | }, 12 | { 13 | "include": "#blockcomments" 14 | }, 15 | { 16 | "include": "#operators" 17 | }, 18 | { 19 | "include": "#punctuation" 20 | }, 21 | { 22 | "include": "#numbers" 23 | }, 24 | { 25 | "include": "#constant" 26 | }, 27 | { 28 | "include": "#keywords" 29 | }, 30 | { 31 | "include": "#strings" 32 | }, 33 | { 34 | "include": "#identifiers" 35 | } 36 | ], 37 | "repository": { 38 | "linecomments": { 39 | "name": "comment.line.double-slash.nuru", 40 | "match": "(//).*$\n?", 41 | "captures": { 42 | "1": { 43 | "name": "punctuation.definition.comment.nuru" 44 | } 45 | } 46 | }, 47 | "blockcomments": { 48 | "name": "comment.block.nuru", 49 | "begin": "/\\*", 50 | "end": "\\*/", 51 | "captures": { 52 | "0": { 53 | "name": "punctuation.definition.comment.nuru" 54 | } 55 | } 56 | }, 57 | "numbers": { 58 | "name": "constant.numeric.nuru", 59 | "match": "\\b[0-9]+(\\.[0-9]+)?\\b" 60 | }, 61 | "constant": { 62 | "name": "constant.language.nuru", 63 | "match": "\\b(kweli|sikweli)\\b" 64 | }, 65 | "operators": { 66 | "patterns": [ 67 | { 68 | "name": "keyword.operator.arithmetic.nuru", 69 | "match": "\\b(\\+|\\-|%|\\*|\\/|\\^)\\b" 70 | }, 71 | { 72 | "name": "keyword.operator.logical.nuru", 73 | "match": "\\b(==|<=|>=|<|>|&&|\\|\\|)\\b" 74 | }, 75 | { 76 | "name": "keyword.operator.assignment.nuru", 77 | "match": "\\b(=|:)\\b" 78 | }, 79 | { 80 | "name": "punctuation.accessor.nuru", 81 | "match": "\\." 82 | } 83 | ] 84 | }, 85 | "punctuation": { 86 | "patterns": [ 87 | { 88 | "name": "punctuation.separator", 89 | "match": "," 90 | }, 91 | { 92 | "include": "#groups" 93 | } 94 | ] 95 | }, 96 | "keywords": { 97 | "patterns": [ 98 | { 99 | "name": "storage.type.function.nuru", 100 | "match": "\\b(unda|andika|idadi|jumla|yamwisho|sukuma|jaza|aina|fungua)\\b" 101 | }, 102 | { 103 | "name": "storage.type.nuru", 104 | "match": "\\bfanya\\b" 105 | }, 106 | { 107 | "name": "keyword.control.nuru", 108 | "match": "\\b(kama|au|sivyo|wakati|rudisha|vunja|endelea|tupu|ktk|kwa|badili|ikiwa|kawaida|tumia)\\b" 109 | }, 110 | { 111 | "name": "support.function.nuru", 112 | "match": "\\b(os|muda)\\b" 113 | } 114 | ] 115 | }, 116 | "identifiers": { 117 | "patterns": [ 118 | { 119 | "name": "meta.functioncall.nuru", 120 | "match": "\\b([_A-Za-z][_A-Za-z0-9]*)\\b(?=\\()", 121 | "captures": { 122 | "1": { 123 | "name": "entity.name.function.nuru" 124 | } 125 | } 126 | } 127 | ] 128 | }, 129 | "strings": { 130 | "patterns": [ 131 | { 132 | "name": "string.quoted.double.nuru", 133 | "begin": "\"", 134 | "end": "\"", 135 | "patterns": [ 136 | { 137 | "name": "constant.character.escape.nuru", 138 | "match": "\\\\." 139 | } 140 | ] 141 | }, 142 | { 143 | "name": "string.quoted.single.nuru", 144 | "begin": "\\'", 145 | "end": "\\'", 146 | "patterns": [ 147 | { 148 | "name": "constant.character.escape.nuru", 149 | "match": "\\\\." 150 | } 151 | ] 152 | } 153 | ] 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/NuruProgramming/Nuru 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/AvicennaJr/GoPrompt v0.0.0-20230411215003-be2316d88e2d 7 | github.com/charmbracelet/bubbles v0.15.0 8 | github.com/charmbracelet/bubbletea v0.23.2 9 | github.com/charmbracelet/glamour v0.6.0 10 | github.com/charmbracelet/lipgloss v0.7.1 11 | github.com/lrstanley/bubblezone v0.0.0-20230303230241-08f906ff62a9 12 | ) 13 | 14 | require ( 15 | github.com/alecthomas/chroma v0.10.0 // indirect 16 | github.com/atotto/clipboard v0.1.4 // indirect 17 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 18 | github.com/aymerick/douceur v0.2.0 // indirect 19 | github.com/containerd/console v1.0.3 // indirect 20 | github.com/dlclark/regexp2 v1.4.0 // indirect 21 | github.com/gorilla/css v1.0.0 // indirect 22 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 23 | github.com/mattn/go-colorable v0.1.7 // indirect 24 | github.com/mattn/go-isatty v0.0.18 // indirect 25 | github.com/mattn/go-localereader v0.0.1 // indirect 26 | github.com/mattn/go-runewidth v0.0.14 // indirect 27 | github.com/mattn/go-tty v0.0.3 // indirect 28 | github.com/microcosm-cc/bluemonday v1.0.21 // indirect 29 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect 30 | github.com/muesli/cancelreader v0.2.2 // indirect 31 | github.com/muesli/reflow v0.3.0 // indirect 32 | github.com/muesli/termenv v0.15.1 // indirect 33 | github.com/olekukonko/tablewriter v0.0.5 // indirect 34 | github.com/pkg/term v1.2.0-beta.2 // indirect 35 | github.com/rivo/uniseg v0.4.4 // indirect 36 | github.com/sahilm/fuzzy v0.1.0 // indirect 37 | github.com/yuin/goldmark v1.5.2 // indirect 38 | github.com/yuin/goldmark-emoji v1.0.1 // indirect 39 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b // indirect 40 | golang.org/x/sync v0.1.0 // indirect 41 | golang.org/x/sys v0.7.0 // indirect 42 | golang.org/x/term v0.7.0 // indirect 43 | golang.org/x/text v0.9.0 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /gotest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NuruProgramming/Nuru/0805bb2df91dda4040be58160f8db114cc84764f/gotest -------------------------------------------------------------------------------- /lexer/lexer_test.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/NuruProgramming/Nuru/token" 7 | ) 8 | 9 | func TestNextToken(t *testing.T) { 10 | input := ` 11 | // Testing kama lex luther iko sawa 12 | fanya tano = 5; 13 | fanya kumi = 10; 14 | 15 | fanya jumla = unda(x, y){ 16 | x + y; 17 | }; 18 | 19 | fanya jibu = jumla(tano, kumi); 20 | 21 | !-/5; 22 | 5 < 10 > 5; 23 | 24 | kama (5 < 10) { 25 | rudisha kweli; 26 | } sivyo { 27 | rudisha sikweli; 28 | } 29 | 30 | 10 == 10; 31 | 10 != 9; // Hii ni comment 32 | // Comment nyingine 33 | 34 | /* 35 | multiline comment 36 | */ 37 | 38 | /* multiline comment number twooooooooooo */ 39 | 5 40 | "bangi" 41 | "ba ngi" 42 | [1, 2]; 43 | {"mambo": "vipi"} 44 | . // test dot 45 | tumia muda 46 | 47 | badili (a) { 48 | ikiwa 2 { 49 | andika(2) 50 | } 51 | kawaida { 52 | andika(0) 53 | } 54 | } 55 | 56 | tupu 57 | 58 | kwa i, v ktk j` 59 | 60 | tests := []struct { 61 | expectedType token.TokenType 62 | expectedLiteral string 63 | }{ 64 | {token.LET, "fanya"}, 65 | {token.IDENT, "tano"}, 66 | {token.ASSIGN, "="}, 67 | {token.INT, "5"}, 68 | {token.SEMICOLON, ";"}, 69 | {token.LET, "fanya"}, 70 | {token.IDENT, "kumi"}, 71 | {token.ASSIGN, "="}, 72 | {token.INT, "10"}, 73 | {token.SEMICOLON, ";"}, 74 | {token.LET, "fanya"}, 75 | {token.IDENT, "jumla"}, 76 | {token.ASSIGN, "="}, 77 | {token.FUNCTION, "unda"}, 78 | {token.LPAREN, "("}, 79 | {token.IDENT, "x"}, 80 | {token.COMMA, ","}, 81 | {token.IDENT, "y"}, 82 | {token.RPAREN, ")"}, 83 | {token.LBRACE, "{"}, 84 | {token.IDENT, "x"}, 85 | {token.PLUS, "+"}, 86 | {token.IDENT, "y"}, 87 | {token.SEMICOLON, ";"}, 88 | {token.RBRACE, "}"}, 89 | {token.SEMICOLON, ";"}, 90 | {token.LET, "fanya"}, 91 | {token.IDENT, "jibu"}, 92 | {token.ASSIGN, "="}, 93 | {token.IDENT, "jumla"}, 94 | {token.LPAREN, "("}, 95 | {token.IDENT, "tano"}, 96 | {token.COMMA, ","}, 97 | {token.IDENT, "kumi"}, 98 | {token.RPAREN, ")"}, 99 | {token.SEMICOLON, ";"}, 100 | {token.BANG, "!"}, 101 | {token.MINUS, "-"}, 102 | {token.SLASH, "/"}, 103 | {token.INT, "5"}, 104 | {token.SEMICOLON, ";"}, 105 | {token.INT, "5"}, 106 | {token.LT, "<"}, 107 | {token.INT, "10"}, 108 | {token.GT, ">"}, 109 | {token.INT, "5"}, 110 | {token.SEMICOLON, ";"}, 111 | {token.IF, "kama"}, 112 | {token.LPAREN, "("}, 113 | {token.INT, "5"}, 114 | {token.LT, "<"}, 115 | {token.INT, "10"}, 116 | {token.RPAREN, ")"}, 117 | {token.LBRACE, "{"}, 118 | {token.RETURN, "rudisha"}, 119 | {token.TRUE, "kweli"}, 120 | {token.SEMICOLON, ";"}, 121 | {token.RBRACE, "}"}, 122 | {token.ELSE, "sivyo"}, 123 | {token.LBRACE, "{"}, 124 | {token.RETURN, "rudisha"}, 125 | {token.FALSE, "sikweli"}, 126 | {token.SEMICOLON, ";"}, 127 | {token.RBRACE, "}"}, 128 | {token.INT, "10"}, 129 | {token.EQ, "=="}, 130 | {token.INT, "10"}, 131 | {token.SEMICOLON, ";"}, 132 | {token.INT, "10"}, 133 | {token.NOT_EQ, "!="}, 134 | {token.INT, "9"}, 135 | {token.SEMICOLON, ";"}, 136 | {token.INT, "5"}, 137 | {token.STRING, "bangi"}, 138 | {token.STRING, "ba ngi"}, 139 | {token.LBRACKET, "["}, 140 | {token.INT, "1"}, 141 | {token.COMMA, ","}, 142 | {token.INT, "2"}, 143 | {token.RBRACKET, "]"}, 144 | {token.SEMICOLON, ";"}, 145 | {token.LBRACE, "{"}, 146 | {token.STRING, "mambo"}, 147 | {token.COLON, ":"}, 148 | {token.STRING, "vipi"}, 149 | {token.RBRACE, "}"}, 150 | {token.DOT, "."}, 151 | {token.IMPORT, "tumia"}, 152 | {token.IDENT, "muda"}, 153 | {token.SWITCH, "badili"}, 154 | {token.LPAREN, "("}, 155 | {token.IDENT, "a"}, 156 | {token.RPAREN, ")"}, 157 | {token.LBRACE, "{"}, 158 | {token.CASE, "ikiwa"}, 159 | {token.INT, "2"}, 160 | {token.LBRACE, "{"}, 161 | {token.IDENT, "andika"}, 162 | {token.LPAREN, "("}, 163 | {token.INT, "2"}, 164 | {token.RPAREN, ")"}, 165 | {token.RBRACE, "}"}, 166 | {token.DEFAULT, "kawaida"}, 167 | {token.LBRACE, "{"}, 168 | {token.IDENT, "andika"}, 169 | {token.LPAREN, "("}, 170 | {token.INT, "0"}, 171 | {token.RPAREN, ")"}, 172 | {token.RBRACE, "}"}, 173 | {token.RBRACE, "}"}, 174 | {token.NULL, "tupu"}, 175 | {token.FOR, "kwa"}, 176 | {token.IDENT, "i"}, 177 | {token.COMMA, ","}, 178 | {token.IDENT, "v"}, 179 | {token.IN, "ktk"}, 180 | {token.IDENT, "j"}, 181 | {token.EOF, ""}, 182 | } 183 | 184 | l := New(input) 185 | 186 | for i, tt := range tests { 187 | tok := l.NextToken() 188 | 189 | if tok.Type != tt.expectedType { 190 | t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q", 191 | i, tt.expectedType, tok.Type) 192 | } 193 | 194 | if tok.Literal != tt.expectedLiteral { 195 | t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q", 196 | i, tt.expectedLiteral, tok.Literal) 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/NuruProgramming/Nuru/repl" 9 | "github.com/NuruProgramming/Nuru/styles" 10 | "github.com/charmbracelet/lipgloss" 11 | ) 12 | 13 | var ( 14 | Title = styles.TitleStyle. 15 | Render(` 16 | █░░ █░█ █▀▀ █░█ ▄▀█   █▄█ ▄▀█   █▄░█ █░█ █▀█ █░█ 17 | █▄▄ █▄█ █▄█ █▀█ █▀█   ░█░ █▀█   █░▀█ █▄█ █▀▄ █▄█`) 18 | Version = styles.VersionStyle.Render("v0.5.18") 19 | Author = styles.AuthorStyle.Render("by Nuru Org") 20 | NewLogo = lipgloss.JoinVertical(lipgloss.Center, Title, lipgloss.JoinHorizontal(lipgloss.Center, Author, " | ", Version)) 21 | Help = styles.HelpStyle.Italic(false).Render(fmt.Sprintf(`💡 Namna ya kutumia Nuru: 22 | %s: Kuanza programu ya Nuru 23 | %s: Kuendesha faili la Nuru 24 | %s: Kusoma nyaraka za Nuru 25 | %s: Kufahamu toleo la Nuru 26 | `, 27 | styles.HelpStyle.Bold(true).Render("nuru"), 28 | styles.HelpStyle.Bold(true).Render("nuru jinaLaFile.nr"), 29 | styles.HelpStyle.Bold(true).Render("nuru --nyaraka"), 30 | styles.HelpStyle.Bold(true).Render("nuru --toleo"))) 31 | ) 32 | 33 | func main() { 34 | 35 | args := os.Args 36 | if len(args) < 2 { 37 | 38 | help := styles.HelpStyle.Render("💡 Tumia exit() au toka() kuondoka") 39 | fmt.Println(lipgloss.JoinVertical(lipgloss.Left, NewLogo, "\n", help)) 40 | repl.Start() 41 | return 42 | } 43 | 44 | if len(args) == 2 { 45 | switch args[1] { 46 | case "msaada", "-msaada", "--msaada", "help", "-help", "--help", "-h": 47 | fmt.Println(Help) 48 | case "version", "-version", "--version", "-v", "v", "--toleo", "-toleo": 49 | fmt.Println(NewLogo) 50 | case "-docs", "--docs", "-nyaraka", "--nyaraka": 51 | repl.Docs() 52 | default: 53 | file := args[1] 54 | 55 | if strings.HasSuffix(file, "nr") || strings.HasSuffix(file, ".sw") { 56 | contents, err := os.ReadFile(file) 57 | if err != nil { 58 | fmt.Println(styles.ErrorStyle.Render("Error: Nuru imeshindwa kusoma faili: ", args[1])) 59 | os.Exit(1) 60 | } 61 | 62 | repl.Read(string(contents)) 63 | } else { 64 | fmt.Println(styles.ErrorStyle.Render("'"+file+"'", "sii faili sahihi. Tumia faili la '.nr' au '.sw'")) 65 | os.Exit(1) 66 | } 67 | } 68 | } else { 69 | fmt.Println(styles.ErrorStyle.Render("Error: Operesheni imeshindikana boss.")) 70 | fmt.Println(Help) 71 | os.Exit(1) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /module/json.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/NuruProgramming/Nuru/object" 7 | ) 8 | 9 | var JsonFunctions = map[string]object.ModuleFunction{} 10 | 11 | func init() { 12 | JsonFunctions["dikodi"] = decode 13 | JsonFunctions["enkodi"] = encode 14 | } 15 | 16 | func decode(args []object.Object, defs map[string]object.Object) object.Object { 17 | if len(defs) != 0 { 18 | return &object.Error{Message: "Hoja hii hairuhusiwi"} 19 | } 20 | if len(args) != 1 { 21 | return &object.Error{Message: "Tunahitaji hoja moja tu"} 22 | } 23 | 24 | if args[0].Type() != object.STRING_OBJ { 25 | return &object.Error{Message: "Hoja lazima iwe neno"} 26 | } 27 | 28 | var i interface{} 29 | 30 | input := args[0].(*object.String).Value 31 | err := json.Unmarshal([]byte(input), &i) 32 | if err != nil { 33 | return &object.Error{Message: "Hii data sio jsoni"} 34 | } 35 | 36 | return convertWhateverToObject(i) 37 | } 38 | 39 | func convertWhateverToObject(i interface{}) object.Object { 40 | switch v := i.(type) { 41 | case map[string]interface{}: 42 | dict := &object.Dict{} 43 | dict.Pairs = make(map[object.HashKey]object.DictPair) 44 | 45 | for k, v := range v { 46 | pair := object.DictPair{ 47 | Key: &object.String{Value: k}, 48 | Value: convertWhateverToObject(v), 49 | } 50 | dict.Pairs[pair.Key.(object.Hashable).HashKey()] = pair 51 | } 52 | 53 | return dict 54 | case []interface{}: 55 | list := &object.Array{} 56 | for _, e := range v { 57 | list.Elements = append(list.Elements, convertWhateverToObject(e)) 58 | } 59 | 60 | return list 61 | case string: 62 | return &object.String{Value: v} 63 | case int64: 64 | return &object.Integer{Value: v} 65 | case float64: 66 | return &object.Float{Value: v} 67 | case bool: 68 | if v { 69 | return &object.Boolean{Value: true} 70 | } else { 71 | return &object.Boolean{Value: false} 72 | } 73 | } 74 | return &object.Null{} 75 | } 76 | 77 | func encode(args []object.Object, defs map[string]object.Object) object.Object { 78 | if len(defs) != 0 { 79 | return &object.Error{Message: "Hoja hii hairuhusiwi"} 80 | } 81 | 82 | input := args[0] 83 | i := convertObjectToWhatever(input) 84 | data, err := json.Marshal(i) 85 | 86 | if err != nil { 87 | return &object.Error{Message: "Siwezi kubadilisha data hii kuwa jsoni"} 88 | } 89 | 90 | return &object.String{Value: string(data)} 91 | } 92 | 93 | func convertObjectToWhatever(obj object.Object) interface{} { 94 | switch v := obj.(type) { 95 | case *object.Dict: 96 | m := make(map[string]interface{}) 97 | for _, pair := range v.Pairs { 98 | key := pair.Key.(*object.String).Value 99 | m[key] = convertObjectToWhatever(pair.Value) 100 | } 101 | return m 102 | case *object.Array: 103 | list := make([]interface{}, len(v.Elements)) 104 | for i, e := range v.Elements { 105 | list[i] = convertObjectToWhatever(e) 106 | } 107 | return list 108 | case *object.String: 109 | return v.Value 110 | case *object.Integer: 111 | return v.Value 112 | case *object.Float: 113 | return v.Value 114 | case *object.Boolean: 115 | return v.Value 116 | case *object.Null: 117 | return nil 118 | } 119 | return nil 120 | } 121 | -------------------------------------------------------------------------------- /module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import "github.com/NuruProgramming/Nuru/object" 4 | 5 | var Mapper = map[string]*object.Module{} 6 | 7 | func init() { 8 | Mapper["os"] = &object.Module{Name: "os", Functions: OsFunctions} 9 | Mapper["muda"] = &object.Module{Name: "time", Functions: TimeFunctions} 10 | Mapper["mtandao"] = &object.Module{Name: "net", Functions: NetFunctions} 11 | Mapper["jsoni"] = &object.Module{Name: "json", Functions: JsonFunctions} 12 | Mapper["hisabati"] = &object.Module{Name: "hisabati", Functions: MathFunctions} 13 | } 14 | -------------------------------------------------------------------------------- /module/net.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "net/http" 8 | 9 | "github.com/NuruProgramming/Nuru/object" 10 | ) 11 | 12 | var NetFunctions = map[string]object.ModuleFunction{} 13 | 14 | func init() { 15 | NetFunctions["peruzi"] = getRequest 16 | NetFunctions["tuma"] = postRequest 17 | } 18 | 19 | func getRequest(args []object.Object, defs map[string]object.Object) object.Object { 20 | 21 | if len(defs) != 0 { 22 | var url *object.String 23 | var headers, params *object.Dict 24 | for k, v := range defs { 25 | switch k { 26 | case "yuareli": 27 | strUrl, ok := v.(*object.String) 28 | if !ok { 29 | return &object.Error{Message: "Yuareli iwe neno"} 30 | } 31 | url = strUrl 32 | case "vichwa": 33 | dictHead, ok := v.(*object.Dict) 34 | if !ok { 35 | return &object.Error{Message: "Vichwa lazima viwe kamusi"} 36 | } 37 | headers = dictHead 38 | case "mwili": 39 | dictHead, ok := v.(*object.Dict) 40 | if !ok { 41 | return &object.Error{Message: "Mwili lazima iwe kamusi"} 42 | } 43 | params = dictHead 44 | default: 45 | return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} 46 | } 47 | } 48 | if url.Value == "" { 49 | return &object.Error{Message: "Yuareli ni lazima"} 50 | } 51 | 52 | var responseBody *bytes.Buffer 53 | if params != nil { 54 | booty := convertObjectToWhatever(params) 55 | 56 | jsonBody, err := json.Marshal(booty) 57 | 58 | if err != nil { 59 | return &object.Error{Message: "Huku format query yako vizuri."} 60 | } 61 | 62 | responseBody = bytes.NewBuffer(jsonBody) 63 | } 64 | 65 | var req *http.Request 66 | var err error 67 | if responseBody != nil { 68 | req, err = http.NewRequest("GET", url.Value, responseBody) 69 | } else { 70 | req, err = http.NewRequest("GET", url.Value, nil) 71 | } 72 | if err != nil { 73 | return &object.Error{Message: "Tumeshindwa kufanya request"} 74 | } 75 | 76 | if headers != nil { 77 | for _, val := range headers.Pairs { 78 | req.Header.Set(val.Key.Inspect(), val.Value.Inspect()) 79 | } 80 | } 81 | client := &http.Client{} 82 | 83 | resp, err := client.Do(req) 84 | 85 | if err != nil { 86 | return &object.Error{Message: "Tumeshindwa kutuma request."} 87 | } 88 | defer resp.Body.Close() 89 | respBody, err := ioutil.ReadAll(resp.Body) 90 | if err != nil { 91 | return &object.Error{Message: "Tumeshindwa kusoma majibu."} 92 | } 93 | 94 | return &object.String{Value: string(respBody)} 95 | 96 | } 97 | 98 | if len(args) == 1 { 99 | url, ok := args[0].(*object.String) 100 | if !ok { 101 | return &object.Error{Message: "Yuareli lazima iwe neno"} 102 | } 103 | req, err := http.NewRequest("GET", url.Value, nil) 104 | if err != nil { 105 | return &object.Error{Message: "Tumeshindwa kufanya request"} 106 | } 107 | 108 | client := &http.Client{} 109 | 110 | resp, err := client.Do(req) 111 | 112 | if err != nil { 113 | return &object.Error{Message: "Tumeshindwa kutuma request."} 114 | } 115 | defer resp.Body.Close() 116 | respBody, err := ioutil.ReadAll(resp.Body) 117 | if err != nil { 118 | return &object.Error{Message: "Tumeshindwa kusoma majibu."} 119 | } 120 | 121 | return &object.String{Value: string(respBody)} 122 | } 123 | return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} 124 | } 125 | 126 | func postRequest(args []object.Object, defs map[string]object.Object) object.Object { 127 | if len(defs) != 0 { 128 | var url *object.String 129 | var headers, params *object.Dict 130 | for k, v := range defs { 131 | switch k { 132 | case "yuareli": 133 | strUrl, ok := v.(*object.String) 134 | if !ok { 135 | return &object.Error{Message: "Yuareli iwe neno"} 136 | } 137 | url = strUrl 138 | case "vichwa": 139 | dictHead, ok := v.(*object.Dict) 140 | if !ok { 141 | return &object.Error{Message: "Vichwa lazima viwe kamusi"} 142 | } 143 | headers = dictHead 144 | case "mwili": 145 | dictHead, ok := v.(*object.Dict) 146 | if !ok { 147 | return &object.Error{Message: "Mwili lazima iwe kamusi"} 148 | } 149 | params = dictHead 150 | default: 151 | return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} 152 | } 153 | } 154 | if url.Value == "" { 155 | return &object.Error{Message: "Yuareli ni lazima"} 156 | } 157 | var responseBody *bytes.Buffer 158 | if params != nil { 159 | booty := convertObjectToWhatever(params) 160 | 161 | jsonBody, err := json.Marshal(booty) 162 | 163 | if err != nil { 164 | return &object.Error{Message: "Huku format query yako vizuri."} 165 | } 166 | 167 | responseBody = bytes.NewBuffer(jsonBody) 168 | } 169 | var req *http.Request 170 | var err error 171 | if responseBody != nil { 172 | req, err = http.NewRequest("POST", url.Value, responseBody) 173 | } else { 174 | req, err = http.NewRequest("POST", url.Value, nil) 175 | } 176 | if err != nil { 177 | return &object.Error{Message: "Tumeshindwa kufanya request"} 178 | } 179 | if headers != nil { 180 | for _, val := range headers.Pairs { 181 | req.Header.Set(val.Key.Inspect(), val.Value.Inspect()) 182 | } 183 | } 184 | req.Header.Add("Content-Type", "application/json") 185 | 186 | client := &http.Client{} 187 | 188 | resp, err := client.Do(req) 189 | 190 | if err != nil { 191 | return &object.Error{Message: "Tumeshindwa kutuma request."} 192 | } 193 | defer resp.Body.Close() 194 | respBody, err := ioutil.ReadAll(resp.Body) 195 | if err != nil { 196 | return &object.Error{Message: "Tumeshindwa kusoma majibu."} 197 | } 198 | return &object.String{Value: string(respBody)} 199 | } 200 | return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} 201 | } 202 | -------------------------------------------------------------------------------- /module/os.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "strings" 7 | 8 | "github.com/NuruProgramming/Nuru/object" 9 | ) 10 | 11 | var OsFunctions = map[string]object.ModuleFunction{} 12 | 13 | func init() { 14 | OsFunctions["toka"] = exit 15 | OsFunctions["kimbiza"] = run 16 | } 17 | 18 | func exit(args []object.Object, defs map[string]object.Object) object.Object { 19 | if len(args) > 1 { 20 | return &object.Error{Message: "Hoja sii sahihi"} 21 | } 22 | 23 | if len(args) == 1 { 24 | status, ok := args[0].(*object.Integer) 25 | if !ok { 26 | return &object.Error{Message: "Hoja sii namba"} 27 | } 28 | os.Exit(int(status.Value)) 29 | return nil 30 | } 31 | 32 | os.Exit(0) 33 | 34 | return nil 35 | } 36 | 37 | func run(args []object.Object, defs map[string]object.Object) object.Object { 38 | if len(args) != 1 { 39 | return &object.Error{Message: "Idadi ya hoja sii sahihi"} 40 | } 41 | 42 | cmd, ok := args[0].(*object.String) 43 | if !ok { 44 | return &object.Error{Message: "Hoja lazima iwe neno"} 45 | } 46 | cmdMain := cmd.Value 47 | cmdArgs := strings.Split(cmdMain, " ") 48 | cmdArgs = cmdArgs[1:] 49 | 50 | out, err := exec.Command(cmdMain, cmdArgs...).Output() 51 | if err != nil { 52 | return &object.Error{Message: "Tumeshindwa kukimbiza komandi"} 53 | } 54 | 55 | return &object.String{Value: string(out)} 56 | } 57 | -------------------------------------------------------------------------------- /module/time.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/NuruProgramming/Nuru/object" 9 | ) 10 | 11 | var TimeFunctions = map[string]object.ModuleFunction{} 12 | 13 | func init() { 14 | TimeFunctions["hasahivi"] = now 15 | TimeFunctions["lala"] = sleep 16 | TimeFunctions["tangu"] = since 17 | TimeFunctions["leo"] = today 18 | TimeFunctions["baada_ya"] = after 19 | TimeFunctions["tofauti"] = diff 20 | TimeFunctions["ongeza"] = addTime 21 | } 22 | 23 | func now(args []object.Object, defs map[string]object.Object) object.Object { 24 | if len(args) != 0 || len(defs) != 0 { 25 | return &object.Error{Message: "hatuhitaji hoja kwenye hasahivi"} 26 | } 27 | 28 | tn := time.Now() 29 | time_string := tn.Format("15:04:05 02-01-2006") 30 | 31 | return &object.Time{TimeValue: time_string} 32 | } 33 | 34 | func sleep(args []object.Object, defs map[string]object.Object) object.Object { 35 | if len(defs) != 0 { 36 | return &object.Error{Message: "Hoja hii hairuhusiwi"} 37 | } 38 | if len(args) != 1 { 39 | return &object.Error{Message: "tunahitaji hoja moja tu"} 40 | } 41 | 42 | objvalue := args[0].Inspect() 43 | inttime, err := strconv.Atoi(objvalue) 44 | 45 | if err != nil { 46 | return &object.Error{Message: "namba tu zinaruhusiwa kwenye hoja"} 47 | } 48 | 49 | time.Sleep(time.Duration(inttime) * time.Second) 50 | 51 | return nil 52 | } 53 | 54 | func since(args []object.Object, defs map[string]object.Object) object.Object { 55 | if len(defs) != 0 { 56 | return &object.Error{Message: "Hoja hii hairuhusiwi"} 57 | } 58 | if len(args) != 1 { 59 | return &object.Error{Message: "tunahitaji hoja moja tu"} 60 | } 61 | 62 | var ( 63 | t time.Time 64 | err error 65 | ) 66 | 67 | switch m := args[0].(type) { 68 | case *object.Time: 69 | t, _ = time.Parse("15:04:05 02-01-2006", m.TimeValue) 70 | case *object.String: 71 | t, err = time.Parse("15:04:05 02-01-2006", m.Value) 72 | if err != nil { 73 | return &object.Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} 74 | } 75 | default: 76 | return &object.Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} 77 | } 78 | 79 | current_time := time.Now().Format("15:04:05 02-01-2006") 80 | ct, _ := time.Parse("15:04:05 02-01-2006", current_time) 81 | 82 | diff := ct.Sub(t) 83 | durationInSeconds := diff.Seconds() 84 | 85 | return &object.Integer{Value: int64(durationInSeconds)} 86 | } 87 | 88 | func today(args []object.Object, defs map[string]object.Object) object.Object { 89 | if len(args) != 0 || len(defs) != 0 { 90 | return &object.Error{Message: "hatuhitaji hoja kwenye leo"} 91 | } 92 | 93 | dateStr := time.Now().Format("02-01-2006") 94 | return &object.String{Value: dateStr} 95 | } 96 | 97 | func after(args []object.Object, defs map[string]object.Object) object.Object { 98 | if len(defs) != 0 || len(args) != 1 { 99 | return &object.Error{Message: "tunahitaji hoja moja tu kwenye baada_ya"} 100 | } 101 | 102 | secondsStr := args[0].Inspect() 103 | seconds, err := strconv.Atoi(secondsStr) 104 | if err != nil { 105 | return &object.Error{Message: "hoja lazima iwe namba"} 106 | } 107 | 108 | future := time.Now().Add(time.Duration(seconds) * time.Second) 109 | return &object.Time{TimeValue: future.Format("15:04:05 02-01-2006")} 110 | } 111 | 112 | func diff(args []object.Object, defs map[string]object.Object) object.Object { 113 | if len(defs) != 0 || len(args) != 2 { 114 | return &object.Error{Message: "tunahitaji hoja mbili kwenye tofauti"} 115 | } 116 | 117 | parseTime := func(o object.Object) (time.Time, error) { 118 | switch v := o.(type) { 119 | case *object.Time: 120 | return time.Parse("15:04:05 02-01-2006", v.TimeValue) 121 | case *object.String: 122 | return time.Parse("15:04:05 02-01-2006", v.Value) 123 | default: 124 | return time.Time{}, fmt.Errorf("aina batili") 125 | } 126 | } 127 | 128 | t1, err1 := parseTime(args[0]) 129 | t2, err2 := parseTime(args[1]) 130 | 131 | if err1 != nil || err2 != nil { 132 | return &object.Error{Message: "tofauti inahitaji nyakati halali mbili"} 133 | } 134 | 135 | diff := t1.Sub(t2).Seconds() 136 | return &object.Integer{Value: int64(diff)} 137 | } 138 | 139 | 140 | func addTime(args []object.Object, defs map[string]object.Object) object.Object { 141 | if len(args) != 1 { 142 | return &object.Error{Message: "ongeza inahitaji wakati mmoja wa kuanzia"} 143 | } 144 | 145 | baseTimeObj := args[0] 146 | baseTime, err := func() (time.Time, error) { 147 | switch t := baseTimeObj.(type) { 148 | case *object.Time: 149 | return time.Parse("15:04:05 02-01-2006", t.TimeValue) 150 | case *object.String: 151 | return time.Parse("15:04:05 02-01-2006", t.Value) 152 | default: 153 | return time.Time{}, fmt.Errorf("aina ya wakati sio sahihi") 154 | } 155 | }() 156 | if err != nil { 157 | return &object.Error{Message: "wakati uliotolewa sio sahihi"} 158 | } 159 | 160 | secs := getInt(defs["sekunde"]) 161 | mins := getInt(defs["dakika"]) 162 | hours := getInt(defs["masaa"]) 163 | days := getInt(defs["siku"]) 164 | weeks := getInt(defs["wiki"]) 165 | months := getInt(defs["miezi"]) 166 | years := getInt(defs["miaka"]) 167 | 168 | result := baseTime. 169 | Add(time.Second * time.Duration(secs)). 170 | Add(time.Minute * time.Duration(mins)). 171 | Add(time.Hour * time.Duration(hours)). 172 | AddDate(years, months, days+(weeks*7)) 173 | 174 | return &object.Time{TimeValue: result.Format("15:04:05 02-01-2006")} 175 | } 176 | 177 | func getInt(obj object.Object) int { 178 | if obj == nil { 179 | return 0 180 | } 181 | switch o := obj.(type) { 182 | case *object.Integer: 183 | return int(o.Value) 184 | case *object.String: 185 | n, err := strconv.Atoi(o.Value) 186 | if err == nil { 187 | return n 188 | } 189 | } 190 | return 0 191 | } 192 | -------------------------------------------------------------------------------- /object/array.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | ) 7 | 8 | type Array struct { 9 | Elements []Object 10 | offset int 11 | } 12 | 13 | func (ao *Array) Type() ObjectType { return ARRAY_OBJ } 14 | func (ao *Array) Inspect() string { 15 | var out bytes.Buffer 16 | 17 | elements := []string{} 18 | if len(ao.Elements) != 0 { 19 | for _, e := range ao.Elements { 20 | if e.Inspect() != "" { 21 | elements = append(elements, e.Inspect()) 22 | } 23 | } 24 | } 25 | 26 | out.WriteString("[") 27 | out.WriteString(strings.Join(elements, ", ")) 28 | out.WriteString("]") 29 | 30 | return out.String() 31 | } 32 | 33 | func (ao *Array) Next() (Object, Object) { 34 | idx := ao.offset 35 | if len(ao.Elements) > idx { 36 | ao.offset = idx + 1 37 | return &Integer{Value: int64(idx)}, ao.Elements[idx] 38 | } 39 | return nil, nil 40 | } 41 | 42 | func (ao *Array) Reset() { 43 | ao.offset = 0 44 | } 45 | 46 | func (a *Array) Method(method string, args []Object) Object { 47 | switch method { 48 | case "idadi": 49 | return a.len(args) 50 | case "sukuma": 51 | return a.push(args) 52 | case "yamwisho": 53 | return a.last() 54 | case "unga": 55 | return a.join(args) 56 | case "chuja": 57 | return a.filter(args) 58 | case "tafuta": 59 | return a.find(args) 60 | default: 61 | return newError("Samahani, kiendesha hiki hakitumiki na tungo (Neno)") 62 | } 63 | } 64 | 65 | func (a *Array) len(args []Object) Object { 66 | if len(args) != 0 { 67 | return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) 68 | } 69 | return &Integer{Value: int64(len(a.Elements))} 70 | } 71 | 72 | func (a *Array) last() Object { 73 | length := len(a.Elements) 74 | if length > 0 { 75 | return a.Elements[length-1] 76 | } 77 | return &Null{} 78 | } 79 | 80 | func (a *Array) push(args []Object) Object { 81 | a.Elements = append(a.Elements, args...) 82 | return a 83 | } 84 | 85 | func (a *Array) join(args []Object) Object { 86 | if len(args) > 1 { 87 | return newError("Samahani, tunahitaji Hoja 1 au 0, wewe umeweka %d", len(args)) 88 | } 89 | if len(a.Elements) > 0 { 90 | glue := "" 91 | if len(args) == 1 { 92 | glue = args[0].(*String).Value 93 | } 94 | length := len(a.Elements) 95 | newElements := make([]string, length) 96 | for k, v := range a.Elements { 97 | newElements[k] = v.Inspect() 98 | } 99 | return &String{Value: strings.Join(newElements, glue)} 100 | } else { 101 | return &String{Value: ""} 102 | } 103 | } 104 | 105 | func (a *Array) filter(args []Object) Object { 106 | if len(args) != 1 { 107 | return newError("Samahani, idadi ya hoja sii sahihi") 108 | } 109 | 110 | dummy := []Object{} 111 | filteredArr := Array{Elements: dummy} 112 | for _, obj := range a.Elements { 113 | if obj.Inspect() == args[0].Inspect() && obj.Type() == args[0].Type() { 114 | filteredArr.Elements = append(filteredArr.Elements, obj) 115 | } 116 | } 117 | return &filteredArr 118 | } 119 | 120 | func (a *Array) find(args []Object) Object { 121 | if len(args) != 1 { 122 | return newError("Samahani, idadi ya hoja sii sahihi") 123 | } 124 | 125 | for _, obj := range a.Elements { 126 | if obj.Inspect() == args[0].Inspect() && obj.Type() == args[0].Type() { 127 | return obj 128 | } 129 | } 130 | return &Null{} 131 | } 132 | -------------------------------------------------------------------------------- /object/at.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | type At struct { 6 | Instance *Instance 7 | } 8 | 9 | func (a *At) Type() ObjectType { return AT } 10 | func (a *At) Inspect() string { 11 | return fmt.Sprintf("@.%s", a.Instance.Package.Name.Value) 12 | } 13 | -------------------------------------------------------------------------------- /object/bool.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type Boolean struct { 4 | Value bool 5 | } 6 | 7 | func (b *Boolean) Inspect() string { 8 | if b.Value { 9 | return "kweli" 10 | } else { 11 | return "sikweli" 12 | } 13 | } 14 | func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ } 15 | 16 | func (b *Boolean) HashKey() HashKey { 17 | var value uint64 18 | 19 | if b.Value { 20 | value = 1 21 | } else { 22 | value = 0 23 | } 24 | 25 | return HashKey{Type: b.Type(), Value: value} 26 | } 27 | -------------------------------------------------------------------------------- /object/break.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type Break struct{} 4 | 5 | func (b *Break) Type() ObjectType { return BREAK_OBJ } 6 | func (b *Break) Inspect() string { return "break" } 7 | -------------------------------------------------------------------------------- /object/builtin.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type BuiltinFunction func(args ...Object) Object 4 | 5 | type Builtin struct { 6 | Fn BuiltinFunction 7 | } 8 | 9 | func (b *Builtin) Inspect() string { return "builtin function" } 10 | func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ } 11 | -------------------------------------------------------------------------------- /object/byte.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type Byte struct { 4 | Value []byte 5 | String string 6 | } 7 | 8 | func (b *Byte) Inspect() string { return "b" + b.String } 9 | func (b *Byte) Type() ObjectType { return BYTE_OBJ } 10 | -------------------------------------------------------------------------------- /object/continue.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type Continue struct{} 4 | 5 | func (c *Continue) Type() ObjectType { return CONTINUE_OBJ } 6 | func (c *Continue) Inspect() string { return "continue" } 7 | -------------------------------------------------------------------------------- /object/dict.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | type DictPair struct { 11 | Key Object 12 | Value Object 13 | } 14 | 15 | type Dict struct { 16 | Pairs map[HashKey]DictPair 17 | offset int 18 | } 19 | 20 | func (d *Dict) Type() ObjectType { return DICT_OBJ } 21 | func (d *Dict) Inspect() string { 22 | var out bytes.Buffer 23 | 24 | pairs := []string{} 25 | 26 | for _, pair := range d.Pairs { 27 | pairs = append(pairs, fmt.Sprintf("%s: %s", pair.Key.Inspect(), pair.Value.Inspect())) 28 | } 29 | 30 | out.WriteString("{") 31 | out.WriteString(strings.Join(pairs, ", ")) 32 | out.WriteString("}") 33 | 34 | return out.String() 35 | } 36 | 37 | func (d *Dict) Next() (Object, Object) { 38 | idx := 0 39 | dict := make(map[string]DictPair) 40 | var keys []string 41 | for _, v := range d.Pairs { 42 | dict[v.Key.Inspect()] = v 43 | keys = append(keys, v.Key.Inspect()) 44 | } 45 | 46 | sort.Strings(keys) 47 | 48 | for _, k := range keys { 49 | if d.offset == idx { 50 | d.offset += 1 51 | return dict[k].Key, dict[k].Value 52 | } 53 | idx += 1 54 | } 55 | return nil, nil 56 | } 57 | 58 | func (d *Dict) Reset() { 59 | d.offset = 0 60 | } 61 | -------------------------------------------------------------------------------- /object/environment.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | func NewEnclosedEnvironment(outer *Environment) *Environment { 4 | env := NewEnvironment() 5 | env.outer = outer 6 | return env 7 | } 8 | 9 | func NewEnvironment() *Environment { 10 | s := make(map[string]Object) 11 | return &Environment{store: s, outer: nil} 12 | } 13 | 14 | type Environment struct { 15 | store map[string]Object 16 | outer *Environment 17 | } 18 | 19 | func (e *Environment) Get(name string) (Object, bool) { 20 | obj, ok := e.store[name] 21 | 22 | if !ok && e.outer != nil { 23 | obj, ok = e.outer.Get(name) 24 | } 25 | return obj, ok 26 | } 27 | 28 | func (e *Environment) Set(name string, val Object) Object { 29 | e.store[name] = val 30 | return val 31 | } 32 | 33 | func (e *Environment) Del(name string) bool { 34 | _, ok := e.store[name] 35 | if ok { 36 | delete(e.store, name) 37 | } 38 | return true 39 | } 40 | -------------------------------------------------------------------------------- /object/error.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | type Error struct { 6 | Message string 7 | } 8 | 9 | func (e *Error) Inspect() string { 10 | msg := fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, "Kosa: ") 11 | return msg + e.Message 12 | } 13 | func (e *Error) Type() ObjectType { return ERROR_OBJ } 14 | -------------------------------------------------------------------------------- /object/file.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type File struct { 8 | Filename string 9 | Content string 10 | } 11 | 12 | func (f *File) Type() ObjectType { return FILE_OBJ } 13 | func (f *File) Inspect() string { return f.Filename } 14 | func (f *File) Method(method string, args []Object) Object { 15 | switch method { 16 | case "soma": 17 | return f.read(args) 18 | case "andika": 19 | return f.write(args) 20 | case "ongeza": 21 | return f.append(args) 22 | } 23 | return nil 24 | } 25 | 26 | func (f *File) read(args []Object) Object { 27 | if len(args) != 0 { 28 | return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) 29 | } 30 | return &String{Value: f.Content} 31 | } 32 | 33 | func (f *File) write(args []Object) Object { 34 | if len(args) != 1 { 35 | return newError("Samahani, tunahitaji Hoja 1, wewe umeweka %d", len(args)) 36 | } 37 | content, ok := args[0].(*String) 38 | if !ok { 39 | return newError("Samahani, hoja lazima iwe Tungo") 40 | } 41 | err := os.WriteFile(f.Filename, []byte(content.Value), 0644) 42 | if err != nil { 43 | return newError("Hitilafu katika kuandika faili: %s", err.Error()) 44 | } 45 | f.Content = content.Value 46 | return &Boolean{Value: true} 47 | } 48 | 49 | func (f *File) append(args []Object) Object { 50 | if len(args) != 1 { 51 | return newError("Samahani, tunahitaji Hoja 1, wewe umeweka %d", len(args)) 52 | } 53 | content, ok := args[0].(*String) 54 | if !ok { 55 | return newError("Samahani, hoja lazima iwe Tungo") 56 | } 57 | file, err := os.OpenFile(f.Filename, os.O_APPEND|os.O_WRONLY, 0644) 58 | if err != nil { 59 | return newError("Hitilafu katika kufungua faili: %s", err.Error()) 60 | } 61 | defer file.Close() 62 | _, err = file.WriteString(content.Value) 63 | if err != nil { 64 | return newError("Hitilafu katika kuongeza kwa faili: %s", err.Error()) 65 | } 66 | f.Content += content.Value 67 | return &Boolean{Value: true} 68 | } 69 | -------------------------------------------------------------------------------- /object/float.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "hash/fnv" 5 | "strconv" 6 | ) 7 | 8 | type Float struct { 9 | Value float64 10 | } 11 | 12 | func (f *Float) Inspect() string { return strconv.FormatFloat(f.Value, 'f', -1, 64) } 13 | func (f *Float) Type() ObjectType { return FLOAT_OBJ } 14 | 15 | func (f *Float) HashKey() HashKey { 16 | h := fnv.New64a() 17 | h.Write([]byte(f.Inspect())) 18 | return HashKey{Type: f.Type(), Value: h.Sum64()} 19 | } 20 | -------------------------------------------------------------------------------- /object/function.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | 7 | "github.com/NuruProgramming/Nuru/ast" 8 | ) 9 | 10 | type Function struct { 11 | Name string 12 | Parameters []*ast.Identifier 13 | Defaults map[string]ast.Expression 14 | Body *ast.BlockStatement 15 | Env *Environment 16 | } 17 | 18 | func (f *Function) Type() ObjectType { return FUNCTION_OBJ } 19 | func (f *Function) Inspect() string { 20 | var out bytes.Buffer 21 | 22 | params := []string{} 23 | for _, p := range f.Parameters { 24 | params = append(params, p.String()) 25 | } 26 | 27 | out.WriteString("unda") 28 | out.WriteString("(") 29 | out.WriteString(strings.Join(params, ", ")) 30 | out.WriteString(") {\n") 31 | out.WriteString(f.Body.String()) 32 | out.WriteString("\n}") 33 | 34 | return out.String() 35 | } 36 | -------------------------------------------------------------------------------- /object/instance.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | type Instance struct { 6 | Package *Package 7 | Env *Environment 8 | } 9 | 10 | func (i *Instance) Type() ObjectType { return INSTANCE } 11 | func (i *Instance) Inspect() string { 12 | return fmt.Sprintf("Pakeji: %s", i.Package.Name.Value) 13 | } 14 | -------------------------------------------------------------------------------- /object/integer.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | type Integer struct { 6 | Value int64 7 | } 8 | 9 | func (i *Integer) Inspect() string { return fmt.Sprintf("%d", i.Value) } 10 | func (i *Integer) Type() ObjectType { return INTEGER_OBJ } 11 | 12 | func (i *Integer) HashKey() HashKey { 13 | return HashKey{Type: i.Type(), Value: uint64(i.Value)} 14 | } 15 | -------------------------------------------------------------------------------- /object/module.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type ModuleFunction func(args []Object, defs map[string]Object) Object 4 | 5 | type Module struct { 6 | Name string 7 | Functions map[string]ModuleFunction 8 | } 9 | 10 | func (m *Module) Type() ObjectType { 11 | switch m.Name { 12 | case "time": 13 | return TIME_OBJ 14 | case "json": 15 | return JSON_OBJ 16 | default: 17 | return MODULE_OBJ 18 | } 19 | } 20 | func (m *Module) Inspect() string { return "Module: " + m.Name } 21 | -------------------------------------------------------------------------------- /object/null.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type Null struct{} 4 | 5 | func (n *Null) Inspect() string { return "null" } 6 | func (n *Null) Type() ObjectType { return NULL_OBJ } 7 | -------------------------------------------------------------------------------- /object/object.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type ObjectType string 8 | 9 | const ( 10 | INTEGER_OBJ = "NAMBA" 11 | FLOAT_OBJ = "DESIMALI" 12 | BOOLEAN_OBJ = "BOOLEAN" 13 | NULL_OBJ = "TUPU" 14 | RETURN_VALUE_OBJ = "RUDISHA" 15 | ERROR_OBJ = "KOSA" 16 | FUNCTION_OBJ = "UNDO (FUNCTION)" 17 | STRING_OBJ = "NENO" 18 | BUILTIN_OBJ = "YA_NDANI" 19 | ARRAY_OBJ = "ORODHA" 20 | DICT_OBJ = "KAMUSI" 21 | CONTINUE_OBJ = "ENDELEA" 22 | BREAK_OBJ = "VUNJA" 23 | FILE_OBJ = "FAILI" 24 | TIME_OBJ = "MUDA" 25 | JSON_OBJ = "JSONI" 26 | MODULE_OBJ = "MODULE" 27 | BYTE_OBJ = "BYTE" 28 | PACKAGE_OBJ = "PAKEJI" 29 | INSTANCE = "PAKEJI" 30 | AT = "@" 31 | ) 32 | 33 | type Object interface { 34 | Type() ObjectType 35 | Inspect() string 36 | } 37 | 38 | type HashKey struct { 39 | Type ObjectType 40 | Value uint64 41 | } 42 | 43 | type Hashable interface { 44 | HashKey() HashKey 45 | } 46 | 47 | // Iterable interface for dicts, strings and arrays 48 | type Iterable interface { 49 | Next() (Object, Object) 50 | Reset() 51 | } 52 | 53 | func newError(format string, a ...interface{}) *Error { 54 | format = fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, format) 55 | return &Error{Message: fmt.Sprintf(format, a...)} 56 | } 57 | -------------------------------------------------------------------------------- /object/object_test.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "testing" 4 | 5 | func TestStringHashKey(t *testing.T) { 6 | hello1 := &String{Value: "Hello World"} 7 | hello2 := &String{Value: "Hello World"} 8 | diff1 := &String{Value: "My name is Avi"} 9 | diff2 := &String{Value: "My name is Avi"} 10 | 11 | if hello1.HashKey() != hello2.HashKey() { 12 | t.Errorf("string with the same content have different dict keys") 13 | } 14 | 15 | if diff1.HashKey() != diff2.HashKey() { 16 | t.Errorf("String with the same content have different dict keys") 17 | } 18 | 19 | if hello1.HashKey() == diff1.HashKey() { 20 | t.Errorf("Strings with different content have the same dict keys") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /object/package.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/NuruProgramming/Nuru/ast" 7 | ) 8 | 9 | type Package struct { 10 | Name *ast.Identifier 11 | Env *Environment 12 | Scope *Environment 13 | } 14 | 15 | func (p *Package) Type() ObjectType { return PACKAGE_OBJ } 16 | func (p *Package) Inspect() string { 17 | return fmt.Sprintf("Pakeji: %s", p.Name.Value) 18 | } 19 | -------------------------------------------------------------------------------- /object/return.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | type ReturnValue struct { 4 | Value Object 5 | } 6 | 7 | func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() } 8 | func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ } 9 | -------------------------------------------------------------------------------- /object/strings.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "fmt" 5 | "hash/fnv" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type String struct { 11 | Value string 12 | offset int 13 | } 14 | 15 | func (s *String) Inspect() string { return s.Value } 16 | func (s *String) Type() ObjectType { return STRING_OBJ } 17 | func (s *String) HashKey() HashKey { 18 | h := fnv.New64a() 19 | h.Write([]byte(s.Value)) 20 | 21 | return HashKey{Type: s.Type(), Value: h.Sum64()} 22 | } 23 | func (s *String) Next() (Object, Object) { 24 | offset := s.offset 25 | if len(s.Value) > offset { 26 | s.offset = offset + 1 27 | return &Integer{Value: int64(offset)}, &String{Value: string(s.Value[offset])} 28 | } 29 | return nil, nil 30 | } 31 | func (s *String) Reset() { 32 | s.offset = 0 33 | } 34 | func (s *String) Method(method string, args []Object) Object { 35 | switch method { 36 | case "idadi": 37 | return s.len(args) 38 | case "herufikubwa": 39 | return s.upper(args) 40 | case "herufindogo": 41 | return s.lower(args) 42 | case "gawa": 43 | return s.split(args) 44 | case "panga": 45 | return s.format(args) 46 | default: 47 | return newError("Samahani, kiendesha hiki hakitumiki na tungo (Neno)") 48 | } 49 | } 50 | 51 | func (s *String) len(args []Object) Object { 52 | if len(args) != 0 { 53 | return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) 54 | } 55 | return &Integer{Value: int64(len(s.Value))} 56 | } 57 | 58 | func (s *String) upper(args []Object) Object { 59 | if len(args) != 0 { 60 | return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) 61 | } 62 | return &String{Value: strings.ToUpper(s.Value)} 63 | } 64 | 65 | func (s *String) lower(args []Object) Object { 66 | if len(args) != 0 { 67 | return newError("Samahani, tunahitaji Hoja 0, wewe umeweka %d", len(args)) 68 | } 69 | return &String{Value: strings.ToLower(s.Value)} 70 | } 71 | 72 | func (s *String) split(args []Object) Object { 73 | if len(args) > 1 { 74 | return newError("Samahani, tunahitaji Hoja 1 au 0, wewe umeweka %d", len(args)) 75 | } 76 | sep := " " 77 | if len(args) == 1 { 78 | sep = args[0].(*String).Value 79 | } 80 | parts := strings.Split(s.Value, sep) 81 | length := len(parts) 82 | elements := make([]Object, length) 83 | for k, v := range parts { 84 | elements[k] = &String{Value: v} 85 | } 86 | return &Array{Elements: elements} 87 | } 88 | 89 | func (s *String) format(args []Object) Object { 90 | value, err := formatStr(s.Value, args) 91 | 92 | if err != nil { 93 | return newError(err.Error()) 94 | } 95 | 96 | return &String{Value: value} 97 | } 98 | 99 | func formatStr(format string, options []Object) (string, error) { 100 | var str strings.Builder 101 | var val strings.Builder 102 | var check_val bool 103 | var opts_len int = len(options) 104 | 105 | var escapeChar bool 106 | 107 | type optM struct { 108 | val bool 109 | obj Object 110 | } 111 | 112 | var optionsMap = make(map[int]optM, opts_len) 113 | 114 | for i, optm := range options { 115 | optionsMap[i] = optM{val: false, obj: optm} 116 | } 117 | 118 | for _, opt := range format { 119 | 120 | if !escapeChar && opt == '\\' { 121 | escapeChar = true 122 | continue 123 | } 124 | 125 | if opt == '{' && !escapeChar { 126 | check_val = true 127 | continue 128 | } 129 | 130 | if escapeChar { 131 | if opt != '{' && opt != '}' { 132 | str.WriteRune('\\') 133 | } 134 | escapeChar = false 135 | } 136 | 137 | if check_val && opt == '}' { 138 | vstr := strings.TrimSpace(val.String()) 139 | arrv, err := strconv.Atoi(vstr) 140 | if err != nil { 141 | return "", fmt.Errorf(fmt.Sprintf("Ulichopeana si NAMBA, jaribu tena: `%s'", vstr)) 142 | } 143 | 144 | oVal, exists := optionsMap[arrv] 145 | 146 | if !exists { 147 | return "", fmt.Errorf(fmt.Sprintf("Nambari ya chaguo unalolitaka %d ni kubwa kuliko ulizopeana (%d)", arrv, opts_len)) 148 | } 149 | 150 | str.WriteString(oVal.obj.Inspect()) 151 | optionsMap[arrv] = optM{val: true, obj: oVal.obj} 152 | 153 | check_val = false 154 | val.Reset() 155 | continue 156 | } 157 | 158 | if check_val { 159 | val.WriteRune(opt) 160 | continue 161 | } 162 | 163 | str.WriteRune(opt) 164 | } 165 | 166 | if check_val { 167 | return "", fmt.Errorf(fmt.Sprintf("Haukufunga '{', tuliokota kabla ya kufika mwisho `%s'", val.String())) 168 | } 169 | 170 | for _, v := range optionsMap { 171 | if !v.val { 172 | return "", fmt.Errorf(fmt.Sprintf("Ulipeana hili chaguo (%s) {%s} lakini haukutumia", v.obj.Inspect(), v.obj.Type())) 173 | } 174 | } 175 | 176 | return str.String(), nil 177 | } 178 | -------------------------------------------------------------------------------- /object/time.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | type Time struct { 10 | TimeValue string 11 | } 12 | 13 | func (t *Time) Type() ObjectType { return TIME_OBJ } 14 | func (t *Time) Inspect() string { return t.TimeValue } 15 | func (t *Time) Method(method string, args []Object, defs map[string]Object) Object { 16 | switch method { 17 | case "ongeza": 18 | return t.add(args, defs) 19 | case "tangu": 20 | return t.since(args, defs) 21 | } 22 | return nil 23 | } 24 | 25 | func (t *Time) add(args []Object, defs map[string]Object) Object { 26 | if len(defs) != 0 { 27 | var sec, min, hr, d, m, y int 28 | for k, v := range defs { 29 | objvalue := v.Inspect() 30 | inttime, err := strconv.Atoi(objvalue) 31 | if err != nil { 32 | return newError("namba tu zinaruhusiwa kwenye hoja") 33 | } 34 | switch k { 35 | case "sekunde": 36 | sec = inttime 37 | case "dakika": 38 | min = inttime 39 | case "saa": 40 | hr = inttime 41 | case "siku": 42 | d = inttime 43 | case "miezi": 44 | m = inttime 45 | case "miaka": 46 | y = inttime 47 | default: 48 | return newError("Hukuweka muda sahihi") 49 | } 50 | } 51 | cur_time, _ := time.Parse("15:04:05 02-01-2006", t.Inspect()) 52 | next_time := cur_time. 53 | Add(time.Duration(sec)*time.Second). 54 | Add(time.Duration(min)*time.Minute). 55 | Add(time.Duration(hr)*time.Hour). 56 | AddDate(y, m, d) 57 | return &Time{TimeValue: string(next_time.Format("15:04:05 02-01-2006"))} 58 | } 59 | 60 | if len(args) != 1 { 61 | return newError("Samahani, tunahitaji Hoja 1, wewe umeweka %d", len(args)) 62 | } 63 | 64 | cur_time, _ := time.Parse("15:04:05 02-01-2006", t.Inspect()) 65 | 66 | objvalue := args[0].Inspect() 67 | inttime, err := strconv.Atoi(objvalue) 68 | 69 | if err != nil { 70 | return newError("namba tu zinaruhusiwa kwenye hoja") 71 | } 72 | 73 | next_time := cur_time.Add(time.Duration(inttime) * time.Hour) 74 | return &Time{TimeValue: string(next_time.Format("15:04:05 02-01-2006"))} 75 | } 76 | 77 | func (t *Time) since(args []Object, defs map[string]Object) Object { 78 | if len(defs) != 0 { 79 | return &Error{Message: "Hoja hii hairuhusiwi"} 80 | } 81 | if len(args) != 1 { 82 | return &Error{Message: "tunahitaji hoja moja tu"} 83 | } 84 | 85 | var ( 86 | o time.Time 87 | err error 88 | ) 89 | 90 | switch m := args[0].(type) { 91 | case *Time: 92 | o, _ = time.Parse("15:04:05 02-01-2006", m.TimeValue) 93 | case *String: 94 | o, err = time.Parse("15:04:05 02-01-2006", m.Value) 95 | if err != nil { 96 | return &Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} 97 | } 98 | default: 99 | return &Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} 100 | } 101 | 102 | ct, _ := time.Parse("15:04:05 02-01-2006", t.TimeValue) 103 | 104 | diff := ct.Sub(o) 105 | durationInSeconds := diff.Seconds() 106 | 107 | return &Integer{Value: int64(durationInSeconds)} 108 | } 109 | -------------------------------------------------------------------------------- /parser/arrays.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseArrayLiteral() ast.Expression { 9 | array := &ast.ArrayLiteral{Token: p.curToken} 10 | 11 | array.Elements = p.parseExpressionList(token.RBRACKET) 12 | 13 | return array 14 | } 15 | 16 | func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression { 17 | list := []ast.Expression{} 18 | 19 | if p.peekTokenIs(end) { 20 | p.nextToken() 21 | return list 22 | } 23 | 24 | p.nextToken() 25 | list = append(list, p.parseExpression(LOWEST)) 26 | 27 | for p.peekTokenIs(token.COMMA) { 28 | p.nextToken() 29 | p.nextToken() 30 | list = append(list, p.parseExpression(LOWEST)) 31 | } 32 | 33 | if !p.expectPeek(end) { 34 | return nil 35 | } 36 | return list 37 | } 38 | -------------------------------------------------------------------------------- /parser/assignEqual.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/NuruProgramming/Nuru/ast" 7 | ) 8 | 9 | func (p *Parser) parseAssignEqualExpression(exp ast.Expression) ast.Expression { 10 | switch node := exp.(type) { 11 | case *ast.Identifier: 12 | e := &ast.AssignEqual{ 13 | Token: p.curToken, 14 | Left: exp.(*ast.Identifier), 15 | } 16 | precendence := p.curPrecedence() 17 | p.nextToken() 18 | e.Value = p.parseExpression(precendence) 19 | return e 20 | case *ast.IndexExpression: 21 | ae := &ast.AssignmentExpression{Token: p.curToken, Left: exp} 22 | 23 | p.nextToken() 24 | 25 | ae.Value = p.parseExpression(LOWEST) 26 | 27 | return ae 28 | default: 29 | if node != nil { 30 | msg := fmt.Sprintf("Mstari %d:Tulitegemea kupata kitambulishi au array, badala yake tumepata: %s", p.curToken.Line, node.TokenLiteral()) 31 | p.errors = append(p.errors, msg) 32 | } else { 33 | msg := fmt.Sprintf("Mstari %d: Umekosea mkuu", p.curToken.Line) 34 | p.errors = append(p.errors, msg) 35 | } 36 | return nil 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /parser/assignment.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/NuruProgramming/Nuru/ast" 7 | ) 8 | 9 | func (p *Parser) parseAssignmentExpression(exp ast.Expression) ast.Expression { 10 | switch node := exp.(type) { 11 | case *ast.Identifier: 12 | e := &ast.Assign{ 13 | Token: p.curToken, 14 | Name: exp.(*ast.Identifier), 15 | } 16 | precedence := p.curPrecedence() 17 | p.nextToken() 18 | e.Value = p.parseExpression(precedence) 19 | return e 20 | 21 | case *ast.IndexExpression: 22 | case *ast.PropertyExpression: 23 | e := &ast.PropertyAssignment{ 24 | Token: p.curToken, 25 | Name: exp.(*ast.PropertyExpression), 26 | } 27 | precedence := p.curPrecedence() 28 | p.nextToken() 29 | e.Value = p.parseExpression(precedence) 30 | return e 31 | default: 32 | if node != nil { 33 | msg := fmt.Sprintf("Mstari %d:Tulitegemea kupata kitambulishi au array, badala yake tumepata: %s", p.curToken.Line, node.TokenLiteral()) 34 | p.errors = append(p.errors, msg) 35 | } else { 36 | msg := fmt.Sprintf("Mstari %d: Umekosea mkuu", p.curToken.Line) 37 | p.errors = append(p.errors, msg) 38 | } 39 | return nil 40 | } 41 | 42 | ae := &ast.AssignmentExpression{Token: p.curToken, Left: exp} 43 | 44 | p.nextToken() 45 | 46 | ae.Value = p.parseExpression(LOWEST) 47 | 48 | return ae 49 | } 50 | -------------------------------------------------------------------------------- /parser/at.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import "github.com/NuruProgramming/Nuru/ast" 4 | 5 | func (p *Parser) parseAt() ast.Expression { 6 | return &ast.At{Token: p.curToken} 7 | } 8 | -------------------------------------------------------------------------------- /parser/boolean.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseBoolean() ast.Expression { 9 | return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)} 10 | } 11 | -------------------------------------------------------------------------------- /parser/break.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseBreak() *ast.Break { 9 | stmt := &ast.Break{Token: p.curToken} 10 | for p.curTokenIs(token.SEMICOLON) { 11 | p.nextToken() 12 | } 13 | return stmt 14 | } 15 | -------------------------------------------------------------------------------- /parser/continue.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseContinue() *ast.Continue { 9 | stmt := &ast.Continue{Token: p.curToken} 10 | for p.curTokenIs(token.SEMICOLON) { 11 | p.nextToken() 12 | } 13 | return stmt 14 | } 15 | -------------------------------------------------------------------------------- /parser/dict.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseDictLiteral() ast.Expression { 9 | dict := &ast.DictLiteral{Token: p.curToken} 10 | dict.Pairs = make(map[ast.Expression]ast.Expression) 11 | 12 | for !p.peekTokenIs(token.RBRACE) { 13 | p.nextToken() 14 | key := p.parseExpression(LOWEST) 15 | 16 | if !p.expectPeek(token.COLON) { 17 | return nil 18 | } 19 | 20 | p.nextToken() 21 | value := p.parseExpression(LOWEST) 22 | 23 | dict.Pairs[key] = value 24 | 25 | if !p.peekTokenIs(token.RBRACE) && !p.expectPeek(token.COMMA) { 26 | return nil 27 | } 28 | } 29 | 30 | if !p.expectPeek(token.RBRACE) { 31 | return nil 32 | } 33 | 34 | return dict 35 | } 36 | -------------------------------------------------------------------------------- /parser/dot.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseMethod(obj ast.Expression) ast.Expression { 9 | tok := p.curToken 10 | precedence := p.curPrecedence() 11 | p.nextToken() 12 | if p.peekTokenIs(token.LPAREN) { 13 | exp := &ast.MethodExpression{Token: tok, Object: obj} 14 | exp.Method = p.parseExpression(precedence) 15 | if !p.expectPeek(token.LPAREN) { 16 | return nil 17 | } 18 | 19 | exp.Defaults = make(map[string]ast.Expression) 20 | 21 | for !p.peekTokenIs(token.RPAREN) { 22 | p.nextToken() 23 | if p.curTokenIs(token.COMMA) { 24 | continue 25 | } 26 | if p.peekTokenIs(token.ASSIGN) { 27 | name := p.curToken.Literal 28 | p.nextToken() 29 | p.nextToken() 30 | val := p.parseExpression(LOWEST) 31 | exp.Defaults[name] = val 32 | } else { 33 | exp.Arguments = append(exp.Arguments, p.parseExpression(LOWEST)) 34 | } 35 | } 36 | 37 | if !p.expectPeek(token.RPAREN) { 38 | return nil 39 | } 40 | 41 | return exp 42 | } else { 43 | exp := &ast.PropertyExpression{Token: tok, Object: obj} 44 | exp.Property = p.parseIdentifier() 45 | return exp 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /parser/float.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/NuruProgramming/Nuru/ast" 8 | ) 9 | 10 | func (p *Parser) parseFloatLiteral() ast.Expression { 11 | fl := &ast.FloatLiteral{Token: p.curToken} 12 | value, err := strconv.ParseFloat(p.curToken.Literal, 64) 13 | if err != nil { 14 | msg := fmt.Sprintf("Mstari %d: Hatuwezi kuparse %q kama desimali", p.curToken.Line, p.curToken.Literal) 15 | p.errors = append(p.errors, msg) 16 | return nil 17 | } 18 | fl.Value = value 19 | return fl 20 | } 21 | -------------------------------------------------------------------------------- /parser/for.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseForExpression() ast.Expression { 9 | expression := &ast.For{Token: p.curToken} 10 | p.nextToken() 11 | if !p.curTokenIs(token.IDENT) { 12 | return nil 13 | } 14 | if !p.peekTokenIs(token.ASSIGN) { 15 | return p.parseForInExpression(expression) 16 | } 17 | 18 | // In future will allow: kwa i = 0; i<10; i++ {andika(i)} 19 | // expression.Identifier = p.curToken.Literal 20 | // expression.StarterName = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} 21 | // if expression.StarterName == nil { 22 | // return nil 23 | // } 24 | // if !p.expectPeek(token.ASSIGN) { 25 | // return nil 26 | // } 27 | 28 | // p.nextToken() 29 | 30 | // expression.StarterValue = p.parseExpression(LOWEST) 31 | // // expression.Starter = p.parseExpression(LOWEST) 32 | // if expression.StarterValue == nil { 33 | // return nil 34 | // } 35 | // p.nextToken() 36 | // for p.curTokenIs(token.SEMICOLON) { 37 | // p.nextToken() 38 | // } 39 | // expression.Condition = p.parseExpression(LOWEST) 40 | // if expression.Condition == nil { 41 | // return nil 42 | // } 43 | // p.nextToken() 44 | // for p.curTokenIs(token.SEMICOLON) { 45 | // p.nextToken() 46 | // } 47 | // expression.Closer = p.parseExpression(LOWEST) 48 | // if expression.Closer == nil { 49 | // return nil 50 | // } 51 | // p.nextToken() 52 | // for p.curTokenIs(token.SEMICOLON) { 53 | // p.nextToken() 54 | // } 55 | // if !p.curTokenIs(token.LBRACE) { 56 | // return nil 57 | // } 58 | // expression.Block = p.parseBlockStatement() 59 | // return expression 60 | return nil 61 | } 62 | 63 | func (p *Parser) parseForInExpression(initialExpression *ast.For) ast.Expression { 64 | expression := &ast.ForIn{Token: initialExpression.Token} 65 | if !p.curTokenIs(token.IDENT) { 66 | return nil 67 | } 68 | val := p.curToken.Literal 69 | var key string 70 | p.nextToken() 71 | if p.curTokenIs(token.COMMA) { 72 | p.nextToken() 73 | if !p.curTokenIs(token.IDENT) { 74 | return nil 75 | } 76 | key = val 77 | val = p.curToken.Literal 78 | p.nextToken() 79 | } 80 | expression.Key = key 81 | expression.Value = val 82 | if !p.curTokenIs(token.IN) { 83 | return nil 84 | } 85 | p.nextToken() 86 | expression.Iterable = p.parseExpression(LOWEST) 87 | if !p.expectPeek(token.LBRACE) { 88 | return nil 89 | } 90 | expression.Block = p.parseBlockStatement() 91 | return expression 92 | } 93 | -------------------------------------------------------------------------------- /parser/function.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseFunctionLiteral() ast.Expression { 9 | lit := &ast.FunctionLiteral{Token: p.curToken} 10 | 11 | if p.peekTokenIs(token.IDENT) { 12 | p.nextToken() 13 | lit.Name = p.curToken.Literal 14 | } 15 | 16 | if !p.expectPeek(token.LPAREN) { 17 | return nil 18 | } 19 | 20 | if !p.parseFunctionParameters(lit) { 21 | return nil 22 | } 23 | 24 | if !p.expectPeek(token.LBRACE) { 25 | return nil 26 | } 27 | 28 | lit.Body = p.parseBlockStatement() 29 | 30 | return lit 31 | } 32 | 33 | func (p *Parser) parseFunctionParameters(lit *ast.FunctionLiteral) bool { 34 | lit.Defaults = make(map[string]ast.Expression) 35 | for !p.peekTokenIs(token.RPAREN) { 36 | p.nextToken() 37 | 38 | if p.curTokenIs(token.COMMA) { 39 | continue 40 | } 41 | 42 | ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} 43 | lit.Parameters = append(lit.Parameters, ident) 44 | 45 | if p.peekTokenIs(token.ASSIGN) { 46 | p.nextToken() 47 | p.nextToken() 48 | lit.Defaults[ident.Value] = p.parseExpression(LOWEST) 49 | } else { 50 | if len(lit.Defaults) > 0 { 51 | return false 52 | } 53 | } 54 | 55 | if !(p.peekTokenIs(token.COMMA) || p.peekTokenIs(token.RPAREN)) { 56 | return false 57 | } 58 | } 59 | 60 | return p.expectPeek(token.RPAREN) 61 | } 62 | 63 | func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression { 64 | exp := &ast.CallExpression{Token: p.curToken, Function: function} 65 | exp.Arguments = p.parseExpressionList(token.RPAREN) 66 | return exp 67 | } 68 | -------------------------------------------------------------------------------- /parser/identifier.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | ) 6 | 7 | func (p *Parser) parseIdentifier() ast.Expression { 8 | return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} 9 | } 10 | -------------------------------------------------------------------------------- /parser/if.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseIfExpression() ast.Expression { 9 | expression := &ast.IfExpression{Token: p.curToken} 10 | 11 | if !p.expectPeek(token.LPAREN) { 12 | return nil 13 | } 14 | 15 | p.nextToken() 16 | expression.Condition = p.parseExpression(LOWEST) 17 | 18 | if !p.expectPeek(token.RPAREN) { 19 | return nil 20 | } 21 | 22 | if !p.expectPeek(token.LBRACE) { 23 | return nil 24 | } 25 | 26 | expression.Consequence = p.parseBlockStatement() 27 | 28 | if p.peekTokenIs(token.ELSE) { 29 | p.nextToken() 30 | if p.peekTokenIs(token.IF) { 31 | p.nextToken() 32 | expression.Alternative = &ast.BlockStatement{ 33 | Statements: []ast.Statement{ 34 | &ast.ExpressionStatement{ 35 | Expression: p.parseIfExpression(), 36 | }, 37 | }, 38 | } 39 | return expression 40 | } 41 | 42 | if !p.expectPeek(token.LBRACE) { 43 | return nil 44 | } 45 | 46 | expression.Alternative = p.parseBlockStatement() 47 | } 48 | 49 | return expression 50 | } 51 | -------------------------------------------------------------------------------- /parser/import.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseImport() ast.Expression { 9 | exp := &ast.Import{Token: p.curToken} 10 | exp.Identifiers = make(map[string]*ast.Identifier) 11 | for p.curToken.Line == p.peekToken.Line { 12 | p.nextToken() 13 | identifier := &ast.Identifier{Value: p.curToken.Literal} 14 | exp.Identifiers[p.curToken.Literal] = identifier 15 | if p.peekTokenIs(token.COMMA) { 16 | p.nextToken() 17 | } 18 | if p.peekTokenIs(token.EOF) { 19 | break 20 | } 21 | } 22 | 23 | return exp 24 | } 25 | -------------------------------------------------------------------------------- /parser/index.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression { 9 | exp := &ast.IndexExpression{Token: p.curToken, Left: left} 10 | 11 | p.nextToken() 12 | exp.Index = p.parseExpression(LOWEST) 13 | if !p.expectPeek(token.RBRACKET) { 14 | return nil 15 | } 16 | 17 | return exp 18 | } 19 | -------------------------------------------------------------------------------- /parser/integer.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/NuruProgramming/Nuru/ast" 8 | ) 9 | 10 | func (p *Parser) parseIntegerLiteral() ast.Expression { 11 | lit := &ast.IntegerLiteral{Token: p.curToken} 12 | 13 | value, err := strconv.ParseInt(p.curToken.Literal, 0, 64) 14 | if err != nil { 15 | msg := fmt.Sprintf("Mstari %d: Hatuwezi kuparse %q kama namba", p.curToken.Line, p.curToken.Literal) 16 | p.errors = append(p.errors, msg) 17 | return nil 18 | } 19 | lit.Value = value 20 | 21 | return lit 22 | } 23 | -------------------------------------------------------------------------------- /parser/null.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | ) 6 | 7 | func (p *Parser) parseNull() ast.Expression { 8 | return &ast.Null{Token: p.curToken} 9 | } 10 | -------------------------------------------------------------------------------- /parser/package.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parsePackage() ast.Expression { 9 | expression := &ast.Package{Token: p.curToken} 10 | p.nextToken() 11 | expression.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} 12 | 13 | if !p.expectPeek(token.LBRACE) { 14 | return nil 15 | } 16 | expression.Block = p.parseBlockStatement() 17 | return expression 18 | } 19 | -------------------------------------------------------------------------------- /parser/statements.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/NuruProgramming/Nuru/ast" 7 | "github.com/NuruProgramming/Nuru/token" 8 | ) 9 | 10 | func (p *Parser) parseStatement() ast.Statement { 11 | switch p.curToken.Type { 12 | case token.LET: 13 | return p.parseLetStatement() 14 | case token.RETURN: 15 | return p.parseReturnStatement() 16 | case token.BREAK: 17 | return p.parseBreak() 18 | case token.CONTINUE: 19 | return p.parseContinue() 20 | default: 21 | return p.parseExpressionStatement() 22 | } 23 | } 24 | 25 | func (p *Parser) parseLetStatement() *ast.LetStatement { 26 | stmt := &ast.LetStatement{Token: p.curToken} 27 | 28 | if !p.expectPeek(token.IDENT) { 29 | return nil 30 | } 31 | 32 | stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} 33 | 34 | if !p.expectPeek(token.ASSIGN) { 35 | return nil 36 | } 37 | 38 | p.nextToken() 39 | 40 | stmt.Value = p.parseExpression(LOWEST) 41 | 42 | if p.peekTokenIs(token.SEMICOLON) { 43 | p.nextToken() 44 | } 45 | 46 | return stmt 47 | } 48 | 49 | func (p *Parser) parseReturnStatement() *ast.ReturnStatement { 50 | stmt := &ast.ReturnStatement{Token: p.curToken} 51 | p.nextToken() 52 | 53 | stmt.ReturnValue = p.parseExpression(LOWEST) 54 | 55 | if p.peekTokenIs(token.SEMICOLON) { 56 | p.nextToken() 57 | } 58 | 59 | return stmt 60 | } 61 | 62 | func (p *Parser) parseBlockStatement() *ast.BlockStatement { 63 | block := &ast.BlockStatement{Token: p.curToken} 64 | block.Statements = []ast.Statement{} 65 | 66 | p.nextToken() 67 | 68 | for !p.curTokenIs(token.RBRACE) { 69 | if p.curTokenIs(token.EOF) { 70 | msg := fmt.Sprintf("Mstari %d: Hukufunga Mabano '}'", p.curToken.Line) 71 | p.errors = append(p.errors, msg) 72 | return nil 73 | } 74 | stmt := p.parseStatement() 75 | block.Statements = append(block.Statements, stmt) 76 | p.nextToken() 77 | } 78 | 79 | return block 80 | } 81 | -------------------------------------------------------------------------------- /parser/string.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | ) 6 | 7 | func (p *Parser) parseStringLiteral() ast.Expression { 8 | return &ast.StringLiteral{Token: p.curToken, Value: p.curToken.Literal} 9 | } 10 | -------------------------------------------------------------------------------- /parser/switch.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/NuruProgramming/Nuru/ast" 7 | "github.com/NuruProgramming/Nuru/token" 8 | ) 9 | 10 | func (p *Parser) parseSwitchStatement() ast.Expression { 11 | expression := &ast.SwitchExpression{Token: p.curToken} 12 | 13 | if !p.expectPeek(token.LPAREN) { 14 | return nil 15 | } 16 | 17 | p.nextToken() 18 | expression.Value = p.parseExpression(LOWEST) 19 | 20 | if expression.Value == nil { 21 | return nil 22 | } 23 | 24 | if !p.expectPeek(token.RPAREN) { 25 | return nil 26 | } 27 | 28 | if !p.expectPeek(token.LBRACE) { 29 | return nil 30 | } 31 | p.nextToken() 32 | 33 | for !p.curTokenIs(token.RBRACE) { 34 | 35 | if p.curTokenIs(token.EOF) { 36 | msg := fmt.Sprintf("Mstari %d: Haukufunga ENDAPO (SWITCH)", p.curToken.Line) 37 | p.errors = append(p.errors, msg) 38 | return nil 39 | } 40 | tmp := &ast.CaseExpression{Token: p.curToken} 41 | 42 | if p.curTokenIs(token.DEFAULT) { 43 | 44 | tmp.Default = true 45 | 46 | } else if p.curTokenIs(token.CASE) { 47 | 48 | p.nextToken() 49 | 50 | if p.curTokenIs(token.DEFAULT) { 51 | tmp.Default = true 52 | } else { 53 | tmp.Expr = append(tmp.Expr, p.parseExpression(LOWEST)) 54 | for p.peekTokenIs(token.COMMA) { 55 | p.nextToken() 56 | p.nextToken() 57 | tmp.Expr = append(tmp.Expr, p.parseExpression(LOWEST)) 58 | } 59 | } 60 | } else { 61 | msg := fmt.Sprintf("Mstari %d: Tulitegemea Kauli IKIWA (CASE) au KAWAIDA (DEFAULT) lakini tumepewa: %s", p.curToken.Line, p.curToken.Type) 62 | p.errors = append(p.errors, msg) 63 | return nil 64 | } 65 | 66 | if !p.expectPeek(token.LBRACE) { 67 | return nil 68 | } 69 | 70 | tmp.Block = p.parseBlockStatement() 71 | p.nextToken() 72 | expression.Choices = append(expression.Choices, tmp) 73 | } 74 | 75 | count := 0 76 | for _, c := range expression.Choices { 77 | if c.Default { 78 | count++ 79 | } 80 | } 81 | if count > 1 { 82 | msg := fmt.Sprintf("Kauli ENDAPO (SWITCH) hua na kauli 'KAWAIDA' (DEFAULT) moja tu! Wewe umeweka %d", count) 83 | p.errors = append(p.errors, msg) 84 | return nil 85 | 86 | } 87 | return expression 88 | 89 | } 90 | -------------------------------------------------------------------------------- /parser/while.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/NuruProgramming/Nuru/ast" 5 | "github.com/NuruProgramming/Nuru/token" 6 | ) 7 | 8 | func (p *Parser) parseWhileExpression() ast.Expression { 9 | expression := &ast.WhileExpression{Token: p.curToken} 10 | 11 | if !p.expectPeek(token.LPAREN) { 12 | return nil 13 | } 14 | 15 | p.nextToken() 16 | expression.Condition = p.parseExpression(LOWEST) 17 | 18 | if !p.expectPeek(token.RPAREN) { 19 | return nil 20 | } 21 | 22 | if !p.expectPeek(token.LBRACE) { 23 | return nil 24 | } 25 | 26 | expression.Consequence = p.parseBlockStatement() 27 | 28 | return expression 29 | } 30 | -------------------------------------------------------------------------------- /repl/docs/en/arrays.md: -------------------------------------------------------------------------------- 1 | # Arrays in Nuru 2 | 3 | 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. 4 | 5 | ## Creating Arrays 6 | 7 | To create an array, use square brackets [] and separate items with commas: 8 | 9 | ```s 10 | orodha = [1, "pili", kweli] 11 | ``` 12 | ## Accessing and Modifying Array Elements 13 | 14 | Arrays in Nuru are zero-indexed. To access an element, use the element's index in square brackets: 15 | 16 | ```s 17 | namba = [10, 20, 30] 18 | jina = namba[1] // jina is 20 19 | ``` 20 | 21 | You can reassign an element in an array using its index: 22 | 23 | ```s 24 | namba[1] = 25 25 | ``` 26 | 27 | ## Concatenating Arrays 28 | 29 | To concatenate two or more arrays, use the + operator: 30 | 31 | ```s 32 | a = [1, 2, 3] 33 | b = [4, 5, 6] 34 | c = a + b 35 | // c is now [1, 2, 3, 4, 5, 6] 36 | ``` 37 | 38 | ## Checking for Array Membership 39 | 40 | Use the `ktk` keyword to check if an item exists in an array: 41 | 42 | ```s 43 | namba = [10, 20, 30] 44 | andika(20 ktk namba) // will print kweli 45 | ``` 46 | 47 | ## Looping Over Arrays 48 | 49 | You can use the kwa and ktk keywords to loop over array elements. To loop over just the values, use the following syntax: 50 | 51 | ``` 52 | namba = [1, 2, 3, 4, 5] 53 | 54 | kwa thamani ktk namba { 55 | andika(thamani) 56 | } 57 | ``` 58 | 59 | To loop over both index and value pairs, use this syntax: 60 | 61 | ```s 62 | majina = ["Juma", "Asha", "Haruna"] 63 | 64 | kwa idx, jina ktk majina { 65 | andika(idx, "-", jina) 66 | } 67 | ``` 68 | 69 | ## Array Methods 70 | 71 | Arrays in Nuru have several built-in methods: 72 | 73 | ### idadi() 74 | 75 | idadi() returns the length of an array: 76 | 77 | ```s 78 | a = [1, 2, 3] 79 | urefu = a.idadi() 80 | andika(urefu) // will print 3 81 | ``` 82 | 83 | ### sukuma() 84 | 85 | sukuma() adds one or more items to the end of an array: 86 | 87 | ```s 88 | a = [1, 2, 3] 89 | a.sukuma("s", "g") 90 | andika(a) // will print [1, 2, 3, "s", "g"] 91 | ``` 92 | 93 | ### yamwisho() 94 | 95 | yamwisho() returns the last item in an array, or tupu if the array is empty: 96 | 97 | ```s 98 | a = [1, 2, 3] 99 | mwisho = a.yamwisho() 100 | andika(mwisho) // will print 3 101 | 102 | b = [] 103 | mwisho = b.yamwisho() 104 | andika(mwisho) // will print tupu 105 | ``` 106 | 107 | ### map() 108 | 109 | 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: 110 | ```s 111 | a = [1, 2, 3] 112 | 113 | b = a.map(unda(x){rudisha x*2}) 114 | 115 | andika(b) // [2, 4, 6] 116 | ``` 117 | 118 | ### chuja() 119 | 120 | 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: 121 | ```s 122 | a = [1, 2, 3, 4] 123 | 124 | b = a.chuja(unda(x){ 125 | kama (x % 2 == 0) 126 | {rudisha kweli} 127 | rudisha sikweli 128 | }) 129 | 130 | andika(b) // [2, 4] 131 | ``` 132 | 133 | With this information, you can now effectively work with arrays in Nuru, making it easy to manipulate collections of data in your programs. 134 | -------------------------------------------------------------------------------- /repl/docs/en/bool.md: -------------------------------------------------------------------------------- 1 | # Working with Booleans in Nuru 2 | 3 | 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. 4 | 5 | ## Evaluating Boolean Expressions 6 | 7 | ### Evaluating Simple Expressions 8 | 9 | In Nuru, you can evaluate simple expressions that return a boolean value: 10 | 11 | ```s 12 | andika(1 > 2) // Output: `sikweli` 13 | 14 | andika(1 + 3 < 10) // Output: `kweli` 15 | ``` 16 | 17 | ### Evaluating Complex Expressions 18 | 19 | In Nuru, you can use boolean operators to evaluate complex expressions: 20 | 21 | ```s 22 | a = 5 23 | b = 10 24 | c = 15 25 | 26 | result = (a < b) && (b < c) 27 | 28 | kama (result) { 29 | andika("Both conditions are true") 30 | } sivyo { 31 | andika("At least one condition is false") 32 | } 33 | // Output: "Both conditions are true" 34 | ``` 35 | 36 | 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". 37 | 38 | ## Boolean Operators 39 | 40 | Nuru has several boolean operators that you can use to evaluate expressions: 41 | 42 | ### The && Operator 43 | 44 | The && operator evaluates to true only if both operands are true. Here's an example: 45 | 46 | ```s 47 | andika(kweli && kweli) // Output: `kweli` 48 | 49 | andika(kweli && sikweli) // Output: `sikweli` 50 | ``` 51 | 52 | ### The || Operator 53 | 54 | The || operator evaluates to true if at least one of the operands is true. Here's an example: 55 | 56 | ```s 57 | andika(kweli || sikweli) // Output: `kweli` 58 | 59 | andika(sikweli || sikweli) // Output: `sikweli` 60 | ``` 61 | 62 | ### The ! Operator 63 | 64 | The ! operator negates the value of the operand. Here's an example: 65 | 66 | ```s 67 | andika(!kweli) // Output: `sikweli` 68 | 69 | andika(!sikweli) // Output: `kweli` 70 | ``` 71 | 72 | ## Working with Boolean Values in Loops 73 | 74 | In Nuru, you can use boolean expressions in loops to control their behavior. Here's an example: 75 | 76 | ```s 77 | namba = [1, 2, 3, 4, 5] 78 | 79 | kwa thamani ktk namba { 80 | kama (thamani % 2 == 0) { 81 | andika(thamani, "is even") 82 | } sivyo { 83 | andika(thamani, "is odd") 84 | } 85 | } 86 | // Output: 87 | // 1 is odd 88 | // 2 is even 89 | // 3 is odd 90 | // 4 is even 91 | // 5 is odd 92 | ``` 93 | 94 | 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. 95 | 96 | 97 | 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. -------------------------------------------------------------------------------- /repl/docs/en/builtins.md: -------------------------------------------------------------------------------- 1 | # Built-in Functions in Nuru 2 | 3 | Nuru has several built-in functions that perform specific tasks. 4 | 5 | ## The andika() Function 6 | 7 | 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: 8 | 9 | ```s 10 | andika(1, 2, 3) // Output: "1 2 3" 11 | ``` 12 | 13 | ## The jaza() Function 14 | 15 | 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: 16 | 17 | ```s 18 | fanya salamu = unda() { 19 | fanya jina = jaza("Unaitwa nani? ") 20 | andika("Mambo vipi", jina) 21 | } 22 | 23 | salamu() 24 | ``` 25 | 26 | 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. 27 | 28 | ## The aina() Function 29 | 30 | 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: 31 | 32 | ```s 33 | aina(2) // Output: "NAMBA" 34 | aina("Nuru") // Output: "NENO" 35 | ``` 36 | 37 | ## The fungua() Function 38 | 39 | 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: 40 | 41 | ```s 42 | faili = fungua("data.txt") 43 | ``` 44 | 45 | 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. -------------------------------------------------------------------------------- /repl/docs/en/comments.md: -------------------------------------------------------------------------------- 1 | # Comments in Nuru 2 | 3 | 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. 4 | 5 | ## Single-Line Comments 6 | 7 | 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: 8 | 9 | ```s 10 | // This line will be ignored by the Nuru interpreter 11 | ``` 12 | 13 | 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. 14 | 15 | ## Multi-Line Comments 16 | 17 | 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: 18 | 19 | ```s 20 | /* 21 | These lines 22 | Will 23 | be 24 | ignored 25 | */ 26 | ``` 27 | 28 | 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. 29 | 30 | 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. -------------------------------------------------------------------------------- /repl/docs/en/dictionaries.md: -------------------------------------------------------------------------------- 1 | # Dictionaries in Nuru 2 | 3 | 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. 4 | 5 | ## Creating Dictionaries 6 | 7 | Dictionaries are enclosed in curly braces {} and consist of keys and values separated by colons. Here's an example of defining a dictionary: 8 | 9 | ```s 10 | 11 | orodha = {"jina": "Juma", "umri": 25} 12 | ``` 13 | 14 | Keys can be strings, integers, floats, or booleans, while values can be any data type, including strings, integers, floats, booleans, null, or functions: 15 | 16 | ```s 17 | k = { 18 | "jina": "Juma", 19 | "umri": 25, 20 | kweli: "kweli", 21 | "salimu": unda(x) { andika("habari", x) }, 22 | "sina value": tupu 23 | } 24 | ``` 25 | 26 | ## Accessing Elements 27 | 28 | Access individual elements in a dictionary using their keys: 29 | 30 | ```s 31 | 32 | andika(k[kweli]) // kweli 33 | andika(k["salimu"]("Juma")) // habari Juma 34 | ``` 35 | 36 | ## Updating Elements 37 | 38 | Update the value of an element by assigning a new value to its key: 39 | 40 | ```s 41 | k['umri'] = 30 42 | andika(k['umri']) // 30 43 | ``` 44 | 45 | ## Adding New Elements 46 | 47 | Add a new key-value pair to a dictionary by assigning a value to a non-existent key: 48 | 49 | ```s 50 | k["lugha"] = "Kiswahili" 51 | andika(k["lugha"]) // Kiswahili 52 | ``` 53 | 54 | ## Concatenating Dictionaries 55 | 56 | Combine two dictionaries using the + operator: 57 | 58 | ```s 59 | matunda = {"a": "apple", "b": "banana"} 60 | mboga = {"c": "carrot", "d": "daikon"} 61 | vyakula = matunda + mboga 62 | andika(vyakula) // {"a": "apple", "b": "banana", "c": "carrot", "d": "daikon"} 63 | ``` 64 | 65 | ## Checking If a Key Exists in a Dictionary 66 | 67 | Use the ktk keyword to check if a key exists in a dictionary: 68 | 69 | ```s 70 | 71 | "umri" ktk k // kweli 72 | "urefu" ktk k // sikweli 73 | ``` 74 | 75 | ## Looping Over a Dictionary 76 | 77 | Loop over a dictionary to access its keys and values: 78 | 79 | ```s 80 | 81 | hobby = {"a": "asili", "b": "baiskeli", "c": "chakula"} 82 | kwa i, v ktk hobby { 83 | andika(i, "=>", v) 84 | } 85 | ``` 86 | Output 87 | ```s 88 | a => asili 89 | b => baiskeli 90 | c => chakula 91 | ``` 92 | 93 | Loop over just the values: 94 | 95 | ```s 96 | kwa v ktk hobby { 97 | andika(v) 98 | } 99 | ``` 100 | Output 101 | ```s 102 | asili 103 | baiskeli 104 | chakula 105 | ``` 106 | 107 | 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. -------------------------------------------------------------------------------- /repl/docs/en/files.md: -------------------------------------------------------------------------------- 1 | # Files in Nuru 2 | 3 | Nuru's ability to deal with files is primitive, and as for now it only allows you to read contents of a file. 4 | 5 | ## Opening a File 6 | 7 | You open a file with the `fungua` keyword. This will return an object of type `FAILI`: 8 | ``` 9 | fileYangu = fungua("file.txt") 10 | 11 | aina(fileYangu) // FAILI 12 | ``` 13 | 14 | ## Reading a File 15 | 16 | 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: 17 | ``` 18 | fileYangu = fungua("file.txt") 19 | 20 | fileYangu.soma() 21 | ``` 22 | -------------------------------------------------------------------------------- /repl/docs/en/for.md: -------------------------------------------------------------------------------- 1 | # For Loops in Nuru 2 | 3 | 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. 4 | 5 | ## Basic Syntax 6 | 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: 7 | 8 | ```s 9 | jina = "lugano" 10 | 11 | kwa i ktk jina { 12 | andika(i) 13 | } 14 | ``` 15 | Output: 16 | 17 | ```s 18 | l 19 | u 20 | g 21 | a 22 | n 23 | o 24 | ``` 25 | 26 | ## Iterating Over Key-Value Pairs 27 | 28 | ### Dictionaries 29 | 30 | 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: 31 | 32 | ```s 33 | kamusi = {"a": "andaa", "b": "baba"} 34 | 35 | kwa v ktk kamusi { 36 | andika(v) 37 | } 38 | ``` 39 | 40 | Output: 41 | 42 | ```s 43 | andaa 44 | baba 45 | ``` 46 | To iterate over both the keys and the values, use two temporary identifiers: 47 | 48 | ```s 49 | 50 | kwa k, v ktk kamusi { 51 | andika(k + " ni " + v) 52 | } 53 | ``` 54 | Output: 55 | 56 | ```s 57 | a ni andaa 58 | b ni baba 59 | ``` 60 | 61 | ### Strings 62 | 63 | To iterate over just the values in a string, use one temporary identifier: 64 | 65 | ```s 66 | kwa v ktk "mojo" { 67 | andika(v) 68 | } 69 | ``` 70 | 71 | Output: 72 | ```s 73 | m 74 | o 75 | j 76 | o 77 | ``` 78 | To iterate over both the keys and the values in a string, use two temporary identifiers: 79 | 80 | ```s 81 | kwa i, v ktk "mojo" { 82 | andika(i, "->", v) 83 | } 84 | ``` 85 | Output: 86 | ```s 87 | 0 -> m 88 | 1 -> o 89 | 2 -> j 90 | 3 -> o 91 | ``` 92 | 93 | ### Lists 94 | 95 | To iterate over just the values in a list, use one temporary identifier: 96 | 97 | ```s 98 | majina = ["juma", "asha", "haruna"] 99 | 100 | kwa v ktk majina { 101 | andika(v) 102 | } 103 | ``` 104 | 105 | Output: 106 | 107 | ```s 108 | juma 109 | asha 110 | haruna 111 | ``` 112 | 113 | To iterate over both the keys and the values in a list, use two temporary identifiers: 114 | 115 | ```s 116 | kwa i, v ktk majina { 117 | andika(i, "-", v) 118 | } 119 | ``` 120 | 121 | Output: 122 | 123 | ```s 124 | 0 - juma 125 | 1 - asha 126 | 2 - haruna 127 | ``` 128 | 129 | ## Break (Vunja) and Continue (Endelea) 130 | 131 | ### Break (Vunja) 132 | 133 | Use the vunja keyword to terminate a loop: 134 | 135 | ```s 136 | 137 | kwa i, v ktk "mojo" { 138 | kama (i == 2) { 139 | andika("nimevunja") 140 | vunja 141 | } 142 | andika(v) 143 | } 144 | ``` 145 | 146 | Output: 147 | 148 | ```s 149 | m 150 | o 151 | nimevunja 152 | ``` 153 | 154 | ### Continue (Endelea) 155 | 156 | Use the endelea keyword to skip a specific iteration: 157 | 158 | ```s 159 | kwa i, v ktk "mojo" { 160 | kama (i == 2) { 161 | andika("nimeruka") 162 | endelea 163 | } 164 | andika(v) 165 | } 166 | ``` 167 | 168 | Output: 169 | 170 | ```s 171 | m 172 | o 173 | nimeruka 174 | o 175 | ``` -------------------------------------------------------------------------------- /repl/docs/en/function.md: -------------------------------------------------------------------------------- 1 | # Functions in Nuru 2 | 3 | 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. 4 | 5 | ## Basic Syntax 6 | 7 | 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: 8 | 9 | ```s 10 | jum = unda(x, y) { 11 | rudisha x + y 12 | } 13 | 14 | jum(2, 3) // 5 15 | ``` 16 | 17 | ## Parameters 18 | 19 | Functions can have zero or any number of arguments. Arguments can be of any type, even other functions: 20 | 21 | ```s 22 | salamu = unda() { 23 | andika("Habari yako") 24 | } 25 | 26 | salamu() 27 | 28 | salamu = unda(jina) { 29 | andika("Habari yako", jina) 30 | } 31 | 32 | salamu("asha") // Habari yako asha 33 | ``` 34 | 35 | ## Default Parameters 36 | 37 | Functions can be provided with default parameters: 38 | 39 | ```s 40 | salimu = unda(salamu="Habari") { 41 | andika(salamu) 42 | } 43 | 44 | salimu() // Habari 45 | salimu("Mambo") // Mambo 46 | ``` 47 | 48 | ## Return (rudisha) 49 | 50 | You can return values with the rudisha keyword. The rudisha keyword will terminate the block and return the value: 51 | 52 | ```s 53 | mfano = unda(x) { 54 | rudisha "nimerudi" 55 | andika(x) 56 | } 57 | 58 | mfano("x") // nimerudi 59 | ``` 60 | 61 | ## Recursion 62 | 63 | Nuru also supports recursion. Here's an example of a recursive Fibonacci function: 64 | 65 | ```s 66 | 67 | fib = unda(n) { 68 | kama (n <= 1) { 69 | rudisha n 70 | } sivyo { 71 | rudisha fib(n-1) + fib(n-2) 72 | } 73 | } 74 | 75 | andika(fib(10)) // 55 76 | ``` 77 | 78 | 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. 79 | 80 | ## Closures 81 | 82 | 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: 83 | 84 | ```s 85 | fanya jum = unda(x) { 86 | rudisha unda(y) { 87 | rudisha x + y 88 | } 89 | } 90 | 91 | fanya jum_x = jum(5) 92 | andika(jum_x(3)) // 8 93 | ``` 94 | 95 | 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. 96 | 97 | 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. -------------------------------------------------------------------------------- /repl/docs/en/identifiers.md: -------------------------------------------------------------------------------- 1 | # Identifiers in Nuru 2 | 3 | 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. 4 | 5 | ## Syntax Rules 6 | 7 | Identifiers can contain letters, numbers, and underscores. However, there are a few rules you must follow when creating identifiers: 8 | - Identifiers cannot start with a number. 9 | - Identifiers are case-sensitive. For example, myVar and myvar are considered distinct identifiers. 10 | 11 | Here are some examples of valid identifiers: 12 | 13 | ```s 14 | fanya birth_year = 2020 15 | andika(birth_year) // 2020 16 | 17 | fanya convert_c_to_p = "C to P" 18 | andika(convert_c_to_p) // "C to P" 19 | ``` 20 | 21 | In the examples above, birth_year and convert_c_to_p are both valid identifiers. 22 | 23 | ## Best Practices 24 | 25 | When choosing identifiers, it's important to follow best practices to ensure your code is clear and easy to understand: 26 | 27 | - Use descriptive names that clearly indicate the purpose or meaning of the variable or function. 28 | - Follow a consistent naming convention, such as camelCase (myVariableName) or snake_case (my_variable_name). 29 | - Avoid using single-letter variable names, except for commonly accepted cases like loop counters (i, j, k). 30 | 31 | By following these best practices when creating identifiers, you will make your Nuru code more readable and maintainable for yourself and others. 32 | -------------------------------------------------------------------------------- /repl/docs/en/ifStatements.md: -------------------------------------------------------------------------------- 1 | # Conditional Statements in Nuru 2 | 3 | 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. 4 | 5 | ## If Statement (Kama) 6 | 7 | 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. 8 | 9 | ```s 10 | kama (2 > 1) { 11 | andika(kweli) // kweli 12 | } 13 | ``` 14 | 15 | In this example, the condition 2 > 1 is true, so the andika(kweli) statement is executed, and the output is kweli. 16 | 17 | ## Else If and Else Blocks (Au Kama and Sivyo) 18 | 19 | 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. 20 | 21 | ```s 22 | 23 | fanya a = 10 24 | 25 | kama (a > 100) { 26 | andika("a imezidi 100") 27 | } au kama (a < 10) { 28 | andika("a ndogo kuliko 10") 29 | } sivyo { 30 | andika("Thamani ya a ni", a) 31 | } 32 | 33 | // The output will be 'Thamani ya a ni 10' 34 | ``` 35 | 36 | 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'. 37 | 38 | 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. -------------------------------------------------------------------------------- /repl/docs/en/json.md: -------------------------------------------------------------------------------- 1 | # JSON in Nuru 2 | 3 | Nuru also makes it easy to deal with JSON. 4 | 5 | ## Import JSONI 6 | 7 | Use the following to import the json module: 8 | ``` 9 | tumia jsoni 10 | ``` 11 | 12 | ## Decoding JSON with dikodi() 13 | Use this to convert a string to a dictionary: 14 | ``` 15 | jsonString = '{ 16 | "error": false, 17 | "category": "Pun", 18 | "type": "single", 19 | "joke": "I was reading a great book about an immortal dog the other day. It was impossible to put down." 20 | }' 21 | 22 | // to make it a dict 23 | 24 | tumia jsoni 25 | 26 | k = jsoni.dikodi(jsonString) 27 | 28 | k["joke"] // I was reading a great book about an immortal dog the other day. It was impossible to put down. 29 | ``` 30 | 31 | ## Encoding JSON with enkodi() 32 | 33 | You can encode JSON with the `enkodi` method, this will turn a dictionary to a string: 34 | ``` 35 | tumia jsoni 36 | 37 | k = { 38 | "a": "apple", 39 | "b": "banana" 40 | } 41 | 42 | j = json.enkodi(k) 43 | ``` 44 | -------------------------------------------------------------------------------- /repl/docs/en/keywords.md: -------------------------------------------------------------------------------- 1 | # KEYWORDS 2 | 3 | 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. 4 | 5 | ## Reserved Keywords 6 | 7 | The table below lists the reserved keywords in Nuru. These words have specific meanings in the language and cannot be used as identifiers: 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
kwelisikweliundafanya
kamaausivyowakati
rudishavunjaendeleatupu
ktkkwabadiliikiwa
kawaida
43 | 44 | ## BuiltIns 45 | 46 | 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: 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
andikaainajazafungua
58 | 59 | 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. -------------------------------------------------------------------------------- /repl/docs/en/net.md: -------------------------------------------------------------------------------- 1 | # HTTP with Nuru 2 | 3 | You can access the internet via http protocol using the `mtandao` module. 4 | 5 | ## Importing 6 | 7 | Import the module with: 8 | ``` 9 | tumia mtandao 10 | ``` 11 | 12 | ## Methods 13 | 14 | ### peruzi() 15 | 16 | Use this as GET method. It can either accept one positional argument which will be the URL: 17 | 18 | ``` 19 | tumia mtandao 20 | 21 | mtandao.peruzi("http://google.com") 22 | ``` 23 | 24 | Or you can use keyword arguments to pass in parameters and headers as shown below. Note that headers and parameters must be a dictionary: 25 | 26 | ``` 27 | tumia mtandao 28 | 29 | url = "http://mysite.com" 30 | headers = {"Authentication": "Bearer XXXX"} 31 | 32 | mtandao.peruzi(yuareli=url, vichwa=headers, mwili=params) 33 | ``` 34 | 35 | ### tuma() 36 | 37 | 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: 38 | 39 | ``` 40 | tumia mtandao 41 | 42 | url = "http://mysite.com" 43 | headers = {"Authentication": "Bearer XXXX"} 44 | params = {"key": "Value"} 45 | 46 | mtandao.tuma(yuareli=url, vichwa=headers, mwili=params) 47 | ``` 48 | -------------------------------------------------------------------------------- /repl/docs/en/null.md: -------------------------------------------------------------------------------- 1 | # Null (Tupu) in Nuru 2 | 3 | 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. 4 | 5 | ## Definition 6 | 7 | A null data type is a data type with no value, defined with the tupu keyword: 8 | 9 | ```s 10 | fanya a = tupu 11 | ``` 12 | ## Evaluation 13 | 14 | When evaluating a null data type in a conditional expression, it will evaluate to false: 15 | 16 | ```s 17 | kama (a) { 18 | andika("niko tupu") 19 | } sivyo { 20 | andika("nimevaa nguo") 21 | } 22 | 23 | // Output: nimevaa nguo 24 | ``` 25 | 26 | 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. -------------------------------------------------------------------------------- /repl/docs/en/numbers.md: -------------------------------------------------------------------------------- 1 | # INTEGERS (NAMBA) AND FLOATS (DESIMALI) 2 | 3 | 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. 4 | 5 | ## PRECEDENCE 6 | 7 | Integers and floats behave as expected in mathematical operations, following the BODMAS rule: 8 | ```go 9 | 2 + 3 * 5 // 17 10 | 11 | fanya a = 2.5 12 | fanya b = 3/5 13 | 14 | a + b // 2.8 15 | ``` 16 | 17 | ## UNARY INCREMENTS 18 | 19 | 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: 20 | 21 | ```go 22 | fanya i = 2.4 23 | 24 | i++ // 3.4 25 | ``` 26 | 27 | ## SHORTHAND ASSIGNMENT 28 | 29 | Nuru supports shorthand assignments with +=, -=, /=, *=, and %=: 30 | You 31 | ```go 32 | fanya i = 2 33 | 34 | i *= 3 // 6 35 | i /= 2 // 3 36 | i += 100 // 103 37 | i -= 10 // 93 38 | i %= 90 // 3 39 | ``` 40 | 41 | ## NEGATIVE NUMBERS 42 | 43 | Negative numbers also behave as expected: 44 | 45 | ```go 46 | fanya i = -10 47 | 48 | wakati (i < 0) { 49 | andika(i) 50 | i++ 51 | } 52 | 53 | ``` 54 | Output: 55 | ```s 56 | -10 57 | -9 58 | -8 59 | -7 60 | -6 61 | -5 62 | -4 63 | -3 64 | -2 65 | -1 66 | 0 67 | 1 68 | 2 69 | 3 70 | 4 71 | 5 72 | 6 73 | 7 74 | 8 75 | 9 76 | ``` 77 | -------------------------------------------------------------------------------- /repl/docs/en/operators.md: -------------------------------------------------------------------------------- 1 | # OPERATORS 2 | 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. 3 | 4 | ## ASSIGNMENT 5 | 6 | Assuming `i` and `v` are predefined variables, Nuru supports the following assignment operators: 7 | 8 | - `i = v`: which is the regular assignment operator 9 | - `i += v`: which is the equivalent of `i = i + v` 10 | - `i -= v`: which is the equivalent of `i = i - v` 11 | - `i *= v`: which is the equivalent of `i = i * v` 12 | - `i /= v`: which is the equivalent of `i = i / v` 13 | - `i += v`: which is the equivalent of `i = i + v` 14 | 15 | For `strings`, `arrays` and `dictionaries`, the `+=` sign operator is permissible. Example: 16 | ``` 17 | list1 += list2 // this is equivalent to list1 = list1 + list2 18 | ``` 19 | 20 | ## ARITHMETIC OPERATORS 21 | 22 | Nuru supports the following arithmetic operators: 23 | 24 | - `+`: Additon 25 | - `-`: Subtraction 26 | - `*`: Multiplication 27 | - `/`: Division 28 | - `%`: Modulo (ie the remainder of a division) 29 | - `**`: Exponential power (eg: `2**3 = 8`) 30 | 31 | ## COMPARISON OPERATORS 32 | 33 | Nuru supports the following comparison operators: 34 | 35 | - `==`: Equal to 36 | - `!=`: Not equal to 37 | - `>`: Greater than 38 | - `>=`: Greater than or equal to 39 | - `<`: Less than 40 | - `<=`: Less than or equal to 41 | 42 | ## MEMBER OPERATOR 43 | 44 | The member operator in Nuru is `ktk`. It will check if an object exists in another object: 45 | ```go 46 | fanya majina = ['juma', 'asha', 'haruna'] 47 | 48 | "haruna" ktk majina // kweli 49 | "halima" ktk majina // sikweli 50 | ``` 51 | 52 | ## LOGIC OPERATORS 53 | 54 | Nuru supports the following logic operators: 55 | 56 | - `&&`: Logical `AND`. It will evaluate to true if both are true, otherwise it will evaluate to false. 57 | - `||`: Logical `OR`. It will evaluate to false if both are false, otherwise it will evaluate to true. 58 | - `!`: Logical `NOT`. It will evaluate to the opposite of a given expression. 59 | 60 | ## PRECEDENCE OF OPERATORS 61 | 62 | Operators have the following precedence, starting from the highest priority to the lowest: 63 | 64 | - `()` : Items in paranthesis have the highest priority 65 | - `!`: Negation 66 | - `%`: Modulo 67 | - `**`: Exponential power 68 | - `/, *`: Division and Multiplication 69 | - `+, +=, -, -=`: Addition and Subtraction 70 | - `>, >=, <, <=`: Comparison operators 71 | - `==, !=`: Equal or Not Equal to 72 | - `=`: Assignment Operator 73 | - `ktk`: Member Operator 74 | - `&&, ||`: Logical AND and OR 75 | 76 | Understanding operators in Nuru allows you to create complex expressions, perform calculations, and make decisions based on the values of variables. 77 | -------------------------------------------------------------------------------- /repl/docs/en/packages.md: -------------------------------------------------------------------------------- 1 | # Packages in Nuru 2 | 3 | You can use third packages written in Nuru with the following conditions: 4 | 5 | - The package file MUST be in the same directory 6 | - The package file MUST end with `nr` 7 | - The package name and package file MUST have the same name (eg: if `pakeji hesabu` then the file name must be `hesabu.nr`) 8 | - The package must have the following structure: 9 | ``` 10 | // imports if any 11 | 12 | pakeji [name of package] { 13 | andaa = unda() { // the andaa function is mandatory even if its empty 14 | 15 | } 16 | [body of package] 17 | } 18 | ``` 19 | - The package must be initialized with the `andaa` keyword (see above). 20 | 21 | 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`. 22 | 23 | A variable being globally available means that the variable can be accessed and manipulated by all other methods in the package. 24 | 25 | 26 | Below is an example Sarufi package: 27 | ``` 28 | // import modules 29 | tumia mtandao 30 | tumia jsoni 31 | 32 | // package body 33 | pakeji sarufi { 34 | 35 | // initialize function 36 | andaa = unda(file) { 37 | config = fungua(file) // read passwords from json file 38 | configString = config.soma() 39 | 40 | configDict = jsoni.dikodi(configString) // convert it to a dict 41 | clientID = configDict["client_id"] 42 | clientSecret = configDict["client_secret"] 43 | 44 | // fill in params 45 | params = {"client_id": clientID, "client_secret": clientSecret} 46 | 47 | // get response 48 | resp = mtandao.tuma(yuareli="https://api.sarufi.io/api/access_token", mwili=params) 49 | tokenDict = jsoni.dikodi(resp) 50 | 51 | // extract token and make it globally available 52 | @.token = tokenDict["access_token"] 53 | 54 | // make the "Bearer " globally available 55 | @.Auth = "Bearer " + @.token 56 | } 57 | 58 | // a method to get token 59 | tokenYangu = unda() { 60 | rudisha @.token 61 | } 62 | 63 | // a method to create new chatbots. 64 | // the data should be a dict 65 | tengenezaChatbot = unda(data) { 66 | majibu = mtandao.tuma(yuareli="https://api.sarufi.io/chatbot", vichwa={"Authorization": @.Auth}, mwili = data) 67 | rudisha majibu 68 | } 69 | 70 | // a method to get all available chatbots 71 | pataChatbotZote = unda() { 72 | majibu = mtandao.peruzi(yuareli="https://api.sarufi.io/chatbots", vichwa={"Authorization": @.Auth}) 73 | rudisha majibu 74 | } 75 | } 76 | ``` 77 | -------------------------------------------------------------------------------- /repl/docs/en/range.md: -------------------------------------------------------------------------------- 1 | ## Range Function (mfululizo) 2 | 3 | The `mfululizo` function generates a sequence of numbers. It can be used in loops or to create arrays of sequential numbers. 4 | 5 | ### Syntax 6 | 7 | ```go 8 | mfululizo(end) 9 | mfululizo(start, end) 10 | mfululizo(start, end, step) 11 | ``` 12 | 13 | ### Parameters 14 | 15 | - `end`: The upper limit of the sequence (exclusive). 16 | - `start` (optional): The starting value of the sequence. Default is 0. 17 | - `step` (optional): The increment between each number in the sequence. Default is 1. 18 | 19 | ### Return Value 20 | 21 | Returns an array of integers. 22 | 23 | ### Examples 24 | 25 | ```go 26 | // Generate numbers from 0 to 4 27 | kwa i katika mfululizo(5) { 28 | andika(i) 29 | } 30 | // Output: 0 1 2 3 4 31 | 32 | // Generate numbers from 1 to 9 33 | kwa i katika mfululizo(1, 10) { 34 | andika(i) 35 | } 36 | // Output: 1 2 3 4 5 6 7 8 9 37 | 38 | // Generate even numbers from 0 to 8 39 | kwa i katika mfululizo(0, 10, 2) { 40 | andika(i) 41 | } 42 | // Output: 0 2 4 6 8 43 | 44 | // Generate numbers in reverse order 45 | kwa i katika mfululizo(10, 0, -1) { 46 | andika(i) 47 | } 48 | // Output: 10 9 8 7 6 5 4 3 2 1 49 | ``` 50 | 51 | ### Notes 52 | 53 | - The `end` value is exclusive, meaning the sequence will stop before reaching this value. 54 | - If a negative `step` is provided, `start` should be greater than `end`. 55 | - The `step` value cannot be zero. 56 | -------------------------------------------------------------------------------- /repl/docs/en/strings.md: -------------------------------------------------------------------------------- 1 | # Strings in Nuru 2 | 3 | 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. 4 | 5 | ## Basic Syntax 6 | 7 | Strings can be enclosed in either single quotes '' or double quotes "": 8 | 9 | ```s 10 | andika("mambo") // mambo 11 | 12 | fanya a = 'niaje' 13 | 14 | andika("mambo", a) // mambo niaje 15 | ``` 16 | 17 | ## Concatenating Strings 18 | 19 | Strings can be concatenated using the + operator: 20 | 21 | ```s 22 | fanya a = "habari" + " " + "yako" 23 | 24 | andika(a) // habari yako 25 | 26 | fanya b = "habari" 27 | 28 | b += " yako" 29 | 30 | // habari yako 31 | ``` 32 | 33 | You can also repeat a string n number of times using the * operator: 34 | 35 | ```s 36 | andika("mambo " * 4) 37 | 38 | // mambo mambo mambo mambo 39 | 40 | fanya a = "habari" 41 | 42 | a *= 4 43 | 44 | // habarihabarihabarihabari 45 | ``` 46 | 47 | ## Looping over a String 48 | 49 | You can loop through a string using the kwa keyword: 50 | 51 | ```s 52 | fanya jina = "avicenna" 53 | 54 | kwa i ktk jina {andika(i)} 55 | ``` 56 | Output 57 | ```s 58 | a 59 | v 60 | i 61 | c 62 | e 63 | n 64 | n 65 | a 66 | ``` 67 | 68 | And for key-value pairs: 69 | 70 | ```s 71 | kwa i, v ktk jina { 72 | andika(i, "=>", v) 73 | } 74 | ``` 75 | Output 76 | ```s 77 | 0 => a 78 | 1 => v 79 | 2 => i 80 | 3 => c 81 | 4 => e 82 | 5 => n 83 | 6 => n 84 | 7 => a 85 | ``` 86 | 87 | ## Comparing Strings 88 | 89 | You can compare two strings using the == operator: 90 | 91 | ```s 92 | fanya a = "nuru" 93 | 94 | andika(a == "nuru") // kweli 95 | 96 | andika(a == "mambo") // sikweli 97 | ``` 98 | 99 | ## String Methods 100 | 101 | ### idadi() 102 | 103 | You can find the length of a string using the idadi method. It does not accept any parameters. 104 | 105 | ```s 106 | fanya a = "mambo" 107 | a.idadi() // 5 108 | ``` 109 | 110 | ### herufikubwa() 111 | 112 | This method converts a string to uppercase. It does not accept any parameters. 113 | 114 | ```s 115 | fanya a = "nuru" 116 | a.herufikubwa() // NURU 117 | ``` 118 | 119 | ### herufindogo 120 | 121 | This method converts a string to lowercase. It does not accept any parameters. 122 | 123 | ```s 124 | fanya a = "NURU" 125 | a.herufindogo() // nuru 126 | ``` 127 | 128 | ### gawa 129 | 130 | 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. 131 | 132 | Example without a parameter: 133 | 134 | ```s 135 | fanya a = "nuru mambo habari" 136 | fanya b = a.gawa() 137 | andika(b) // ["nuru", "mambo", "habari"] 138 | ``` 139 | 140 | Example with a parameter: 141 | 142 | ```s 143 | fanya a = "nuru,mambo,habari" 144 | fanya b = a.gawa(",") 145 | andika(b) // ["nuru", "mambo", "habari"] 146 | ``` 147 | 148 | By understanding strings and their manipulation in Nuru, you can effectively work with text data in your programs. -------------------------------------------------------------------------------- /repl/docs/en/switch.md: -------------------------------------------------------------------------------- 1 | # Switch Statements in Nuru 2 | 3 | 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. 4 | 5 | ## Basic Syntax 6 | 7 | You initialize a switch statement with the badili keyword, the expression inside parentheses (), and all cases enclosed within curly braces {}. 8 | 9 | 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: 10 | 11 | ```s 12 | fanya a = 2 13 | 14 | badili (a){ 15 | ikiwa 3 { 16 | andika("a ni tatu") 17 | } 18 | ikiwa 2 { 19 | andika ("a ni mbili") 20 | } 21 | } 22 | ``` 23 | 24 | ## Multiple Values in a Case 25 | 26 | Multiple possibilities can be assigned to a single case (ikiwa) statement: 27 | 28 | ```s 29 | badili (a) { 30 | ikiwa 1,2,3 { 31 | andika("a ni kati ya 1, 2 au 3") 32 | } 33 | ikiwa 4 { 34 | andika("a ni 4") 35 | } 36 | } 37 | ``` 38 | 39 | ## Default Case (kawaida) 40 | 41 | The default statement will be executed when no condition is satisfied. The default statement is represented by kawaida: 42 | 43 | ```s 44 | fanya z = 20 45 | 46 | badili(z) { 47 | ikiwa 10 { 48 | andika("kumi") 49 | } 50 | ikiwa 30 { 51 | andika("thelathini") 52 | } 53 | kawaida { 54 | andika("ishirini") 55 | } 56 | } 57 | ``` 58 | 59 | By understanding switch statements in Nuru, you can create more efficient and organized code that can handle multiple conditions easily. -------------------------------------------------------------------------------- /repl/docs/en/time.md: -------------------------------------------------------------------------------- 1 | # Time in Nuru 2 | 3 | ## Importing Time 4 | 5 | To use Time in Nuru, you first have to import the `muda` module as follows: 6 | 7 | ```so 8 | tumia muda 9 | ``` 10 | 11 | --- 12 | 13 | ## Time Methods 14 | 15 | ### `hasahivi()` 16 | 17 | 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`. 18 | 19 | ```so 20 | tumia muda 21 | 22 | saivi = muda.hasahivi() 23 | ``` 24 | 25 | --- 26 | 27 | ### `leo()` 28 | 29 | To get today’s date in the format `dd-MM-YYYY`: 30 | 31 | ```so 32 | tumia muda 33 | 34 | leo = muda.leo() 35 | ``` 36 | 37 | --- 38 | 39 | ### `tangu(time)` 40 | 41 | 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. 42 | 43 | ```so 44 | tumia muda 45 | 46 | muda_ulioyopita = muda.tangu("15:00:00 01-01-2024") 47 | ``` 48 | 49 | --- 50 | 51 | ### `lala(sekunde)` 52 | 53 | Pauses the program for the given number of seconds: 54 | 55 | ```so 56 | tumia muda 57 | 58 | muda.lala(5) // sleeps for 5 seconds 59 | ``` 60 | 61 | --- 62 | 63 | ### `baada_ya(sekunde)` 64 | 65 | Returns a `muda` object representing the time after the given number of seconds from now. 66 | 67 | ```so 68 | tumia muda 69 | 70 | baadaye = muda.baada_ya(60) // one minute from now 71 | ``` 72 | 73 | --- 74 | 75 | ### `tofauti(muda1, muda2)` 76 | 77 | Returns the difference between two time values in seconds. 78 | 79 | ```so 80 | tumia muda 81 | 82 | saa1 = muda.hasahivi() 83 | saa2 = muda.baada_ya(30) 84 | 85 | tofauti = muda.tofauti(saa2, saa1) // 30 86 | ``` 87 | 88 | --- 89 | 90 | ### `ongeza(...)` 91 | 92 | To add time to a `muda` object. You must specify at least one of the following fields `sekunde`, `dakika`, `masaa`, `siku`, `wiki`, `miezi`, `miaka`. 93 | 94 | Example: 95 | 96 | ```so 97 | tumia muda 98 | 99 | sasa = muda.hasahivi() 100 | kesho = sasa.ongeza(siku=1) 101 | mwakani = sasa.ongeza(miaka=1) 102 | ``` 103 | -------------------------------------------------------------------------------- /repl/docs/en/while.md: -------------------------------------------------------------------------------- 1 | # WHILE (WAKATI) 2 | 3 | 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. 4 | 5 | ## Basic Syntax 6 | 7 | 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 `{}`: 8 | ```s 9 | fanya i = 1 10 | 11 | wakati (i <= 5) { 12 | andika(i) 13 | i++ 14 | } 15 | ``` 16 | Output 17 | ```s 18 | 1 19 | 2 20 | 3 21 | 4 22 | 5 23 | ``` 24 | 25 | ## Break (vunja) and Continue (endelea) 26 | ### Break (Vunja) 27 | 28 | Use the vunja keyword to terminate a loop: 29 | 30 | ```s 31 | fanya i = 1 32 | 33 | wakati (i < 5) { 34 | kama (i == 3) { 35 | andika("nimevunja") 36 | vunja 37 | } 38 | andika(i) 39 | i++ 40 | } 41 | ``` 42 | Output 43 | ```s 44 | 1 45 | 2 46 | nimevunja 47 | ``` 48 | 49 | ### Continue (Endelea) 50 | 51 | Use the endelea keyword to skip a specific iteration: 52 | 53 | ```s 54 | fanya i = 0 55 | 56 | wakati (i < 5) { 57 | i++ 58 | kama (i == 3) { 59 | andika("nimeruka") 60 | endelea 61 | } 62 | andika(i) 63 | } 64 | ``` 65 | Output 66 | ```s 67 | 1 68 | 2 69 | nimeruka 70 | 4 71 | 5 72 | ``` 73 | 74 | 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. -------------------------------------------------------------------------------- /repl/docs/sw/README.md: -------------------------------------------------------------------------------- 1 | # NYARAKA YA LUGHA YA PROGRAMU YA NURU 2 | 3 | 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. 4 | 5 | ## Yaliyomo 6 | 7 | - [Safu Kwenye Nuru](arrays.md#arrays-in-nuru) 8 | - [Kutengeneza Safu](arrays.md#creating-arrays) 9 | - [Kupata na Kurekebisha vipengele vya Safu](arrays.md#accessing-and-modifying-array-elements) 10 | - [Kuunganisha Safu](arrays.md#concatenating-arrays) 11 | - [Kuangalia uwepo wa vipengele ndani ya safu](arrays.md#checking-for-array-membership) 12 | - [Kupita na Kurejea kwenye safu](arrays.md#looping-over-arrays) 13 | - [Mbinu za Safu](arrays.md#array-methods) 14 | - [idadi()](arrays.md#idadi()) 15 | - [sukuma()](arrays.md#sukuma()) 16 | - [yamwisho()](arrays.md#yamwisho()) 17 | - [Visaidia-kazi vya Nuru](builtins.md#built-in-functions-in-nuru) 18 | - [Kisaidia-kazi andika() ](builtins.md#the-andika()-function) 19 | - [Kisaidia-kazi jaza()](builtins.md#the-jaza()-function) 20 | - [Kisaidia-kazi aina()](builtins.md#the-aina()-function) 21 | - [Kisaidia-kazi fungua()](builtins.md#the-fungua()-function) 22 | - [Maoni kwenye Nuru](comments.md#comments-in-nuru) 23 | - [Maoni ya mstari mmoja](comments.md#single-line-comments) 24 | - [Maoni ya mistari mingi](comments.md#multi-line-comments) 25 | - [Kauli za masharti kwenye Nuru](ifStatements.md#conditional-statements-in-nuru) 26 | - [Kauli ya kama](ifStatements.md#if-statement-(kama)) 27 | - [Kauli za Au Kama na Sivyo)](ifStatements.md#else-if-and-else-blocks-(au-kama-and-sivyo)) -------------------------------------------------------------------------------- /repl/docs/sw/arrays.md: -------------------------------------------------------------------------------- 1 | # Orodha Au Safu Katika Nuru 2 | 3 | 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. 4 | 5 | ## Kutengeneza Safu 6 | 7 | Kutengeneza safu, tumia mabano mraba na tenganisha kila kitu kimoja kwa kutumia mkwaju: 8 | 9 | ```s 10 | orodha = [1, "pili", kweli] 11 | ``` 12 | 13 | ## Kupata na Kubadilisha Vipengele vya Safu 14 | 15 | 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: 16 | 17 | ```s 18 | namba = [10, 20, 30] 19 | jina = namba[1] // jina is 20 20 | ``` 21 | 22 | Unaweza ukabadilisha kipengele katika safu kwa kutumia kumbukumbu namba yake: 23 | 24 | ```s 25 | namba = [10, 20, 30] 26 | namba[1] = 25 27 | andika(namba) // Tokeo: [10,25,30] 28 | ``` 29 | 30 | ## Kuunganisha Safu 31 | 32 | Kuunganisha safu mbili au zaidi, tumia kiendeshi `+`: 33 | 34 | ```s 35 | a = [1, 2, 3] 36 | b = [4, 5, 6] 37 | c = a + b 38 | // c is now [1, 2, 3, 4, 5, 6] 39 | ``` 40 | 41 | ## Kuangalia Uanachama Katika Safu 42 | 43 | Tumia neno msingi `ktk` kuangalia kama kipengele kipo ndani ya safu: 44 | 45 | ```s 46 | namba = [10, 20, 30] 47 | andika(20 ktk namba) // Tokeo: kweli 48 | ``` 49 | 50 | ## Kuzunguka Ndani ya Safu 51 | 52 | Unaweza kutumia maneno msingi `kwa` na `ktk` kuzunguka ndani ya safu. Kuzunguka ndani ya safu na kupata kipengele peke yake tumia sintaksia ifuatayo: 53 | 54 | ```s 55 | namba = [1, 2, 3, 4, 5] 56 | 57 | kwa thamani ktk namba { 58 | andika(thamani) 59 | } 60 | 61 | //Tokeo: 62 | 1 63 | 2 64 | 3 65 | 4 66 | 5 67 | ``` 68 | 69 | Kuzunguka ndani ya safu na kupata kumbukumbu namba na kipengele tumia sintaksi aifuatayo: 70 | 71 | ```s 72 | majina = ["Juma", "Asha", "Haruna"] 73 | 74 | kwa idx, jina ktk majina { 75 | andika(idx, "-", jina) 76 | } 77 | 78 | //Tokeo: 79 | 0-Juma 80 | 1-Asha 81 | 2-Haruna 82 | ``` 83 | 84 | ## Vitendakazi vya Safu 85 | 86 | Nuru ina vitendakazi mbalimbali vilivyojengwa ndani kwa ajili ya Safu: 87 | 88 | ### idadi() 89 | 90 | `idadi()` hurudisha urefu wa safu: 91 | 92 | ```s 93 | a = [1, 2, 3] 94 | urefu = a.idadi() 95 | andika(urefu) // Tokeo: 3 96 | ``` 97 | 98 | ### sukuma() 99 | 100 | `sukuma()` huongeza kipengele kimoja au zaidi mwishoni mwa safu: 101 | 102 | ```s 103 | a = [1, 2, 3] 104 | a.sukuma("s", "g") 105 | andika(a) // Tokeo [1, 2, 3, "s", "g"] 106 | ``` 107 | 108 | ### yamwisho() 109 | 110 | `yamwisho()` hurudisha kipengele cha mwisho katika safu, au `tupu` kama safu haina kitu: 111 | 112 | ```s 113 | a = [1, 2, 3] 114 | mwisho = a.yamwisho() 115 | andika(mwisho) // Tokeo: 3 116 | 117 | b = [] 118 | mwisho = b.yamwisho() 119 | andika(mwisho) // Tokeo: tupu 120 | ``` 121 | 122 | Kwa kutumia taarifa hii, unaweza ukafanyakazi na safu za Nuru kwa ufanisi, kufanya iwe rahisi kuchambua mikusanyo ya data katika programu zako. 123 | -------------------------------------------------------------------------------- /repl/docs/sw/bools.md: -------------------------------------------------------------------------------- 1 | # Kufanya Kazi na Buliani Katika Nuru 2 | 3 | Vitu vyote katika Nuru ni kweli, yaani thamani yoyote ni kweli isipokua tupu and sikweli. Hutumika kutathmini semi ambazo zinarudisha kweli au sikweli. 4 | 5 | ## Kutathmini Semi za Buliani 6 | 7 | ### Kutathmini Semi Rahisi 8 | 9 | Katika Nuru, unaweza kutathmini semi rahisi zinazorudisha thamani ya buliani: 10 | 11 | ```go 12 | andika(1 > 2) // Matokeo: `sikweli` 13 | 14 | andika(1 + 3 < 10) // Matokeo: `kweli` 15 | ``` 16 | 17 | ### Kutathmini Semi Tata 18 | 19 | Katika Nuru, unaweza kutumia viendeshaji vya buliani kutathmini semi tata: 20 | 21 | ```go 22 | a = 5 23 | b = 10 24 | c = 15 25 | 26 | tokeo = (a < b) && (b < c) 27 | 28 | kama (tokeo) { 29 | andika("Hali zote mbili ni kweli") 30 | } sivyo { 31 | andika("Angalau hali moja ni sikweli") 32 | } 33 | // Tokeo: "Hali zote mbili ni kweli" 34 | ``` 35 | 36 | 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". 37 | 38 | ## Vitendakazi vya Buliani 39 | 40 | Nuru ina vitendakazi vya buliani kadhaa ambavyo unaweza ukatumia kutathmini semi: 41 | 42 | ### Kitendakazi `&&` 43 | 44 | Kitendakazi `&&` hutathmini kwenda kweli kama tu vitu vyote vinavyohusika ni kweli. Kwa mfano: 45 | 46 | ```go 47 | andika(kweli && kweli) // Tokeo: `kweli` 48 | 49 | andika(kweli && sikweli) // Tokeo: `sikweli` 50 | ``` 51 | 52 | ### Kitendakazi `||` 53 | 54 | Kitendakazi || hutathmini kwenda kweli kama angalau kitu kimoja kati ya vyote vinavyohusika ni kweli. Kwa mfano: 55 | 56 | ```go 57 | andika(kweli || sikweli) // Tokeo: `kweli` 58 | 59 | andika(sikweli || sikweli) // Tokeo: `sikweli` 60 | ``` 61 | 62 | ### Kitendakazi `!` 63 | 64 | Kitendakazi `!` hukanusha thamani ya kitu. Kwa mfano: 65 | 66 | ```go 67 | andika(!kweli) // Tokeo: `sikweli` 68 | 69 | andika(!sikweli) // Tokeo: `kweli` 70 | ``` 71 | 72 | ## Kufanya Kazi na Thamani za Buliani Katika Vitanzi 73 | 74 | Katika Nuru, unaweza ukatumia semi za buliani katika vitanzi kuendesha tabia zake. Kwa mfano: 75 | 76 | ```go 77 | namba = [1, 2, 3, 4, 5] 78 | 79 | kwa thamani ktk namba { 80 | kama (thamani % 2 == 0) { 81 | andika(thamani, " ni namba shufwa") 82 | } sivyo { 83 | andika(thamani, " ni namba witiri") 84 | } 85 | } 86 | 87 | // Output: 88 | // 1 ni namba witiri 89 | // 2 ni namba shufwa 90 | // 3 ni namba witiri 91 | // 4 ni namba shufwa 92 | // 5 ni namba witiri 93 | ``` 94 | 95 | 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. 96 | 97 | 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. 98 | -------------------------------------------------------------------------------- /repl/docs/sw/builtins.md: -------------------------------------------------------------------------------- 1 | # Vitendakazi Vilivyojengwa Ndani ya Nuru 2 | 3 | Nuru ina vitendakazi kadhaa vilivyojengwa ndani vinavyofanya kazi husika. 4 | 5 | ## Kitendakazi andika() 6 | 7 | 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: 8 | 9 | ```go 10 | andika(1, 2, 3) // Output: 1 2 3 11 | ``` 12 | 13 | ```go 14 | andika("Jina: Asha /n Umri: 20 /n Chuo: IFM") 15 | 16 | // Output: 17 | // Jina: Asha 18 | // Umri: 20 19 | // Chuo: IFM 20 | ``` 21 | 22 | ## Kitendakazi jaza() 23 | 24 | Kitendakazi `jaza()` kinatumika kupata ingizo kutoka kwa mtumiaji. Inawezakuchukua hoja sifuri au moja, ambayo ni utungo utakao tumika kama kimahasishi kwa mtumiaji. Mfano: 25 | 26 | ```go 27 | fanya salamu = unda() { 28 | fanya jina = jaza("Unaitwa nani? ") 29 | andika("Mambo vipi", jina) 30 | } 31 | 32 | salamu() 33 | ``` 34 | 35 | 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. 36 | 37 | ## Kitendakazi aina() 38 | 39 | Kitendakazi `aina()` kinatumika kutambua aina ya kitu. Inakubali hoja moja, na thamani inayorudi hua ni utungo unaoonyesha aina ya kitu. Mfano: 40 | 41 | ```go 42 | aina(2) // Output: "NAMBA" 43 | aina("Nuru") // Output: "NENO" 44 | ``` 45 | 46 | ## Kitendakazi fungua() 47 | 48 | Kitendakazi `fungua()` kinatumika kufungua faili. Inakubali hoja moja, ambayo ni njia ya faili unalotaka kufungua. Mfano: 49 | 50 | ```go 51 | faili = fungua("data.txt") 52 | ``` 53 | 54 | Katika mfano huu, tumetumia kitendakazi `fungua()` kufungua faili linaloitwa "data.txt". Kibadilika `faili` kinabeba kumbukumbu ya faili lililofunguliwa. 55 | -------------------------------------------------------------------------------- /repl/docs/sw/dictionaries.md: -------------------------------------------------------------------------------- 1 | # Kamusi Katika Nuru 2 | 3 | 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. 4 | 5 | ## Kutengeneza Kamusi 6 | 7 | Kamusi zinawekwa kwenye mabano singasinga na hujumuisha funguo na thamani zake zikitenganishwa na nukta pacha. Mfano wa uainishwaji wa kamusi: 8 | 9 | ```go 10 | 11 | orodha = {"jina": "Juma", "umri": 25} 12 | ``` 13 | 14 | Funguo zinawezakua tungo, namba, desimali, au buliani na thamani inaweza kua aina ya data yoyote ikiwemo tungo, namba, desimali, buliani, tupu, au kitendakazi: 15 | 16 | ```go 17 | k = { 18 | "jina": "Juma", 19 | "umri": 25, 20 | kweli: "kweli", 21 | "salimu": unda(x) { andika("habari", x) }, 22 | "sina thamani": tupu 23 | } 24 | ``` 25 | 26 | ## Kupata Vipengele 27 | 28 | Unaweza kupata vipengele vya kamusi kwa kutumia funguo zake: 29 | 30 | ```go 31 | k = { 32 | "jina": "Juma", 33 | "umri": 25, 34 | kweli: "kweli", 35 | "salimu": unda(x) { andika("habari", x) }, 36 | "sina thamani": tupu 37 | } 38 | 39 | andika(k[kweli]) // kweli 40 | andika(k["salimu"]("Juma")) // habari Juma 41 | ``` 42 | 43 | ## Kuboresha Vipengele 44 | 45 | Boresha thamani ya kipengele kwa kukipa thamani mpya kwenye funguo yake: 46 | 47 | ```go 48 | k = { 49 | "jina": "Juma", 50 | "umri": 25, 51 | kweli: "kweli", 52 | "salimu": unda(x) { andika("habari", x) }, 53 | "sina thamani": tupu 54 | } 55 | 56 | k['umri'] = 30 57 | andika(k['umri']) // 30 58 | ``` 59 | 60 | ## Kuongeza Vipengele Vipya 61 | 62 | Ongeza jozi mpya ya funguo-thamani kwenye kamusi kwa kuipa thamani funguo ambayo haipo kwenye kamusi husika: 63 | 64 | ```go 65 | k["lugha"] = "Kiswahili" 66 | andika(k["lugha"]) // Kiswahili 67 | ``` 68 | 69 | ## Kuunganisha Kamusi 70 | 71 | Unganisha kamusi mbili kwa kutumia kiendeshi `+`: 72 | 73 | ```go 74 | matunda = {"a": "apple", "b": "banana"} 75 | mboga = {"c": "tembele", "d": "mchicha"} 76 | vyakula = matunda + mboga 77 | andika(vyakula) // {"a": "apple", "b": "banana", "c": "tembele", "d": "mchicha"} 78 | ``` 79 | 80 | ## Angalia Kama Funguo Ipo Kwenye Kamusi 81 | 82 | Tumia neno msingi `ktk` kuangalia kama funguo ipo kwenye kamusi: 83 | 84 | ```go 85 | 86 | k = { 87 | "jina": "Juma", 88 | "umri": 25, 89 | kweli: "kweli", 90 | "salimu": unda(x) { andika("habari", x) }, 91 | "sina thamani": tupu 92 | } 93 | 94 | "umri" ktk k // kweli 95 | "urefu" ktk k // sikweli 96 | ``` 97 | 98 | ## Kuzunguka Ndani Ya Kamusi 99 | 100 | Zunguka ndani ya kamusi kupata funguo na thamani zake: 101 | 102 | ```go 103 | 104 | hobby = {"a": "kulala", "b": "kucheza mpira", "c": "kuimba"} 105 | 106 | kwa i, v ktk hobby { 107 | andika(i, "=>", v) 108 | } 109 | 110 | //Output 111 | 112 | a => kulala 113 | b => kucheza mpira 114 | c => kuimba 115 | ``` 116 | 117 | Kuzunguka ndani ya kamusi na kupata thamani peke yake: 118 | 119 | ```go 120 | 121 | hobby = {"a": "kulala", "b": "kucheza mpira", "c": "kuimba"} 122 | 123 | kwa i, v ktk hobby { 124 | andika(i, "=>", v) 125 | } 126 | 127 | //Output 128 | 129 | kulala 130 | kucheza mpira 131 | kuimba 132 | ``` 133 | 134 | 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. 135 | -------------------------------------------------------------------------------- /repl/docs/sw/for.md: -------------------------------------------------------------------------------- 1 | # Vitanzi Vya Kwa Katika Nuru 2 | 3 | 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`. 4 | 5 | ## Sintaksia 6 | 7 | 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: 8 | 9 | ```go 10 | jina = "lugano" 11 | 12 | kwa i ktk jina { 13 | andika(i) 14 | } 15 | 16 | // Tokeo: 17 | l 18 | u 19 | g 20 | a 21 | n 22 | o 23 | ``` 24 | 25 | ## Kuzunguka Ndani ya Jozi ya Funguo-Thamani 26 | 27 | ### Kamusi 28 | 29 | 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: 30 | 31 | ```go 32 | kamusi = {"a": "andaa", "b": "baba"} 33 | 34 | kwa v ktk kamusi { 35 | andika(v) 36 | } 37 | 38 | // Tokeo: 39 | 40 | andaa 41 | baba 42 | ``` 43 | 44 | Kupata thamani ya funguo na thamani zake, tumia vitambulishi vya muda mfupi viwili: 45 | 46 | ```go 47 | 48 | kwa k, v ktk kamusi { 49 | andika(k + " ni " + v) 50 | } 51 | 52 | // Tokeo: 53 | 54 | a ni andaa 55 | b ni baba 56 | ``` 57 | 58 | ### Tungo 59 | 60 | Kuzunguka juu ya thamani za tungo, tumia kitambulishi cha muda mfupi: 61 | 62 | ```go 63 | kwa v ktk "mojo" { 64 | andika(v) 65 | } 66 | 67 | // Tokeo: 68 | 69 | m 70 | o 71 | j 72 | o 73 | ``` 74 | 75 | Kuzunguka juu ya funguo na thamani zake, tumia vitambulishi vya muda mfupi viwili: 76 | 77 | ```go 78 | kwa i, v ktk "mojo" { 79 | andika(i, "->", v) 80 | } 81 | 82 | // Tokeo: 83 | 84 | 0 -> m 85 | 1 -> o 86 | 2 -> j 87 | 3 -> o 88 | ``` 89 | 90 | ### Safu 91 | 92 | Kuzunguka juu ya thamani za safu, tumia kitambulishi cha muda mfupi: 93 | 94 | ```go 95 | majina = ["juma", "asha", "haruna"] 96 | 97 | kwa v ktk majina { 98 | andika(v) 99 | } 100 | 101 | // Tokeo: 102 | 103 | juma 104 | asha 105 | haruna 106 | ``` 107 | 108 | Kuzunguka juu ya funguo na thamani katika safy, tumia vitambulishi vya muda mfupi viwili: 109 | 110 | ```go 111 | kwa i, v ktk majina { 112 | andika(i, "-", v) 113 | } 114 | 115 | // Tokeo: 116 | 117 | 0 - juma 118 | 1 - asha 119 | 2 - haruna 120 | ``` 121 | 122 | ## Vunja na Endelea 123 | 124 | ### Vunja 125 | 126 | Tumia neno msingi `vunja` kisitisha kitanzi: 127 | 128 | ```go 129 | 130 | kwa i, v ktk "mojo" { 131 | kama (i == 2) { 132 | andika("nimevunja") 133 | vunja 134 | } 135 | andika(v) 136 | } 137 | 138 | // Tokeo: 139 | 140 | m 141 | o 142 | j 143 | nimevunja 144 | 145 | ``` 146 | 147 | ### Endelea 148 | 149 | Tumia neno msingi `endelea` kuruka mzunguko maalum: 150 | 151 | ```go 152 | kwa i, v ktk "mojo" { 153 | kama (i == 2) { 154 | andika("nimeruka") 155 | endelea 156 | } 157 | andika(v) 158 | } 159 | 160 | // Tokeo: 161 | 162 | m 163 | o 164 | nimeruka 165 | o 166 | ``` 167 | -------------------------------------------------------------------------------- /repl/docs/sw/functions.md: -------------------------------------------------------------------------------- 1 | # Undo (Functions) 2 | 3 | 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. 4 | 5 | ## Sintaksia 6 | 7 | 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: 8 | 9 | ```go 10 | jumla = unda(x, y) { 11 | rudisha x + y 12 | } 13 | 14 | jumla(2, 3) // 5 15 | ``` 16 | 17 | ## Vipengele 18 | 19 | Vitendakazi vinawezakuwa nazifuri au idadi yoyote ya vipengele. Vipengele vinawezakua vya aina yoyote hata vitendakazi vingine: 20 | 21 | ```go 22 | salamu = unda(jina) { 23 | andika("Habari yako", jina) 24 | } 25 | 26 | salamu("asha") // Habari yako asha 27 | ``` 28 | 29 | ## Vipengele Vya Msingi 30 | 31 | Vitendakazi vinawezakupewa vipengele vya msingi: 32 | 33 | ```go 34 | salimu = unda(salamu="Habari") { 35 | andika(salamu) 36 | } 37 | 38 | salimu() // Habari 39 | salimu("Mambo") // Mambo 40 | ``` 41 | 42 | ## Rudisha 43 | 44 | Unaweza pia ukarudisha thamani kwa kutumia neno msingi `rudisha`. Neno msingi `rudisha` husitisha pande la msimbo na kurudisha thamani: 45 | 46 | ```go 47 | mfano = unda(x) { 48 | rudisha "nimerudi" 49 | andika(x) 50 | } 51 | 52 | mfano("x") // nimerudi 53 | ``` 54 | 55 | ## Kujirudia 56 | 57 | Nuru pia inahimili kujirudia. Mfano wa kujirudia kwa kitendakazi cha Fibonacci: 58 | 59 | ```go 60 | 61 | fib = unda(n) { 62 | kama (n <= 1) { 63 | rudisha n 64 | } sivyo { 65 | rudisha fib(n-1) + fib(n-2) 66 | } 67 | } 68 | 69 | andika(fib(10)) // 55 70 | ``` 71 | 72 | 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. 73 | 74 | ## Vifungizi 75 | 76 | 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: 77 | 78 | ```go 79 | fanya jum = unda(x) { 80 | rudisha unda(y) { 81 | rudisha x + y 82 | } 83 | } 84 | 85 | fanya jum_x = jum(5) 86 | andika(jum_x(3)) // 8 87 | ``` 88 | 89 | Katika mfano hapo juu, kitendakazi `jum` kinarudisha kitendakazi kingine ambacho kinabeba kipengele kimoja tu `y`. Kitendakazi kinachorudisha kinawezakupata kibadiliki x kutoka katika muktadha unaokizunguka. 90 | 91 | 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. 92 | -------------------------------------------------------------------------------- /repl/docs/sw/identifiers.md: -------------------------------------------------------------------------------- 1 | # Vitambulisho katika Nuru 2 | 3 | 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. 4 | 5 | ## Sheria za Sintaksia 6 | 7 | Vitambulisho vinaweza kuwa na herufi, nambari na nistari wa chini `_`. Walakini, kuna sheria chache ambazo unapaswa kufuata wakati wa kuunda vitambulisho: 8 | 9 | - Vitambulisho haviwezi kuanza na nambari. 10 | - Vitambulisho huwa na tofauti kulingana na matumizi ya herufi kubwa na ndogo. Kwa mfano, `kibadilikaChangu` na `kibadilikachangu` huchukuliwa kuwa vitambulisho tofauti. 11 | 12 | Hapa kuna mifano ya vitambulisho halali: 13 | 14 | ```go 15 | fanya mwaka_wa_kuzaliwa = 2020 16 | andika(mwaka_wa_kuzaliwa) // 2020 17 | 18 | fanya badili_c_kwenda_p = "C kwenda P" 19 | andika(badili_c_kwenda_p) // "C kwenda P" 20 | ``` 21 | 22 | Katika mifano iliyo hapo juu, mwaka_wa_kuzaliwa na badili_c_kwenda_p zote ni vitambulisho halali. 23 | 24 | ## Mazoea Bora 25 | 26 | Wakati wa kuchagua vitambulisho, ni muhimu kufuata mazoea bora ili kuhakikisha kuwa msimbo wako uko wazi na rahisi kueleweka: 27 | 28 | - Tumia majina yanayoelezea wazi kusudi au maana ya kigezo au kitendakazi. 29 | - Fuata kanuni thabiti ya kuweka majina, kama vile camelCase (kibadilikaChangu) au snake_case (kibadilika_changu). 30 | - Epuka kutumia majina tofauti ya herufi moja, isipokuwa kwa vijisehemu vinavyokubalika kwa kawaida kama vile vihesabu vitanzi (i, j, k). 31 | 32 | Kwa kufuata mbinu bora hizi unapounda vitambulisho, utafanya code yako ya Nuru iwe rahisi kusoma na kutunza kwa wewe na wengine. 33 | -------------------------------------------------------------------------------- /repl/docs/sw/if.md: -------------------------------------------------------------------------------- 1 | # Kama/Sivyo (If/Else) -------------------------------------------------------------------------------- /repl/docs/sw/keywords.md: -------------------------------------------------------------------------------- 1 | # Maneno Muhimu (Keywords) -------------------------------------------------------------------------------- /repl/docs/sw/maoni.md: -------------------------------------------------------------------------------- 1 | # Maoni Katika Nuru 2 | 3 | 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. 4 | 5 | ## Maoni ya Mstari Mmoja 6 | 7 | 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: 8 | 9 | ```s 10 | // Mstari huu utapuuzwa na mfasiri wa Nuru 11 | ``` 12 | 13 | Katika mfano huu, maandishi ya maoni "Mstari huu utapuuzwa na mfasiri wa Nuru" yatapuuzwa na mfasiri, kwa hivyo haitaathiri tabia ya programu. 14 | 15 | ## Maoni ya Mistari Mingi 16 | 17 | 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: 18 | 19 | ```s 20 | / 21 | Mistari hii 22 | Ita 23 | puuzwa 24 | / 25 | ``` 26 | 27 | Katika mfano huu, mistari yote kati ya alama /* na */ itapuuzwa na mfasiri wa Nuru, kwa hivyo haitaathiri tabia ya programu. 28 | 29 | 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. 30 | -------------------------------------------------------------------------------- /repl/docs/sw/null.md: -------------------------------------------------------------------------------- 1 | # Tupu (Null) -------------------------------------------------------------------------------- /repl/docs/sw/numbers.md: -------------------------------------------------------------------------------- 1 | # Namba na Desimali (Ints/Floats) -------------------------------------------------------------------------------- /repl/docs/sw/operators.md: -------------------------------------------------------------------------------- 1 | # Matendaji (Operators) -------------------------------------------------------------------------------- /repl/docs/sw/range.md: -------------------------------------------------------------------------------- 1 | ## Kitendakazi cha Mfululizo 2 | 3 | Kitendakazi cha `mfululizo` hutoa mfululizo wa nambari, sawa na kitendakazi cha `range()` cha Python. Kinaweza kutumika katika vitanzi au kuunda safu za nambari zinazofuatana. 4 | 5 | ### Muundo 6 | 7 | ```go 8 | mfululizo(mwisho) 9 | mfululizo(mwanzo, mwisho) 10 | mfululizo(mwanzo, mwisho, hatua) 11 | ``` 12 | 13 | ### Vipengele 14 | 15 | - `mwisho`: Kikomo cha juu cha mfululizo (haijumuishwi). 16 | - `mwanzo` (si lazima): Thamani ya kuanzia ya mfululizo. Chaguo-msingi ni 0. 17 | - `hatua` (si lazima): Ongezeko kati ya kila nambari katika mfululizo. Chaguo-msingi ni 1. 18 | 19 | ### Thamani Inayorudishwa 20 | 21 | Hurudisha safu ya nambari kamili. 22 | 23 | ### Mifano 24 | 25 | ```go 26 | // Toa nambari kutoka 0 hadi 4 27 | kwa i katika mfululizo(5) { 28 | andika(i) 29 | } 30 | // Tokeo: 0 1 2 3 4 31 | 32 | // Toa nambari kutoka 1 hadi 9 33 | kwa i katika mfululizo(1, 10) { 34 | andika(i) 35 | } 36 | // Tokeo: 1 2 3 4 5 6 7 8 9 37 | 38 | // Toa nambari shufwa kutoka 0 hadi 8 39 | kwa i katika mfululizo(0, 10, 2) { 40 | andika(i) 41 | } 42 | // Tokeo: 0 2 4 6 8 43 | 44 | // Toa nambari kwa mpangilio wa kurudi nyuma 45 | kwa i katika mfululizo(10, 0, -1) { 46 | andika(i) 47 | } 48 | // Tokeo: 10 9 8 7 6 5 4 3 2 1 49 | ``` 50 | 51 | ### Vidokezo 52 | 53 | - Thamani ya `mwisho` haijumuishwi, ikimaanisha mfululizo utasimama kabla ya kufikia thamani hii. 54 | - Ikiwa `hatua` hasi imetolewa, `mwanzo` inapaswa kuwa kubwa kuliko `mwisho`. 55 | - Thamani ya `hatua` haiwezi kuwa sifuri. 56 | -------------------------------------------------------------------------------- /repl/docs/sw/strings.md: -------------------------------------------------------------------------------- 1 | # Neno (Strings) -------------------------------------------------------------------------------- /repl/docs/sw/switch.md: -------------------------------------------------------------------------------- 1 | # Badili (Switch) -------------------------------------------------------------------------------- /repl/docs/sw/while.md: -------------------------------------------------------------------------------- 1 | # Wakati (While) -------------------------------------------------------------------------------- /styles/styles.go: -------------------------------------------------------------------------------- 1 | package styles 2 | 3 | import "github.com/charmbracelet/lipgloss" 4 | 5 | var ( 6 | TitleStyle = lipgloss.NewStyle().Margin(1, 0).Foreground(lipgloss.Color("#aa6f5a")) 7 | VersionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#ff9671")) 8 | AuthorStyle = lipgloss.NewStyle().Italic(true).Foreground(lipgloss.Color("#ff9671")) 9 | HelpStyle = lipgloss.NewStyle().Italic(true).Faint(true).Foreground(lipgloss.Color("#ffe6d6")) 10 | ErrorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("196")).Italic(true) 11 | ReplStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("76")).Italic(true) 12 | PromptStyle = "" 13 | ) 14 | -------------------------------------------------------------------------------- /third_party/math/test.nr: -------------------------------------------------------------------------------- 1 | tumia "hesabu" 2 | 3 | andika("abs: ",hesabu.abs(-42)); 4 | andika("acos: ",hesabu.acos(0.5)); 5 | andika("acosh: ",hesabu.acosh(2)); 6 | andika("asin: ", hesabu.asin(0.5)); 7 | andika("asinh: ",hesabu.asinh(2)); 8 | andika("atan: ",hesabu.atan(1)); 9 | andika("atan2: ",hesabu.atan2(1, 1)); 10 | andika("atanh: ",hesabu.atanh(0.5)); 11 | andika("cbrt: ",hesabu.cbrt(8)); 12 | andika("root: ",hesabu.root(27, 3)); 13 | andika("ceil: ",hesabu.ceil(4.3)); 14 | andika("cos: ",hesabu.cos(5)); 15 | andika("cosh: ",hesabu.cosh(5)); 16 | andika("exp: ",hesabu.exp(2)); 17 | andika("expm1: ",hesabu.expm1(1)); 18 | andika("floor: ",hesabu.floor(4.7)); 19 | andika("hypot: ",hesabu.hypot([3, 4])); 20 | andika("log: ",hesabu.log(2)); 21 | andika("log10: ",hesabu.log10(100)); 22 | andika("log1p: ",hesabu.log1p(1)); 23 | andika("log2: ",hesabu.log2(8)); 24 | andika("max: ",hesabu.max([4, 2, 9, 5])); 25 | andika("min: ",hesabu.min([4, 2, 9, 5])); 26 | andika("round: ",hesabu.round(4.6, "rpi")); 27 | andika("sign: ",hesabu.sign(-5)); 28 | andika("sin: ",hesabu.sin(1)); 29 | andika("sinh: ",hesabu.sinh(0)); 30 | andika("sqrt: ",hesabu.sqrt(4)); 31 | andika("tan: ",hesabu.tan(1)); 32 | andika("tanh: ",hesabu.tanh(0)); 33 | andika("factorial: ",hesabu.factorial(5)); 34 | andika("isNegative: ",hesabu.isNegative(-5)); 35 | andika("isInteger: ",hesabu.isInteger(4.5)); 36 | andika("getIntegerPart: ",hesabu.getIntegerPart(4.5)); 37 | andika("list: ",hesabu.list(1, 5, 1)); 38 | fanya callback = unda(accumulator, currentValue){ 39 | rudisha accumulator + currentValue; 40 | } 41 | andika("reduce: ",hesabu.reduce([1, 2, 3, 4],callback,0) ); 42 | -------------------------------------------------------------------------------- /token/token.go: -------------------------------------------------------------------------------- 1 | // This is where we define our tokens 2 | 3 | package token 4 | 5 | type TokenType string 6 | 7 | type Token struct { 8 | Type TokenType 9 | Literal string 10 | Line int 11 | } 12 | 13 | const ( 14 | ILLEGAL = "HARAMU" 15 | EOF = "MWISHO" 16 | 17 | // Identifiers + literals 18 | IDENT = "KITAMBULISHI" 19 | INT = "NAMBA" 20 | STRING = "NENO" 21 | FLOAT = "DESIMALI" 22 | 23 | // Operators 24 | ASSIGN = "=" 25 | PLUS = "+" 26 | MINUS = "-" 27 | BANG = "!" 28 | ASTERISK = "*" 29 | POW = "**" 30 | SLASH = "/" 31 | MODULUS = "%" 32 | LT = "<" 33 | LTE = "<=" 34 | GT = ">" 35 | GTE = ">=" 36 | EQ = "==" 37 | NOT_EQ = "!=" 38 | AND = "&&" 39 | OR = "||" 40 | PLUS_ASSIGN = "+=" 41 | PLUS_PLUS = "++" 42 | MINUS_ASSIGN = "-=" 43 | MINUS_MINUS = "--" 44 | ASTERISK_ASSIGN = "*=" 45 | SLASH_ASSIGN = "/=" 46 | MODULUS_ASSIGN = "%=" 47 | SHEBANG = "#!" 48 | 49 | //Delimiters 50 | COMMA = "," 51 | SEMICOLON = ";" 52 | LPAREN = "(" 53 | RPAREN = ")" 54 | LBRACE = "{" 55 | RBRACE = "}" 56 | LBRACKET = "[" 57 | RBRACKET = "]" 58 | COLON = ":" 59 | DOT = "." 60 | AT = "@" 61 | 62 | // Keywords 63 | FUNCTION = "FUNCTION" 64 | LET = "FANYA" 65 | TRUE = "KWELI" 66 | FALSE = "SIKWELI" 67 | IF = "KAMA" 68 | ELSE = "SIVYO" 69 | RETURN = "RUDISHA" 70 | WHILE = "WAKATI" 71 | NULL = "TUPU" 72 | BREAK = "VUNJA" 73 | CONTINUE = "ENDELEA" 74 | IN = "KTK" 75 | FOR = "KWA" 76 | SWITCH = "BADILI" 77 | CASE = "IKIWA" 78 | DEFAULT = "KAWAIDA" 79 | IMPORT = "TUMIA" 80 | PACKAGE = "PAKEJI" 81 | ) 82 | 83 | var keywords = map[string]TokenType{ 84 | "unda": FUNCTION, 85 | "fanya": LET, 86 | "kweli": TRUE, 87 | "sikweli": FALSE, 88 | "kama": IF, 89 | "au": ELSE, 90 | "sivyo": ELSE, 91 | "wakati": WHILE, 92 | "rudisha": RETURN, 93 | "vunja": BREAK, 94 | "endelea": CONTINUE, 95 | "tupu": NULL, 96 | "ktk": IN, 97 | "kwa": FOR, 98 | "badili": SWITCH, 99 | "ikiwa": CASE, 100 | "kawaida": DEFAULT, 101 | "tumia": IMPORT, 102 | "pakeji": PACKAGE, 103 | "@": AT, 104 | } 105 | 106 | func LookupIdent(ident string) TokenType { 107 | if tok, ok := keywords[ident]; ok { 108 | return tok 109 | } 110 | return IDENT 111 | } 112 | -------------------------------------------------------------------------------- /upx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NuruProgramming/Nuru/0805bb2df91dda4040be58160f8db114cc84764f/upx --------------------------------------------------------------------------------