├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .vscode └── launch.json ├── Cargo.toml ├── LICENSE ├── README.md ├── SUMMARY.md ├── changes.md ├── code_of_conduct.md ├── contributing.md ├── contributors.md ├── dokumanlar ├── README.md ├── hata_kodlari.md ├── liste.md ├── sayi.md ├── sayilar.md ├── sozluk.md └── yazi.md ├── karamelapp ├── Cargo.toml └── src │ └── main.rs ├── karamellib ├── Cargo.toml ├── src │ ├── buildin │ │ ├── base_functions.rs │ │ ├── class │ │ │ ├── baseclass.rs │ │ │ ├── dict.rs │ │ │ ├── list.rs │ │ │ ├── mod.rs │ │ │ ├── number.rs │ │ │ ├── proxy.rs │ │ │ └── text.rs │ │ ├── debug.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── num.rs │ ├── compiler │ │ ├── ast.rs │ │ ├── compiler.rs │ │ ├── context.rs │ │ ├── function.rs │ │ ├── generator │ │ │ ├── call.rs │ │ │ ├── compare.rs │ │ │ ├── constant.rs │ │ │ ├── function.rs │ │ │ ├── init_dict.rs │ │ │ ├── init_list.rs │ │ │ ├── jump.rs │ │ │ ├── load.rs │ │ │ ├── location.rs │ │ │ ├── location_group.rs │ │ │ ├── mod.rs │ │ │ ├── opcode_item.rs │ │ │ └── store.rs │ │ ├── mod.rs │ │ ├── module.rs │ │ ├── scope.rs │ │ ├── static_storage.rs │ │ ├── storage_builder.rs │ │ └── value.rs │ ├── constants │ │ └── mod.rs │ ├── error │ │ └── mod.rs │ ├── file │ │ └── mod.rs │ ├── lib.rs │ ├── logger │ │ └── mod.rs │ ├── macros │ │ └── mod.rs │ ├── parser │ │ ├── comment.rs │ │ ├── line.rs │ │ ├── mod.rs │ │ ├── number.rs │ │ ├── operator.rs │ │ ├── symbol.rs │ │ ├── text.rs │ │ └── whitespace.rs │ ├── syntax │ │ ├── assignment.rs │ │ ├── binary.rs │ │ ├── block.rs │ │ ├── control.rs │ │ ├── expression.rs │ │ ├── func_call.rs │ │ ├── function_defination.rs │ │ ├── function_return.rs │ │ ├── if_condition.rs │ │ ├── load_module.rs │ │ ├── loop_item.rs │ │ ├── loops.rs │ │ ├── mod.rs │ │ ├── newline.rs │ │ ├── primative.rs │ │ ├── statement.rs │ │ ├── unary.rs │ │ └── util.rs │ ├── types.rs │ └── vm │ │ ├── executer.rs │ │ ├── interpreter.rs │ │ └── mod.rs ├── test_files │ ├── fail_func_call_1.k │ ├── fail_test_2.k │ ├── pass_bool.k │ ├── pass_dict_1.k │ ├── pass_dict_2.k │ ├── pass_dict_3.k │ ├── pass_dict_4.k │ ├── pass_dict_5.k │ ├── pass_dict_6.k │ ├── pass_dict_7.k │ ├── pass_dict_8.k │ ├── pass_dict_fonk_1.k │ ├── pass_func_call_1.k │ ├── pass_func_call_2.k │ ├── pass_func_call_3.k │ ├── pass_func_call_4.k │ ├── pass_if_1.k │ ├── pass_if_2.k │ ├── pass_list_1.k │ ├── pass_list_2.k │ ├── pass_list_3.k │ ├── pass_list_4.k │ ├── pass_list_5.k │ ├── pass_list_6.k │ ├── pass_list_7.k │ ├── pass_loop_1.k │ ├── pass_loop_2.k │ ├── pass_loop_3.k │ ├── pass_loop_4.k │ ├── pass_modulo.k │ ├── pass_number.k │ ├── pass_test_1.k │ ├── pass_test_2.k │ ├── pass_test_3.k │ ├── pass_test_4.k │ ├── pass_test_5.k │ ├── pass_test_6.k │ ├── pass_test_7.k │ ├── pass_test_8.k │ └── pass_text.k ├── test_modules │ ├── fail_module_1 │ │ └── baz.k │ ├── pass_module_1 │ │ └── baz.k │ ├── pass_module_2 │ │ ├── baz.k │ │ └── hesapmakinesi.k │ ├── pass_module_3 │ │ ├── baz.k │ │ ├── hata.k │ │ └── hesapmakinesi.k │ ├── pass_sub_module_1 │ │ ├── baz.k │ │ ├── hata.k │ │ └── hesapmakinesi │ │ │ └── baz.k │ ├── pass_sub_module_2 │ │ ├── baz.k │ │ └── foo │ │ │ └── bar │ │ │ └── baz.k │ └── pass_sub_module_3 │ │ ├── baz.k │ │ └── foo │ │ └── bar │ │ └── baz │ │ └── more │ │ └── baz.k └── tests │ ├── assignment_tests.rs │ ├── binary_tests.rs │ ├── block_test.rs │ ├── compiler_tests.rs │ ├── control_tests.rs │ ├── endless_loop_tests.rs │ ├── func_call_tests.rs │ ├── function_defination_tests.rs │ ├── if_stmt_tests.rs │ ├── indentation_tests.rs │ ├── mod.rs │ ├── parser.rs │ ├── syntax_primative.rs │ ├── test_executers.rs │ ├── types.rs │ ├── unary_tests.rs │ └── vm_tests.rs └── karamelweb ├── Cargo.toml ├── build_command └── src ├── lib.rs └── www ├── index.html └── pkg ├── karamelweb.js └── karamelweb_bg.wasm /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --all --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | Cargo.lock 4 | clippy_result.txt -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'karamelapp'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=karamelapp", 15 | "--package=karamelapp" 16 | ], 17 | "filter": { 18 | "name": "karamelapp", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [], 23 | "cwd": "${workspaceFolder}" 24 | }, 25 | { 26 | "type": "cppvsdbg", 27 | "request": "launch", 28 | "name": "Debug executable 'karamelapp' at Windows", 29 | "program": "${workspaceRoot}/target/debug/karamelapp.exe", 30 | "args": [], 31 | "cwd": "${workspaceFolder}" 32 | }, 33 | { 34 | "type": "lldb", 35 | "request": "launch", 36 | "name": "Debug unit tests in executable 'karamelapp'", 37 | "cargo": { 38 | "args": [ 39 | "test", 40 | "--no-run", 41 | "--bin=karamelapp", 42 | "--package=karamelapp" 43 | ], 44 | "filter": { 45 | "name": "karamelapp", 46 | "kind": "bin" 47 | } 48 | }, 49 | "args": [], 50 | "cwd": "${workspaceFolder}" 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ "karamellib", "karamelapp", "karamelweb" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Erhan Barış (Ruslan Ognyanov Asenov) 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Karamel Programlama Dili \(KPD\) 2 | 3 | Karamel Programlama Dili \(kısaca KPD\) sıfırdan kendi sanal makinesi üzerinde çalışan, birden fazla platformda çalışan, dinamik olarak değişkenlerin tanımlandığı, Türkçe konuşanlara yönelik geliştirilmekte olan bir programlama dilidir. Python dilinde bulunan özelliklerden ilham alınarak geliştirilmeye başlanan dil, şu anda windows ve macosx üzerinde sorunsuz olarak çalışmaktadır. Asıl amacı yeni başlayanlara kullanımı ve öğrenmesi kolay bir geliştirme ortamı sağlamaktadır. Dilin tamamı Rust Programlama Dili kullanılarak geliştirilmektedir. Kendi sanal makinesi üzerinde çalışan dil ve WebAssembly yardımı ile web üzerinde de kullanılabilir. 4 | 5 | ### Peki hangi ne tip sanal makine kullanıyor? 6 | 7 | KPD, stack machine isimli sanal makine mimarisini kullanıyoruz. Bunu kullanmamızın nedeni yeni özelliklerin daha hızlı bir şekilde entegre edebilmemizden dolayı. Diğer Register Machine yaklaşımına kıyasla daha yavaş olsada ilk amacımız performanstan ziyade özellik ekleyip, stabil hale getirmek. 8 | 9 | ### Peki Stack Machine tam olarak nasıl çalışıyor? 10 | 11 | Bu mimaride kullanılacak olan değişkenler bir yığın olarak üst üste istiflenir ve sonrasında LIFO \(Last In First Out\) yaklaşımına göre değişkenler istiflerden geri alınıp işleme tabii tutulur. Bu yapının avantajı kodlar en basit haline dönüştürülerek daha performanslı olarak çalışması sağlanmaktadır. Yazılımcının yazdığı yüksek seviyeli olan kodlar işlenerek ara kodlara dönüştürülmektedir. Dönüştürülen ara kodlar KPD sanal makinesinde çalıştırılmaktadır. Aslında Üst düzey yazmış olduğunuz kodlar ve sanal makinenin işledi kodlar olarak iki farklı programlama dili içermektedir. 12 | 13 | ### Peki bunu başka hangi diller kullanıyor? 14 | 15 | Python, PHP, Ruby gibi oldukça popüler olan diller Stack Machine yaklaşımını kullanmaktadırlar. 16 | 17 | ### Dilin şu andaki durumu nedir? 18 | 19 | Halen geliştirme aşamasında olup, yardımlara her zaman açığız. Mutlaka kodlama yapmanıza gerek yok. Fikirleriniz ilede gelip destek olabilirsiniz. 20 | 21 | ### İndirilebilir durumda mı? 22 | 23 | Ne yazık ki indirilebilir değil fakat tarayıcı üzerinden çalıştırabilirsiniz. Aşağıda ki linki tarayıcınızda açarsanız denemeye devam başlayabilirsiniz. 24 | 25 | [https://erhanbaris.github.io/karamel/karamelweb/src/www/](https://erhanbaris.github.io/karamel/karamelweb/src/www/) 26 | 27 | ### Temel Tipler 28 | 29 | * Tam Sayı \(_1024_, _1\_204_, _2048_\) 30 | * Noktalı Sayı \(_1.234_, _1\_234.56789_, _123.4e+4_, _1\_230.4e+4_\) 31 | * Yazı \(_"Merhaba Dünya"_, _'Merhaba Dünya'_\) 32 | * Bool \(_doğru_, _yanlış_\) 33 | * Liste \(_\[1,2,3\]_, _\[\]_, _\[:kayıt\_başarılı, 'Kullanıcı Bilgisi'\]_\) 34 | * Sözlük \(_{'ad':'erhan', 'soyad':'barış'}_\) 35 | 36 | ### Döngü 37 | 38 | ```text 39 | kayıt = 10 40 | toplam = 0 41 | döngü kayıt iken: 42 | gç::satıryaz(kayıt) 43 | kayıt -= 1 44 | toplam += 1 45 | hataayıklama::doğrula(toplam, 10) 46 | hataayıklama::doğrula(kayıt, 0) 47 | ``` 48 | 49 | ```text 50 | sonsuz: 51 | gç::satıryaz("Sonsuza kadar devam") 52 | ``` 53 | 54 | Döngü kontrolü için _devam_, _continue_, _kır_, _break_. 55 | 56 | ### Sorgulama 57 | 58 | ```text 59 | a == b ise: 60 | gç::satıryaz('a eşittir b') 61 | veya a == c ise: 62 | gç::satıryaz('a eşittir c') 63 | veya: 64 | gç::satıryaz('a hiçbirine eşit değil') 65 | ``` 66 | 67 | ## Fonksiyon tanımlama 68 | 69 | ```text 70 | fonk metod_1(a): 71 | gç::yaz(a) 72 | 73 | fonk merhaba_dünya: 74 | gç::yaz('Merhaba dünya') 75 | 76 | fonk metod_1(a): 77 | gç::yaz(a) 78 | 79 | fonk faktoriyel(faktoriyel_sayısı): 80 | faktoriyel_sayısı==1 veya faktoriyel_sayısı==0 ise: 81 | döndür 1 82 | veya: 83 | döndür faktoriyel_sayısı * faktoriyel(faktoriyel_sayısı - 1) 84 | 85 | faktoriyel_sonucu = faktoriyel(10) 86 | gç::satıryaz('faktoriyel 10 => ', faktoriyel_sonucu) 87 | ``` 88 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Karamel Programlama Dili \(KPD\)](README.md) 4 | 5 | ## Karamel Programlama Dili 6 | 7 | * [Kılavuzlar](karamel-programlama-dili/kilavuzlar/README.md) 8 | * [Fonksiyon \| İşlev](karamel-programlama-dili/kilavuzlar/fonksiyon-or-islev/README.md) 9 | * [Yazı İşlevleri](karamel-programlama-dili/kilavuzlar/fonksiyon-or-islev/yazi-islevleri.md) 10 | * [Hakkında](karamel-programlama-dili/hakkinda/README.md) 11 | * [Katkıda Bulunanlar](karamel-programlama-dili/hakkinda/katkida-bulunanlar.md) 12 | 13 | --- 14 | 15 | * [CHANGES](changes.md) 16 | * [Contributor Covenant Code of Conduct](code_of_conduct.md) 17 | * [Contributing](contributing.md) 18 | 19 | ## Sözlük 20 | 21 | * [Genel Sözlük](soezluek/genel-soezluek.md) 22 | * [Denk Sözlük](soezluek/denk-soezluek.md) 23 | * [KPD Terim Sözlüğü](soezluek/kpd-terim-soezluegue.md) 24 | 25 | -------------------------------------------------------------------------------- /changes.md: -------------------------------------------------------------------------------- 1 | # CHANGES 2 | 3 | Not yet released. We are still working on it. 4 | 5 | -------------------------------------------------------------------------------- /code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, 17 | 18 | and learning from the experience 19 | 20 | * Focusing on what is best not just for us as individuals, but for the 21 | 22 | overall community 23 | 24 | Examples of unacceptable behavior include: 25 | 26 | * The use of sexualized language or imagery, and sexual attention or 27 | 28 | advances of any kind 29 | 30 | * Trolling, insulting or derogatory comments, and personal or political attacks 31 | * Public or private harassment 32 | * Publishing others' private information, such as a physical or email 33 | 34 | address, without their explicit permission 35 | 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 43 | 44 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 49 | 50 | ## Enforcement 51 | 52 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at erhanbaris@gmail.com. All complaints will be reviewed and investigated promptly and fairly. 53 | 54 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 55 | 56 | ## Enforcement Guidelines 57 | 58 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 59 | 60 | ### 1. Correction 61 | 62 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 63 | 64 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 65 | 66 | ### 2. Warning 67 | 68 | **Community Impact**: A violation through a single incident or series of actions. 69 | 70 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 71 | 72 | ### 3. Temporary Ban 73 | 74 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 75 | 76 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 77 | 78 | ### 4. Permanent Ban 79 | 80 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 81 | 82 | **Consequence**: A permanent ban from any sort of public interaction within the community. 83 | 84 | ## Attribution 85 | 86 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code\_of\_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html). 87 | 88 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 89 | 90 | For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are available at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). 91 | 92 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | [![open issues](https://badgen.net/github/open-issues/erhanbaris/karamel?label=issues)](https://github.com/erhanbaris/karamel/issues) [![help welcome issues](https://badgen.net/github/label-issues/erhanbaris/karamel/help%20welcome/open)](https://github.com/erhanbaris/karamel/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+welcome%22) [![good first issue](https://badgen.net/github/label-issues/erhanbaris/karamel/good%20first%20issue/open)](https://github.com/erhanbaris/karamel/issues?q=is%3Aopen+is%3Aissue+label%3A%22beginner+friendly%22) 4 | 5 | **Contents** 6 | 7 | * [Welcome](contributing.md#welcome) 8 | * [Prerequisites](contributing.md#prerequisites) 9 | * [Requesting Features](contributing.md#requesting-features) 10 | * [Language Requests](contributing.md#language-requests) 11 | * [Reporting Issues](contributing.md#reporting-issues) 12 | * [Fixing Issues \(PRs\)](contributing.md#fixing-issues-prs) 13 | * [In a nutshell](contributing.md#in-a-nutshell) 14 | * [Build and Test](contributing.md#build-and-test) 15 | 16 | ## Welcome 17 | 18 | Hello and welcome to **Karamel**. We are making **Turkish programming language**, but you probably knew that already. If you are considering contributing to **Karamel** this document will be hopefully be a helpful resource. 19 | 20 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. 21 | 22 | **You do not have to be a programmer.** There are many ways to contribute:: 23 | 24 | * Hang out on our [Discord](https://discord.gg/8ymtm9XPyQ) and help answers questions as they come up 25 | * Report [new issues or bugs](https://github.com/erhanbaris/karamel/issues/new/choose) or join the existing discussion on open issues 26 | * Submit pull requests to resolve issues 27 | * Improve our documentation to better explain all the things to all the peoples 28 | 29 | ### Prerequisites 30 | 31 | * To help answer questions on issues or contribute on Discord you need only be friendly. 32 | * To work on the core language engine you'll need to know Rust-Lang. 33 | * To work on documentation you need to be ready and willing to document things. 34 | 35 | ## Requesting Features 36 | 37 | Feature requests are always welcome. If the feature doesn't belong in the core library then we're always happy to suggest how you might go about developing a plug-in. 38 | 39 | If you're thinking of contributing a feature first open an issue to discuss whether the feature belongs in core vs a plug-in. Often this is a great way to get implementation tips or links to prior discussions on the topic often with additional context, etc. 40 | 41 | ## Reporting Issues 42 | 43 | If you find a bug or think of an improvement, feel free to [open an issue](https://github.com/erhanbaris/karamel/issues/new/choose). 44 | 45 | ## Fixing Issues \(PRs\) 46 | 47 | If you feel comfortable with the [prerequisites](contributing.md#prerequisites), you can grab any issue marked ["good first issue"](https://github.com/erhanbaris/karamel/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). Or feel free to jump in with thoughts or comments on any of the more complex issues. 48 | 49 | ### In a nutshell 50 | 51 | If you're new to contributing to open-source, have a look at [this GitHub Guide](https://guides.github.com/activities/forking). It explains the general process of GitHub collaboration. 52 | 53 | Karamel is developed in Rust-Lang, so you'll need the usual suspects: [Rust-Lang](https://www.rust-lang.org), git, etc. You'll likely start by forking the repository on GitHub and then cloning it locally. 54 | 55 | 1. Fork this project on GitHub. 56 | 2. Clone it locally `git clone git@github.com:username/Karamel.git`. 57 | 3. Create a work branch \(`git checkout -b my-branch`\). 58 | 4. Commit your changes \(`git commit -m 'my changes'`\). 59 | 5. [Build and Test](contributing.md#build-and-test) 60 | 6. Push the branch \(`git push origin my-branch`\). 61 | 7. Open a Pull Request from your fork back to this repository. 62 | 63 | ### Keep in Mind 64 | 65 | Please open a new issue before your PR \(or join the discussion on the existing issue\), so we can explore and discuss the topic at hand. Your time is important, and we need to be sure it's well-spent. 66 | 67 | _Before_ you start coding, keep these tips in mind: 68 | 69 | * You should usually add markup tests \(ie. whenever you've made a significant grammar change or fixed a bug\). Simply adding `keywords` can be an exception to this rule. 70 | * Change only what needs to be changed; don't re-lint or rewrite whole files when fixing small bugs 71 | * Linting or major re-organization needs a dedicated commit 72 | 73 | _After_ you make your changes, we have some housekeeping tasks for you - like updating the [changelog](https://github.com/erhanbaris/karamel/blob/main/CHANGES.md). The PR template will be your guide. 74 | 75 | ### Build and Test 76 | 77 | When contributing a PR \(that doesn't make any specific changes to browser features\) it's usually sufficient to build and test only the Node.js build. Our CI process will guarantee that the browser build is still green. 78 | 79 | Building the Karamel: 80 | 81 | ```text 82 | cargo build --release 83 | ``` 84 | 85 | Testing the Karamel: 86 | 87 | ```text 88 | cargo test --all 89 | ``` 90 | 91 | -------------------------------------------------------------------------------- /contributors.md: -------------------------------------------------------------------------------- 1 | # Katkıda bulunanlar 2 | 3 | ## Çekirdek Takım 4 | 5 | * Erhan Barış [erhanbaris@gmail.com](mailto:erhanbaris@gmail.com) 6 | * Volkan Taş \(datadeveb\) 7 | 8 | -------------------------------------------------------------------------------- /dokumanlar/README.md: -------------------------------------------------------------------------------- 1 | # Karamel Kılavuzlaması 2 | 3 | Karamel programlama dili ile ilgili bilmeniz gereken tüm ayrıntıları bu kılavuzlardan elde edebilirsiniz. Karamel dili için kullanılan ölçünlerden biri **Yazılımca Ölçünleri**'dir. 4 | 5 | ## İlk Adımlar 6 | 7 | Karamel dilinde ya da yazılım programlamada yeni misin? Başlayacağın alan bu! 8 | 9 | **Başlangıçta:** Göz Atış, Kurulum 10 | 11 | **Belleti:** 12 | **--- 1nc Çart:** Bir veriyi çıktı olarak elde etmek 13 | **--- 2nc Çart:** Toplama, çıkarma, çarpma, bölme işlemleri 14 | **--- 3nc Çart:** Doğum tarihi ile kişinin yenidoğan, çağa, bebek, cıbar, çocuk, ergen, genç, yetişkin veya yaşlı olup olmadığını belirleme 15 | 16 | 17 | 18 | 19 | 20 | ## Yardımcı Olalım! 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /dokumanlar/liste.md: -------------------------------------------------------------------------------- 1 | # Liste 2 | 3 | ## Fonksiyonlar 4 | 5 | ### getir(sıra) 6 | 7 | _Liste_'den *sıra*da ki elemanı döndürür. Eğer sıra numarası _Liste_ sınırları dışında ise geriye *boş* döndürülür. 8 | 9 | ### uzunluk() 10 | 11 | _Liste_'nın uzunluğunu döndürür. 12 | 13 | ### ekle(nesne) 14 | 15 | _Liste_'ye yeni bir öğe ekler. Geri dönüş değeri olarak yeni eklenen nesnenin sırası döndürülür. 16 | 17 | ### temizle() 18 | 19 | _Liste_'de ki bütün nesneleri siler. 20 | 21 | ### arayaekle(sıra, nesne) 22 | 23 | *nesne*'yi _Liste_'de ki *sıra*ya ekler. Fonksiyon geriye *doğru* yada *yanlış* geri çevirir. Eğer *sıra* bilgisi _Liste_ boyutundan büyük ise geriye *yanlış* değil ise *doğru* değeri döndürülür. 24 | 25 | ### pop() 26 | 27 | _Liste_'ye en son eklenen *nesne* geri döndürülür ve _Liste_'den bu nesne silinir. 28 | 29 | ### sil(sıra) 30 | 31 | _Liste_'den *sıra*da ki eleman silinir ve geriye döndürülür. Eğer sıra numarası _Liste_ sınırları dışında ise geriye *boş* döndürülür. 32 | -------------------------------------------------------------------------------- /dokumanlar/sayi.md: -------------------------------------------------------------------------------- 1 | # Sayı 2 | 3 | ## Fonksiyonlar 4 | 5 | ### yazı() 6 | Değeri yazı tipine dönüştürür. 7 | ``` 8 | gç::satıryaz(1024.yuvarla()) // "1024" 9 | ``` 10 | 11 | ### hex() 12 | Sayıyı dexadecimal formatında yazar. 13 | ``` 14 | gç::satıryaz(1.2.yuvarla()) // 1 15 | ``` 16 | 17 | ### yuvarla() 18 | Noktalı sayıyı en yakın sayıya yuvarlar. 19 | ``` 20 | gç::satıryaz(1.2.yuvarla()) // 1 21 | gç::satıryaz(1.5.yuvarla()) // 2 22 | gç::satıryaz(1.51.yuvarla()) // 2 23 | gç::satıryaz(-1.2.yuvarla()) // -1 24 | gç::satıryaz(-1.5.yuvarla()) // -2 25 | gç::satıryaz(-1.51.yuvarla()) // -2 26 | ``` 27 | 28 | ### tavan() 29 | Noktalı sayıyı üst sayıya tamamlar. 30 | 31 | ``` 32 | gç::satıryaz(1.2.tavan()) // 2 33 | gç::satıryaz(1.5.tavan()) // 2 34 | gç::satıryaz(1.51.tavan()) // 2 35 | gç::satıryaz(-1.2.tavan()) // -1 36 | gç::satıryaz(-1.5.tavan()) // -1 37 | gç::satıryaz(-1.51.tavan()) // -1 38 | ``` 39 | 40 | ### taban() 41 | Noktalı sayıyı alt sayıya tamamlar. 42 | 43 | ``` 44 | gç::satıryaz(1.2.tavan()) // 1 45 | gç::satıryaz(1.5.tavan()) // 1 46 | gç::satıryaz(1.51.tavan()) // 1 47 | gç::satıryaz(-1.2.tavan()) // -2 48 | gç::satıryaz(-1.5.tavan()) // -2 49 | gç::satıryaz(-1.51.tavan()) // -2 50 | ``` 51 | 52 | 53 | ### tamsayı() 54 | Bir sayının tamsayı kısmını geri döndürür. 55 | 56 | ``` 57 | gç::satıryaz(1.2.tamsayı()) // 1 58 | gç::satıryaz(1.5.tamsayı()) // 1 59 | ``` 60 | 61 | ### kesir() 62 | Bir sayının kesir kısmını geri döndürür. 63 | 64 | ``` 65 | gç::satıryaz(1.2.kesir()) // 0.2 66 | gç::satıryaz(1.5.kesir()) // 0.5 67 | ``` 68 | -------------------------------------------------------------------------------- /dokumanlar/sayilar.md: -------------------------------------------------------------------------------- 1 | # TASLAK 2 | 3 | **Karamel Programlama Dili** kısa adıyla **KPD**; ikilik, sekizlik, onluk ve onaltılık sayı tabanlarını desteklemektedir. 4 | 5 | _Sozdizimi:_ 6 |   _Sayi ::_ 7 |     _2lik_ 8 |     _8lik_ 9 |     _10luk_ 10 |     _16lik_ 11 | 12 |   _2lik ::_ 13 |    **0b** _2lik sayilar_ 14 |    **0B** _2lik sayilar_ 15 | 16 |   _8lik ::_ 17 |    **0o** _8lik sayilar_ 18 |    **0O** _8lik sayilar_ 19 | 20 |   _10luk ::_ 21 |    _10luk sayilar_ 22 |    _10luk sayilar_ 23 | 24 |   _16lik ::_ 25 |    **0x** _16lik sayilar_ 26 |    **0X** _16lik sayilar_ 27 | 28 |   _2lik sayilar ::_ 29 |     _2lik sayi_ 30 |     _2lik sayilar 2lik sayi_ 31 | 32 |   _2lik sayi ::_ **biri** 33 |    **0 1** 34 | 35 |   _8lik sayilar ::_ 36 |     _8lik sayi_ 37 |     _8lik sayilar 8lik sayi_ 38 | 39 |   _8lik sayi ::_ **biri** 40 |    **0 1 2 3 4 5 6 7** 41 | 42 |   _10luk sayilar ::_ 43 |     _10luk sayi_ 44 |     _10luk sayilar 10luk sayi_ 45 | 46 |   _10luk sayi ::_ **biri** 47 |    **0 1 2 3 4 5 6 7 8 9** 48 | 49 |   _16lik sayilar ::_ 50 |     _16lik sayi_ 51 |     _16lik sayilar 16lik sayi_ 52 | 53 |   _16lik sayi ::_ **biri** 54 |    **0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F** 55 | 56 | ## Onluk Sayilar 57 | 58 | Gunluk kullanimda olan onluk sayi sistemi kendi icerinde gercek sayilar ve tam sayilar olarak ikiye ayrilmaktadir. Her iki tur sayilarda kullanilabilmektedir. 59 | 60 | _0 1 2 3 4 5 6 7 8 9_ karakterlerinden olusmaktadir. 61 | 62 | ### Tam sayilar 63 | 64 | En buyuk sayi **9007199254740991** ve en kucuk sayi **-9007199254740991** kullanilabilmektedir. 65 | 66 | Ornek kullanimi: 67 | _2020_ 68 | _0123456789_ 69 | _6699770000000_ 70 | 71 | ### Noktali sayilar 72 | 73 | Gunluk hayatta kullandigimiz noktali sayilar AAA dili icerinde ki karsiligi bircok diger dilde oldugu gibi **nokta** ile iki kisima ayrilmaktadir. En kucuk noktali sayi **1.7976931348623157e+308** en kucuk noktali sayi **5e-324** olarak tanimlanmistir. Tam sayilardan farkli olarak ust bilgisi kullanilabilmektedir. Fakat us bilgisi kullanilmis olan sayinin ciktisi noktali yada tam sayi olabilmektedir. 74 | 75 | Ornek kullanimi: 76 | _1.23456789_ 77 | _-123.456_ 78 | _-123.4e-4_ 79 | _123.4e+4_ 80 | 81 | -------------------------------------------------------------------------------- /dokumanlar/sozluk.md: -------------------------------------------------------------------------------- 1 | # Sözlük 2 | 3 | ## Fonksiyonlar 4 | 5 | ### uzunluk() 6 | 7 | _Sözlük__'nın uzunluğunu döndürür. 8 | 9 | ### getir(anahtar) 10 | 11 | _Sözlük_ içerisinden verilen anahtarın değerini döndürür. Eğer değer bulunamaz ise derişer _Boş_ döndürülür. 12 | 13 | ### ekle(anahtar, değer) 14 | 15 | _anahtar_ ile eşleşen _değer_ bilgisi güncellenir. Eğer Kayıt bulunamaz ise yeni kayıt eklenir. 16 | 17 | ### güncelle(anahtar, değer) 18 | 19 | _anahtar_ ile eşleşen _değer_ bilgisi güncellenir. Eğer Kayıt bulunamaz ise yeni kayıt eklenir. 20 | 21 | ### içeriyormu(anahtar) 22 | 23 | _Sözlük_ içerisinde _anahtar_ var mı diye kontrol eder ve geriye _Bool_ veri çevirir. 24 | 25 | ### temizle() 26 | 27 | _Sözlük_ içerisinde ki bütün veriler silinir. 28 | 29 | ### sil(anahtar) 30 | 31 | _anahtar_ ile eşleşen kayıt _Sözlük_ içerisinden silinir. 32 | 33 | ### anahtarlar() 34 | 35 | _Sözlük'te kayıtlı olan bütün kayıtların anahtarları bir liste içerisinde geri döndürülür. 36 | -------------------------------------------------------------------------------- /dokumanlar/yazi.md: -------------------------------------------------------------------------------- 1 | # Yazı 2 | 3 | ## Fonksiyonlar 4 | 5 | ### uzunluk() 6 | 7 | _Yazı_'nın uzunluğunu döndürür. 8 | 9 | ### harfleriküçült() 10 | 11 | Bütün harfleri küçük harfe çevirir. Şu an için sadece türkçe karakterlere yönelik olarak küçültme işlemi yapmaktadır. 12 | 13 | ### harfleribüyült() 14 | 15 | Bütün harfleri büyük harfe çevirir. Şu an için sadece türkçe karakterlere yönelik olarak büyütme işlemi yapmaktadır. 16 | 17 | ### içeriyormu(aranan) 18 | 19 | _Yazı_ içerisinde bir kelime var mı diye kontrol eder. Geriye _Bool_ veri çevirir. 20 | 21 | ### satırlar() 22 | 23 | _Yazı_'yı satırlara göre bölüp _Liste_ geri çevirir. 24 | 25 | ### parçala(bununla) 26 | 27 | Verilen _Yazı_'ya göre parçalara ayırır. Geriye _Liste__ döndürür. 28 | 29 | ### kırp() 30 | 31 | _Yazı__'nın sonunda ki ve başında ki _BeyazBoşluk_'ları temizler. 32 | 33 | ### sonukırp() 34 | 35 | _Yazı__'nın sonunda ki _BeyazBoşluk_'ları temizler. 36 | 37 | ### başıkırp() 38 | 39 | _Yazı__'nın başında ki _BeyazBoşluk_'ları temizler. 40 | 41 | ### parçagetir(buradan, burayakadar) 42 | Bir _Yazı_ içerisinden bir parçayı almak için kullanılır. Eğer _buradan_ değeri 0'dan küçük olursa, başlangıç noktası 0 olarak kabul edilir. Eğer _burayakadar_ değeri _Yazı_'nın uzunluğundan büyük olursa, bitiş değeri _Yazı_'ının uzunluğu olarak kabul edilir. 43 | 44 | **Örnek** 45 | 46 | ``` 47 | değişkenim = "merhaba dünya" 48 | gç::satıryaz(değişkenim) // merhaba dünya 49 | gç::satıryaz(değişkenim.parçagetir(0, 7)) // merhaba 50 | gç::satıryaz(değişkenim.parçagetir(8, 14)) // dünya 51 | ``` 52 | 53 | ### değiştir(bunu, bununla) 54 | 55 | Bir _Yazı_'nın tüm eşleşmelerini başka bir _Yazı_ ile değiştirir. Bu fonksiyon yeni bir _Yazı_ oluşturur ve asıl _Yazı_ içeriğini kopyalar sonrasında değiştirme işlemi yapar. Orjinal _Yazı_ içeriği değişmez. 56 | 57 | **Örnek** 58 | 59 | "**merhaba dünya**" elimizde olan bir _Yazı_'mız olsun. "**dünya**" sözcüğünü silip yerine "**karamel**" sözcüğü yazarak "**merhaba karamel**" _Yazı_'sını elde etmeye çalışalım. 60 | 61 | ``` 62 | değişkenim = "merhaba dünya" 63 | gç::satıryaz("Orjinal içerik : ", değişkenim) // merhaba dünya 64 | gç::satıryaz("Değiştirilmiş içerik : ", değişkenim.değiştir("dünya", "karamel")) // merhaba karamel 65 | ``` 66 | 67 | ****_BeyazBoşluk_******* 68 | 69 | - U+0009 (yatay sekme, '\t') 70 | - U+000A (yeni satır, '\n') 71 | - U+000B (dikey sekme) 72 | - U+000C (form besleme) 73 | - U+000D (satırbaşı, '\r') 74 | - U+0020 (boşluk, ' ') 75 | - U+0085 (sonraki satır) 76 | - U+200E (soldan sağa işareti) 77 | - U+200F (sağdan sola işareti) 78 | - U+2028 (satır ayırıcı) 79 | - U+2029 (paragraf ayırıcı) 80 | -------------------------------------------------------------------------------- /karamelapp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "karamelapp" 3 | version = "0.1.0" 4 | authors = ["Erhan BARIS "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | mimalloc = { version = "*", default-features = false } 11 | karamellib = { path = "../karamellib" } 12 | clap = "~2.27.0" -------------------------------------------------------------------------------- /karamelapp/src/main.rs: -------------------------------------------------------------------------------- 1 | use mimalloc::MiMalloc; 2 | 3 | #[global_allocator] 4 | static GLOBAL: MiMalloc = MiMalloc; 5 | extern crate karamellib; 6 | use clap::{Arg, App}; 7 | 8 | 9 | use karamellib::{constants::{KARAMEL_CONTACT_EMAIL, KARAMEL_HELP_ABOUT, KARAMEL_TITLE, KARAMEL_VERSION}, vm::executer::{ExecutionParameters, ExecutionSource}}; 10 | 11 | fn main() { 12 | let matches = App::new(KARAMEL_TITLE) 13 | .version(KARAMEL_VERSION) 14 | .author(KARAMEL_CONTACT_EMAIL) 15 | .about(KARAMEL_HELP_ABOUT) 16 | .arg(Arg::with_name("file") 17 | .short("d") 18 | .long("dosya") 19 | .value_name("FILE") 20 | .help("Çalıştırılacak karamel dosyası") 21 | .takes_value(true)) 22 | .get_matches(); 23 | 24 | let parameters = match matches.value_of("file") { 25 | Some(file) => ExecutionParameters { 26 | source: ExecutionSource::File(file.to_string()), 27 | return_opcode: true, 28 | return_output: true, 29 | dump_opcode: false, 30 | dump_memory: false 31 | }, 32 | None => ExecutionParameters { 33 | source: ExecutionSource::Code(r#" 34 | döngü i = 0, i < 10, i++: 35 | i mod 2 ise: 36 | gç::satıryaz('Mod 2 ', i.yazi()) 37 | veya: 38 | gç::satıryaz('Mod 1 ', i.yazi()) 39 | 40 | "#.to_string()), 41 | return_opcode: true, 42 | return_output: true, 43 | dump_opcode: false, 44 | dump_memory: false 45 | } 46 | }; 47 | 48 | 49 | let result = karamellib::vm::executer::code_executer(parameters); 50 | match result.executed { 51 | true => println!("Success"), 52 | false => println!("Fail") 53 | }; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /karamellib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "karamellib" 3 | version = "0.1.0" 4 | authors = ["Erhan BARIS "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | bitflags = "1.2" 11 | log-update = "*" 12 | colored = "2" 13 | log = "0.4.14" 14 | lazy_static = "1.4.0" 15 | unicode-width = "0.1.7" 16 | levenshtein = "1.0.5" 17 | 18 | 19 | # For enum 20 | strum = "0.21.0" 21 | strum_macros = "0.21.1" 22 | thiserror = "1.0.26" 23 | 24 | [features] 25 | dumpExecutionOpcode = [] 26 | dumpMemory = [] 27 | dumpOpcodes = [] 28 | liveOpcodeView = [] 29 | wasmBuild = [] 30 | unittest = [] 31 | default = [] 32 | 33 | dbg = [] 34 | dbg_level1 = [] 35 | dbg_level2 = [] 36 | dbg_level3 = [] 37 | 38 | [lib] 39 | crate-type = ["cdylib", "rlib"] 40 | doctest = false -------------------------------------------------------------------------------- /karamellib/src/buildin/base_functions.rs: -------------------------------------------------------------------------------- 1 | use crate::compiler::{EMPTY_OBJECT, function::{FunctionParameter, FunctionReference, NativeCall, NativeCallResult}}; 2 | use crate::types::VmObject; 3 | use crate::buildin::{Module, Class}; 4 | use crate::compiler::GetType; 5 | use crate::error::KaramelErrorType; 6 | use crate::{n_parameter_expected}; 7 | use std::{cell::RefCell, collections::HashMap}; 8 | use std::rc::Rc; 9 | 10 | 11 | #[derive(Clone)] 12 | pub struct BaseFunctionsModule { 13 | methods: RefCell>>, 14 | path: Vec 15 | } 16 | 17 | impl Module for BaseFunctionsModule { 18 | fn get_module_name(&self) -> String { 19 | "baz".to_string() 20 | } 21 | 22 | fn get_path(&self) -> &Vec { 23 | &self.path 24 | } 25 | 26 | fn get_method(&self, name: &str) -> Option> { 27 | match self.methods.borrow().get(name) { 28 | Some(method) => Some(method.clone()), 29 | None => None 30 | } 31 | } 32 | 33 | fn get_module(&self, _: &str) -> Option> { 34 | None 35 | } 36 | 37 | fn get_methods(&self) -> Vec> { 38 | let mut response = Vec::new(); 39 | self.methods.borrow().iter().for_each(|(_, reference)| response.push(reference.clone())); 40 | response 41 | } 42 | 43 | fn get_modules(&self) -> HashMap> { 44 | HashMap::new() 45 | } 46 | 47 | fn get_classes(&self) -> Vec> { 48 | Vec::new() 49 | } 50 | } 51 | 52 | impl BaseFunctionsModule { 53 | pub fn new() -> Rc { 54 | let module = BaseFunctionsModule { 55 | methods: RefCell::new(HashMap::new()), 56 | path: vec!["baz".to_string()] 57 | }; 58 | 59 | let rc_module = Rc::new(module); 60 | rc_module.methods.borrow_mut().insert("tür_bilgisi".to_string(), FunctionReference::native_function(Self::type_info as NativeCall, "tür_bilgisi".to_string(), rc_module.clone())); 61 | rc_module 62 | } 63 | 64 | pub fn type_info(parameter: FunctionParameter) -> NativeCallResult { 65 | if parameter.length() > 1 { 66 | return n_parameter_expected!("tür_bilgisi".to_string(), 1); 67 | } 68 | 69 | match parameter.iter().next() { 70 | Some(arg) => Ok(VmObject::from(Rc::new(arg.deref().get_type()))), 71 | None => Ok(EMPTY_OBJECT) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /karamellib/src/buildin/class/baseclass.rs: -------------------------------------------------------------------------------- 1 | use crate::{buildin::{Class, ClassProperty}, compiler::function::{IndexerGetCall, IndexerSetCall, NativeCall, FunctionFlag}, types::VmObject}; 2 | use crate::compiler::{KaramelPrimative, function::{FunctionReference}}; 3 | 4 | use std::{rc::Rc}; 5 | use crate::compiler::GetType; 6 | use crate::buildin::ClassConfig; 7 | 8 | #[derive(Default)] 9 | pub struct BasicInnerClass { 10 | config: ClassConfig 11 | } 12 | 13 | impl Class for BasicInnerClass { 14 | fn set_class_config(&mut self, config: ClassConfig) { 15 | self.config = config; 16 | } 17 | 18 | fn get_class_name(&self) -> String { 19 | self.config.name.clone() 20 | } 21 | 22 | fn has_element(&self, _: Option, field: Rc) -> bool { 23 | self.config.properties.get(&*field).is_some() 24 | } 25 | 26 | fn properties(&self) -> std::collections::hash_map::Iter<'_, String, ClassProperty> { 27 | self.config.properties.iter() 28 | } 29 | 30 | fn get_element(&self, _: Option, field: Rc) -> Option { 31 | match self.config.properties.get(&*field) { 32 | Some(data) => Some((*data).clone()), 33 | None => None 34 | } 35 | } 36 | 37 | fn property_count(&self) -> usize { 38 | self.config.properties.len() 39 | } 40 | 41 | fn add_method(&mut self, name: &str, function: NativeCall, flags: FunctionFlag) { 42 | self.config.properties.insert(name.to_string(), ClassProperty::Function(FunctionReference::buildin_function(function, name.to_string(), flags))); 43 | } 44 | 45 | fn add_property(&mut self, name: &str, property: Rc) { 46 | self.config.properties.insert(name.to_string(), ClassProperty::Field(property)); 47 | } 48 | 49 | fn set_getter(&mut self, indexer: IndexerGetCall) { 50 | self.config.indexer.get = Some(indexer); 51 | } 52 | 53 | fn get_getter(&self) -> Option { 54 | match &self.config.indexer.get { 55 | Some(indexer) => Some(*indexer), 56 | None => None 57 | } 58 | } 59 | 60 | fn set_setter(&mut self, indexer: IndexerSetCall) { 61 | self.config.indexer.set = Some(indexer); 62 | } 63 | 64 | fn get_setter(&self) -> Option { 65 | match &self.config.indexer.set { 66 | Some(indexer) => Some(*indexer), 67 | None => None 68 | } 69 | } 70 | } 71 | 72 | impl BasicInnerClass { 73 | pub fn set_name(&mut self, name: &str) { 74 | if self.config.name.len() == 0 { 75 | self.config.name = name.to_string(); 76 | } 77 | } 78 | 79 | pub fn add_static_method(&mut self, name: &str, function: NativeCall) { 80 | self.add_method(name, function, FunctionFlag::IN_CLASS & FunctionFlag::STATIC); 81 | } 82 | 83 | pub fn add_class_method(&mut self, name: &str, function: NativeCall) { 84 | self.add_method(name, function, FunctionFlag::IN_CLASS); 85 | } 86 | } 87 | 88 | impl GetType for BasicInnerClass { 89 | fn get_type(&self) -> String { 90 | self.config.name.clone() 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod test { 96 | use std::rc::Rc; 97 | use crate::buildin::Class; 98 | use crate::compiler::GetType; 99 | use crate::buildin::class::baseclass::BasicInnerClass; 100 | use crate::compiler::KaramelPrimative; 101 | 102 | #[test] 103 | fn test_opcode_class_1() { 104 | let opcode_class = BasicInnerClass::default(); 105 | assert_eq!(opcode_class.get_type().len(), 0); 106 | assert_eq!(opcode_class.property_count(), 0); 107 | } 108 | 109 | #[test] 110 | fn test_opcode_class_2() { 111 | let mut opcode_class: BasicInnerClass = BasicInnerClass::default(); 112 | opcode_class.set_name("test_class"); 113 | 114 | assert_eq!(opcode_class.get_class_name(), "test_class".to_string()); 115 | assert_eq!(opcode_class.property_count(), 0); 116 | assert_eq!(opcode_class.get_type(), opcode_class.get_class_name()); 117 | assert_eq!(opcode_class.get_type(), "test_class".to_string()); 118 | } 119 | 120 | #[test] 121 | fn test_opcode_class_4() { 122 | let mut opcode_class: BasicInnerClass = BasicInnerClass::default(); 123 | opcode_class.set_name("test_class"); 124 | 125 | opcode_class.add_property("field_1", Rc::new(KaramelPrimative::Number(1024.0))); 126 | opcode_class.add_property("field_2", Rc::new(KaramelPrimative::Number(2048.0))); 127 | 128 | assert_eq!(opcode_class.get_class_name(), "test_class".to_string()); 129 | assert_eq!(opcode_class.property_count(), 2); 130 | } 131 | } -------------------------------------------------------------------------------- /karamellib/src/buildin/class/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod number; 2 | pub mod text; 3 | pub mod list; 4 | pub mod dict; 5 | pub mod baseclass; 6 | pub mod proxy; 7 | 8 | use crate::buildin::class::baseclass::BasicInnerClass; 9 | use std::{collections::HashSet, rc::Rc}; 10 | use lazy_static::*; 11 | 12 | use super::Class; 13 | 14 | use std::sync::Mutex; 15 | 16 | lazy_static! { 17 | pub static ref PRIMATIVE_CLASS_NAMES: Mutex> = Mutex::new(HashSet::new()); 18 | } 19 | 20 | pub fn get_empty_class() -> Rc { 21 | let mut opcode = BasicInnerClass::default(); 22 | opcode.set_name("__NO__CLASS__"); 23 | Rc::new(opcode) 24 | } 25 | 26 | 27 | #[macro_export] 28 | macro_rules! nativecall_test { 29 | ($name:ident, $function_name:ident, $query:expr, $result:expr) => { 30 | #[test] 31 | fn $name () { 32 | use std::cell::RefCell; 33 | let stack: Vec = Vec::new(); 34 | let stdout = Some(RefCell::new(String::new())); 35 | let stderr = Some(RefCell::new(String::new())); 36 | 37 | let parameter = FunctionParameter::new(&stack, Some(VmObject::native_convert($query)), 0, 0, &stdout, &stderr); 38 | let result = $function_name(parameter); 39 | assert!(result.is_ok()); 40 | let object = result.unwrap().deref(); 41 | assert_eq!(*object, $result); 42 | } 43 | }; 44 | } 45 | 46 | #[macro_export] 47 | macro_rules! primative_text { 48 | ($text:expr) => { 49 | KaramelPrimative::Text(Rc::new($text.to_string())) 50 | }; 51 | } 52 | 53 | #[macro_export] 54 | macro_rules! primative_number { 55 | ($number:expr) => { 56 | KaramelPrimative::Number($number as f64) 57 | }; 58 | } 59 | 60 | #[macro_export] 61 | macro_rules! primative_list { 62 | ($list:expr) => { 63 | KaramelPrimative::List(RefCell::new($list)) 64 | }; 65 | } 66 | 67 | #[macro_export] 68 | macro_rules! arc_text { 69 | ($text:expr) => { 70 | VmObject::native_convert(primative_text!($text)) 71 | }; 72 | } 73 | 74 | #[macro_export] 75 | macro_rules! arc_number { 76 | ($number:expr) => { 77 | VmObject::from($number as f64) 78 | }; 79 | } 80 | 81 | #[macro_export] 82 | macro_rules! arc_bool { 83 | ($bool:expr) => { 84 | VmObject::from($bool) 85 | }; 86 | } 87 | 88 | #[macro_export] 89 | macro_rules! arc_empty { 90 | () => { 91 | EMPTY_OBJECT 92 | }; 93 | } 94 | 95 | #[macro_export] 96 | macro_rules! nativecall_test_with_params { 97 | ($name:ident, $function_name:ident, $query:expr, $params:expr, $result:expr) => { 98 | #[test] 99 | fn $name () { 100 | use std::cell::RefCell; 101 | let stack: Vec = $params.to_vec(); 102 | let stdout = Some(RefCell::new(String::new())); 103 | let stderr = Some(RefCell::new(String::new())); 104 | 105 | let parameter = FunctionParameter::new(&stack, Some(VmObject::native_convert($query)), stack.len() as usize, stack.len() as u8, &stdout, &stderr); 106 | let result = $function_name(parameter); 107 | assert!(result.is_ok()); 108 | let object = result.unwrap().deref(); 109 | assert_eq!(*object, $result); 110 | } 111 | }; 112 | } 113 | 114 | #[macro_export] 115 | macro_rules! n_parameter_check { 116 | ($function_name:expr, $parameter_size:expr) => { 117 | if parameter.length() > 1 { 118 | return n_parameter_expected!("tür_bilgisi".to_string(), 1); 119 | } 120 | }; 121 | } 122 | 123 | #[macro_export] 124 | macro_rules! n_parameter_expected { 125 | ($function_name:expr, $parameter_size:expr) => { Err(KaramelErrorType::FunctionArgumentNotMatching { 126 | function: $function_name, 127 | expected: $parameter_size, 128 | found: 0 129 | }) }; 130 | ($function_name:expr, $parameter_size:expr, $parameter_found:expr) => { Err(KaramelErrorType::FunctionArgumentNotMatching { 131 | function: $function_name, 132 | expected: $parameter_size, 133 | found: $parameter_found 134 | }) }; 135 | } 136 | 137 | #[macro_export] 138 | macro_rules! expected_parameter_type { 139 | ($function_name:expr, $expected_type:expr) => { Err(KaramelErrorType::FunctionExpectedThatParameterType { 140 | function: $function_name, 141 | expected: $expected_type 142 | }) }; 143 | } 144 | -------------------------------------------------------------------------------- /karamellib/src/buildin/class/proxy.rs: -------------------------------------------------------------------------------- 1 | use crate::compiler::KaramelPrimative; 2 | use crate::{ 3 | buildin::{Class, ClassProperty}, 4 | compiler::function::{IndexerGetCall, IndexerSetCall, NativeCall, FunctionFlag}, 5 | types::VmObject, 6 | }; 7 | 8 | use crate::buildin::ClassConfig; 9 | use crate::compiler::GetType; 10 | use std::rc::Rc; 11 | 12 | #[derive(Default)] 13 | pub struct ProxyClass { 14 | config: ClassConfig 15 | } 16 | 17 | impl Class for ProxyClass { 18 | fn set_class_config(&mut self, _: ClassConfig) {} 19 | 20 | fn get_class_name(&self) -> String { 21 | "".to_string() 22 | } 23 | 24 | fn has_element(&self, source: Option, field: Rc) -> bool { 25 | match source { 26 | Some(source_object) => match &*source_object.deref() { 27 | KaramelPrimative::Class(class) => class.has_element(source, field), 28 | _ => false 29 | }, 30 | None => false, 31 | } 32 | } 33 | 34 | fn properties(&self) -> std::collections::hash_map::Iter<'_, String, ClassProperty> { 35 | self.config.properties.iter() 36 | } 37 | 38 | fn get_element(&self, source: Option, field: Rc) -> Option { 39 | match source { 40 | Some(source_object) => match &*source_object.deref() { 41 | KaramelPrimative::Class(class) => class.get_element(source, field), 42 | _ => None 43 | }, 44 | None => None, 45 | } 46 | } 47 | 48 | fn property_count(&self) -> usize { 49 | 0 50 | } 51 | 52 | fn add_method(&mut self, _: &str, _: NativeCall, _: FunctionFlag) {} 53 | 54 | fn add_property(&mut self, _: &str, _: Rc) {} 55 | 56 | fn set_getter(&mut self, _: IndexerGetCall) {} 57 | 58 | fn get_getter(&self) -> Option { 59 | None 60 | } 61 | 62 | fn set_setter(&mut self, _: IndexerSetCall) {} 63 | 64 | fn get_setter(&self) -> Option { 65 | None 66 | } 67 | } 68 | 69 | pub fn get_primative_class() -> Rc { 70 | Rc::new(ProxyClass { 71 | config: ClassConfig::default() 72 | }) 73 | } 74 | 75 | impl ProxyClass { 76 | pub fn set_name(&mut self, _: &str) {} 77 | } 78 | 79 | impl GetType for ProxyClass { 80 | fn get_type(&self) -> String { 81 | "".to_string() 82 | } 83 | } 84 | 85 | #[cfg(test)] 86 | mod test { 87 | use crate::buildin::class::baseclass::BasicInnerClass; 88 | use crate::buildin::Class; 89 | use crate::compiler::KaramelPrimative; 90 | use crate::compiler::GetType; 91 | use std::rc::Rc; 92 | 93 | #[test] 94 | fn test_opcode_class_1() { 95 | let opcode_class = BasicInnerClass::default(); 96 | assert_eq!(opcode_class.get_type().len(), 0); 97 | assert_eq!(opcode_class.property_count(), 0); 98 | } 99 | 100 | #[test] 101 | fn test_opcode_class_2() { 102 | let mut opcode_class: BasicInnerClass = BasicInnerClass::default(); 103 | opcode_class.set_name("test_class"); 104 | 105 | assert_eq!(opcode_class.get_class_name(), "test_class".to_string()); 106 | assert_eq!(opcode_class.property_count(), 0); 107 | assert_eq!(opcode_class.get_type(), opcode_class.get_class_name()); 108 | assert_eq!(opcode_class.get_type(), "test_class".to_string()); 109 | } 110 | 111 | #[test] 112 | fn test_opcode_class_4() { 113 | let mut opcode_class: BasicInnerClass = BasicInnerClass::default(); 114 | opcode_class.set_name("test_class"); 115 | 116 | opcode_class.add_property("field_1", Rc::new(KaramelPrimative::Number(1024.0))); 117 | opcode_class.add_property("field_2", Rc::new(KaramelPrimative::Number(2048.0))); 118 | 119 | assert_eq!(opcode_class.get_class_name(), "test_class".to_string()); 120 | assert_eq!(opcode_class.property_count(), 2); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /karamellib/src/buildin/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::buildin::{Module, Class}; 2 | use crate::compiler::function::{FunctionReference, NativeCall, NativeCallResult}; 3 | use crate::compiler::function::FunctionParameter; 4 | use crate::compiler::value::EMPTY_OBJECT; 5 | use crate::error::KaramelErrorType; 6 | use std::cell::RefCell; 7 | use std::collections::HashMap; 8 | use std::rc::Rc; 9 | 10 | #[derive(Clone)] 11 | pub struct DebugModule { 12 | methods: RefCell>>, 13 | path: Vec 14 | } 15 | 16 | impl Module for DebugModule { 17 | fn get_module_name(&self) -> String { 18 | "hataayıklama".to_string() 19 | } 20 | 21 | fn get_path(&self) -> &Vec { 22 | &self.path 23 | } 24 | 25 | fn get_method(&self, name: &str) -> Option> { 26 | self.methods.borrow().get(name).map(|method| method.clone()) 27 | } 28 | 29 | fn get_module(&self, _: &str) -> Option> { 30 | None 31 | } 32 | 33 | fn get_methods(&self) -> Vec> { 34 | let mut response = Vec::new(); 35 | self.methods.borrow().iter().for_each(|(_, reference)| response.push(reference.clone())); 36 | response 37 | } 38 | 39 | fn get_modules(&self) -> HashMap> { 40 | HashMap::new() 41 | } 42 | 43 | fn get_classes(&self) -> Vec> { 44 | Vec::new() 45 | } 46 | } 47 | 48 | impl DebugModule { 49 | pub fn new() -> Rc { 50 | let module = DebugModule { 51 | methods: RefCell::new(HashMap::new()), 52 | path: vec!["hataayıklama".to_string()] 53 | }; 54 | 55 | let rc_module = Rc::new(module); 56 | rc_module.methods.borrow_mut().insert("doğrula".to_string(), FunctionReference::native_function(Self::assert as NativeCall, "doğrula".to_string(), rc_module.clone())); 57 | rc_module.clone() 58 | } 59 | 60 | pub fn assert(parameter: FunctionParameter) -> NativeCallResult { 61 | match parameter.length() { 62 | 1 => { 63 | match parameter.iter().next().unwrap().deref().is_true() { 64 | false => Err(KaramelErrorType::AssertFailed), 65 | true => Ok(EMPTY_OBJECT) 66 | } 67 | }, 68 | 2 => { 69 | let mut iter = parameter.iter(); 70 | let left = iter.next().unwrap().deref(); 71 | let right = iter.next().unwrap().deref(); 72 | match left == right { 73 | false => Err(KaramelErrorType::AssertFailedWithArgument { 74 | left: left.clone(), 75 | right: right.clone() 76 | }), 77 | true => Ok(EMPTY_OBJECT) 78 | } 79 | }, 80 | _ => Err(KaramelErrorType::AssertFailed) 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /karamellib/src/buildin/io.rs: -------------------------------------------------------------------------------- 1 | use crate::compiler::{function::{FunctionParameter, FunctionReference, NativeCall, NativeCallResult}}; 2 | use crate::types::{VmObject}; 3 | use crate::compiler::value::EMPTY_OBJECT; 4 | use crate::buildin::{Module, Class}; 5 | use std::{cell::RefCell, collections::HashMap}; 6 | use std::rc::Rc; 7 | use std::io; 8 | 9 | use log; 10 | 11 | 12 | #[derive(Clone)] 13 | pub struct IoModule { 14 | methods: RefCell>>, 15 | path: Vec 16 | } 17 | 18 | impl Module for IoModule { 19 | fn get_module_name(&self) -> String { 20 | "gç".to_string() 21 | } 22 | 23 | fn get_path(&self) -> &Vec { 24 | &self.path 25 | } 26 | 27 | fn get_method(&self, name: &str) -> Option> { 28 | self.methods.borrow().get(name).map(|method| method.clone()) 29 | } 30 | 31 | fn get_module(&self, _: &str) -> Option> { 32 | None 33 | } 34 | 35 | fn get_methods(&self) -> Vec> { 36 | let mut response = Vec::new(); 37 | self.methods.borrow().iter().for_each(|(_, reference)| response.push(reference.clone())); 38 | response 39 | } 40 | 41 | fn get_modules(&self) -> HashMap> { 42 | HashMap::new() 43 | } 44 | 45 | fn get_classes(&self) -> Vec> { 46 | Vec::new() 47 | } 48 | } 49 | 50 | impl IoModule { 51 | pub fn new() -> Rc { 52 | let module = IoModule { 53 | methods: RefCell::new(HashMap::new()), 54 | path: vec!["gç".to_string()] 55 | }; 56 | 57 | let rc_module = Rc::new(module); 58 | rc_module.methods.borrow_mut().insert("satıroku".to_string(), FunctionReference::native_function(Self::readline as NativeCall, "satıroku".to_string(), rc_module.clone())); 59 | rc_module.methods.borrow_mut().insert("satiroku".to_string(), FunctionReference::native_function(Self::readline as NativeCall, "satiroku".to_string(), rc_module.clone())); 60 | rc_module.methods.borrow_mut().insert("yaz".to_string(), FunctionReference::native_function(Self::print as NativeCall, "yaz".to_string(), rc_module.clone())); 61 | rc_module.methods.borrow_mut().insert("satıryaz".to_string(), FunctionReference::native_function(Self::printline as NativeCall, "satıryaz".to_string(), rc_module.clone())); 62 | rc_module.methods.borrow_mut().insert("satiryaz".to_string(), FunctionReference::native_function(Self::printline as NativeCall, "satiryaz".to_string(), rc_module.clone())); 63 | rc_module.methods.borrow_mut().insert("biçimlendir".to_string(), FunctionReference::native_function(Self::format as NativeCall, "biçimlendir".to_string(), rc_module.clone())); 64 | rc_module.methods.borrow_mut().insert("bicimlendir".to_string(), FunctionReference::native_function(Self::format as NativeCall, "bicimlendir".to_string(), rc_module.clone())); 65 | rc_module.clone() 66 | } 67 | 68 | pub fn readline(_: FunctionParameter) -> NativeCallResult { 69 | let mut line = String::new(); 70 | match io::stdin().read_line(&mut line) { 71 | Ok(_) => return Ok(VmObject::from(Rc::new(line.trim().to_string()))), 72 | _ => Ok(EMPTY_OBJECT) 73 | } 74 | } 75 | 76 | pub fn print(parameter: FunctionParameter) -> NativeCallResult { 77 | let mut buffer = String::new(); 78 | for arg in parameter.iter() { 79 | buffer.push_str(&format!("{}", arg.deref())); 80 | } 81 | log::info!("{}", buffer); 82 | 83 | parameter.write_to_stdout(&buffer); 84 | Ok(EMPTY_OBJECT) 85 | } 86 | 87 | pub fn printline(parameter: FunctionParameter) -> NativeCallResult { 88 | let mut buffer = String::new(); 89 | 90 | for arg in parameter.iter() { 91 | buffer.push_str(&format!("{}", arg.deref())); 92 | } 93 | 94 | buffer.push_str(&"\r\n"); 95 | log::info!("{}", buffer); 96 | 97 | parameter.write_to_stdout(&buffer); 98 | Ok(EMPTY_OBJECT) 99 | } 100 | 101 | pub fn format(parameter: FunctionParameter) -> NativeCallResult { 102 | if parameter.length() != 1 { 103 | return Ok(EMPTY_OBJECT); 104 | } 105 | 106 | Ok(VmObject::from(Rc::new(format!("{}", parameter.iter().next().unwrap().deref())))) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /karamellib/src/buildin/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod debug; 2 | pub mod io; 3 | pub mod num; 4 | pub mod base_functions; 5 | 6 | use std::collections::hash_map::Iter; 7 | 8 | #[macro_use] 9 | pub mod class; 10 | 11 | use crate::{compiler::{GetType, function::{IndexerGetCall, IndexerSetCall, FunctionFlag}}, types::VmObject}; 12 | 13 | use std::collections::HashMap; 14 | use std::vec::Vec; 15 | use std::rc::Rc; 16 | 17 | use crate::compiler::{KaramelPrimative, function::{FunctionReference, NativeCall}}; 18 | 19 | pub trait Module { 20 | fn get_module_name(&self) -> String; 21 | fn get_path(&self) -> &Vec; 22 | 23 | fn get_method(&self, name: &str) -> Option>; 24 | fn get_module(&self, name: &str) -> Option>; 25 | 26 | fn get_methods(&self) -> Vec>; 27 | fn get_modules(&self) -> HashMap>; 28 | 29 | fn get_classes(&self) -> Vec>; 30 | } 31 | 32 | pub struct ModuleCollectionIterator<'a> { 33 | iter: Iter<'a, String, Rc> 34 | } 35 | 36 | pub struct ModuleCollection { 37 | modules: HashMap> 38 | } 39 | 40 | impl ModuleCollection 41 | { 42 | pub fn new() -> ModuleCollection { 43 | ModuleCollection { 44 | modules: HashMap::new() 45 | } 46 | } 47 | 48 | pub fn add_module(&mut self, module: Rc) { 49 | self.modules.insert(module.get_module_name(), module); 50 | } 51 | 52 | pub fn iter(&self) -> ModuleCollectionIterator { 53 | ModuleCollectionIterator { 54 | iter: self.modules.iter().clone() 55 | } 56 | } 57 | 58 | pub fn has_module(&self, module_path: &Vec) -> bool { 59 | self.modules.iter().find_map(|(key, module)| if module.get_path() == module_path { Some(key) } else { None }).is_some() 60 | } 61 | } 62 | 63 | impl<'a> Iterator for ModuleCollectionIterator<'a> { 64 | type Item = (&'a String, &'a Rc); 65 | 66 | fn next(&mut self) -> Option { 67 | self.iter.next() 68 | } 69 | } 70 | 71 | #[derive(Clone)] 72 | pub enum ClassProperty { 73 | Function(Rc), 74 | Field(Rc) 75 | } 76 | 77 | #[derive(Default)] 78 | pub struct ClassConfig { 79 | pub name: String, 80 | pub storage_index: usize, 81 | pub properties: HashMap, 82 | pub is_readonly: bool, 83 | pub is_buildin: bool, 84 | pub is_static: bool, 85 | pub indexer: Indexer 86 | } 87 | 88 | #[derive(Default)] 89 | pub struct Indexer { 90 | pub get: Option, 91 | pub set: Option 92 | } 93 | 94 | 95 | pub trait Class: GetType { 96 | fn set_class_config(&mut self, config: ClassConfig); 97 | fn get_class_name(&self) -> String; 98 | 99 | fn has_element(&self, source: Option, field: Rc) -> bool; 100 | fn get_element(&self, source: Option, field: Rc) -> Option; 101 | fn property_count(&self) -> usize; 102 | fn properties(&self) -> std::collections::hash_map::Iter<'_, String, ClassProperty>; 103 | 104 | fn add_method(&mut self, name: &str, function: NativeCall, flags: FunctionFlag); 105 | fn add_property(&mut self, name: &str, property: Rc); 106 | 107 | fn set_getter(&mut self, indexer: IndexerGetCall); 108 | fn get_getter(&self) -> Option; 109 | 110 | fn set_setter(&mut self, indexer: IndexerSetCall); 111 | fn get_setter(&self) -> Option; 112 | } 113 | 114 | pub struct DummyModule { 115 | name: String, 116 | path: Vec 117 | } 118 | 119 | impl DummyModule { 120 | pub fn new() -> Self { 121 | DummyModule { 122 | name: "!dummy".to_string(), 123 | path: vec!["!dummy".to_string()] 124 | } 125 | } 126 | } 127 | 128 | impl Module for DummyModule { 129 | fn get_module_name(&self) -> String { self.name.to_string() } 130 | fn get_path(&self) -> &Vec { &self.path } 131 | 132 | fn get_method(&self, _: &str) -> Option> { None } 133 | fn get_module(&self, _: &str) -> Option> { None } 134 | 135 | fn get_methods(&self) -> Vec> { Vec::new() } 136 | fn get_modules(&self) -> HashMap> { HashMap::new() } 137 | 138 | fn get_classes(&self) -> Vec> { Vec::new() } 139 | } 140 | -------------------------------------------------------------------------------- /karamellib/src/buildin/num.rs: -------------------------------------------------------------------------------- 1 | use crate::compiler::{function::{FunctionParameter, FunctionReference, NativeCall, NativeCallResult}}; 2 | use crate::types::VmObject; 3 | use crate::compiler::value::KaramelPrimative; 4 | use crate::compiler::value::EMPTY_OBJECT; 5 | use crate::error::KaramelErrorType; 6 | use crate::buildin::{Module, Class}; 7 | use crate::{n_parameter_expected, expected_parameter_type}; 8 | use std::{cell::RefCell, collections::HashMap}; 9 | use std::rc::Rc; 10 | 11 | pub struct NumModule { 12 | methods: RefCell>>, 13 | path: Vec 14 | } 15 | 16 | impl Module for NumModule { 17 | fn get_module_name(&self) -> String { 18 | "sayı".to_string() 19 | } 20 | 21 | fn get_path(&self) -> &Vec { 22 | &self.path 23 | } 24 | 25 | fn get_method(&self, name: &str) -> Option> { 26 | match self.methods.borrow().get(name) { 27 | Some(method) => Some(method.clone()), 28 | None => None 29 | } 30 | } 31 | 32 | fn get_module(&self, _: &str) -> Option> { 33 | None 34 | } 35 | 36 | fn get_methods(&self) -> Vec> { 37 | let mut response = Vec::new(); 38 | self.methods.borrow().iter().for_each(|(_, reference)| response.push(reference.clone())); 39 | response 40 | } 41 | 42 | fn get_modules(&self) -> HashMap> { 43 | HashMap::new() 44 | } 45 | 46 | fn get_classes(&self) -> Vec> { 47 | Vec::new() 48 | } 49 | } 50 | 51 | impl NumModule { 52 | pub fn new() -> Rc { 53 | let module = NumModule { 54 | methods: RefCell::new(HashMap::new()), 55 | path: vec!["sayı".to_string()] 56 | }; 57 | 58 | let rc_module = Rc::new(module); 59 | rc_module.methods.borrow_mut().insert("oku".to_string(), FunctionReference::native_function(Self::parse as NativeCall, "tür_bilgisi".to_string(), rc_module.clone())); 60 | rc_module.clone() 61 | } 62 | 63 | pub fn parse(parameter: FunctionParameter) -> NativeCallResult { 64 | if parameter.length() > 1 { 65 | return n_parameter_expected!("oku".to_string(), 1); 66 | } 67 | 68 | let arg = match parameter.iter().next() { 69 | Some(arg) => arg.deref(), 70 | None => return Ok(EMPTY_OBJECT) 71 | }; 72 | 73 | match &*arg { 74 | KaramelPrimative::Number(_) => Ok(*parameter.iter().next().unwrap()), 75 | KaramelPrimative::Text(text) => { 76 | match (*text).parse::() { 77 | Ok(num) => Ok(VmObject::from(num)), 78 | _ => expected_parameter_type!("oku".to_string(), "Yazı".to_string()) 79 | } 80 | }, 81 | _ => Ok(EMPTY_OBJECT) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /karamellib/src/compiler/ast.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::vec::Vec; 3 | use std::rc::Rc; 4 | 5 | use crate::compiler::value::KaramelPrimative; 6 | use crate::syntax::loops::LoopType; 7 | use crate::types::KaramelOperatorType; 8 | 9 | #[repr(C)] 10 | #[derive(Clone)] 11 | #[derive(Debug)] 12 | #[derive(PartialEq)] 13 | pub struct KaramelIfStatementElseItem { 14 | pub condition: Rc, 15 | pub body: Rc 16 | } 17 | 18 | #[repr(C)] 19 | #[derive(Clone)] 20 | #[derive(Debug)] 21 | #[derive(PartialEq)] 22 | pub struct KaramelDictItem { 23 | pub key: Rc, 24 | pub value: Rc 25 | } 26 | 27 | impl KaramelIfStatementElseItem { 28 | pub fn new(condition: Rc, body: Rc) -> KaramelIfStatementElseItem { 29 | KaramelIfStatementElseItem { 30 | condition, 31 | body, 32 | } 33 | } 34 | } 35 | 36 | #[repr(C)] 37 | #[derive(Clone)] 38 | #[derive(Debug)] 39 | #[derive(PartialEq)] 40 | pub enum KaramelAstType { 41 | None, 42 | NewLine, 43 | Block(Vec>), 44 | FuncCall { 45 | func_name_expression: Rc, 46 | arguments: Vec>, 47 | assign_to_temp: Cell 48 | }, 49 | AccessorFuncCall { 50 | source: Rc, 51 | indexer: Rc, 52 | assign_to_temp: Cell 53 | }, 54 | Primative(Rc), 55 | Binary { 56 | left: Rc, 57 | operator: KaramelOperatorType, 58 | right: Rc 59 | }, 60 | Control { 61 | left: Rc, 62 | operator: KaramelOperatorType, 63 | right: Rc 64 | }, 65 | /*Control,*/ 66 | PrefixUnary { 67 | operator: KaramelOperatorType, 68 | expression: Rc, 69 | assign_to_temp: Cell 70 | }, 71 | SuffixUnary(KaramelOperatorType, Rc), 72 | Assignment { 73 | variable: Rc, 74 | operator: KaramelOperatorType, 75 | expression: Rc 76 | }, 77 | IfStatement { 78 | condition: Rc, 79 | body: Rc, 80 | else_body: Option>, 81 | else_if: Vec> 82 | }, 83 | FunctionDefination { 84 | name: String, 85 | arguments: Vec, 86 | body: Rc 87 | }, 88 | Symbol(String), 89 | ModulePath(Vec), 90 | Load(Vec), 91 | List(Vec>), 92 | Dict(Vec>), 93 | Indexer { body: Rc, indexer: Rc }, 94 | Return(Rc), 95 | Break, 96 | Continue, 97 | Loop { 98 | loop_type: LoopType, 99 | body: Rc 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/call.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 6 | 7 | #[derive(Clone)] 8 | 9 | /// Function call type. Karamel is support two type of function call mechanism 10 | pub enum CallType { 11 | 12 | /// Call function from memory location 13 | Call { constant_location: u8 }, 14 | 15 | /// Call function from last stack value 16 | CallStack 17 | } 18 | 19 | #[derive(Clone)] 20 | /// Generate function call opcodes. 21 | pub struct CallGenerator { 22 | 23 | /// Type of the function call type. 24 | pub call_type: CallType, 25 | 26 | /// How many arguments are passed to function 27 | pub argument_size: u8, 28 | 29 | /// Function return value needs to be assigned to stack location or discarded 30 | pub assign_to_temp: bool 31 | } 32 | 33 | /// Generate function call opcodes based on givin parameters 34 | impl OpcodeGeneratorTrait for CallGenerator { 35 | fn generate(&self, opcodes: &mut Vec) { 36 | match self.call_type { 37 | CallType::Call { constant_location } => { 38 | opcodes.push(VmOpCode::Call.into()); 39 | opcodes.push(constant_location); 40 | }, 41 | CallType::CallStack => opcodes.push(VmOpCode::CallStack.into()) 42 | }; 43 | opcodes.push(self.argument_size); 44 | opcodes.push(self.assign_to_temp.into()); 45 | } 46 | 47 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, _: &Vec) { 48 | let opcode_index = index.fetch_add(3, Ordering::SeqCst); 49 | 50 | match self.call_type { 51 | CallType::Call { constant_location } => { 52 | index.fetch_add(1, Ordering::SeqCst); 53 | builder.add(opcode_index, VmOpCode::Call, constant_location.to_string(), self.argument_size.to_string(), (self.assign_to_temp as u8).to_string()); 54 | }, 55 | CallType::CallStack => { 56 | builder.add(opcode_index, VmOpCode::CallStack, self.argument_size.to_string(), (self.assign_to_temp as u8).to_string(), "".to_string()); 57 | } 58 | }; 59 | } 60 | } 61 | 62 | 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use super::*; 67 | 68 | #[test] 69 | fn test_1() { 70 | let mut opcodes = Vec::new(); 71 | let generator = CallGenerator { 72 | call_type: CallType::Call { constant_location: 100 }, 73 | argument_size: 1, 74 | assign_to_temp: false 75 | }; 76 | 77 | generator.generate(&mut opcodes); 78 | 79 | assert_eq!(opcodes.len(), 4); 80 | assert_eq!(opcodes[0], VmOpCode::Call.into()); 81 | assert_eq!(opcodes[1], 100); 82 | assert_eq!(opcodes[2], 1); 83 | assert_eq!(opcodes[3], 0); 84 | } 85 | 86 | #[test] 87 | fn test_2() { 88 | let mut opcodes = Vec::new(); 89 | let generator = CallGenerator { 90 | call_type: CallType::CallStack, 91 | argument_size: 5, 92 | assign_to_temp: true 93 | }; 94 | 95 | generator.generate(&mut opcodes); 96 | 97 | assert_eq!(opcodes.len(), 3); 98 | assert_eq!(opcodes[0], VmOpCode::CallStack.into()); 99 | assert_eq!(opcodes[1], 5); 100 | assert_eq!(opcodes[2], 1); 101 | } 102 | 103 | #[test] 104 | fn test_3() { 105 | let mut opcodes = Vec::new(); 106 | let generator = CallGenerator { 107 | call_type: CallType::Call { constant_location: 100 }, 108 | argument_size: 5, 109 | assign_to_temp: true 110 | }; 111 | 112 | generator.generate(&mut opcodes); 113 | 114 | assert_eq!(opcodes.len(), 4); 115 | assert_eq!(opcodes[0], VmOpCode::Call.into()); 116 | assert_eq!(opcodes[1], 100); 117 | assert_eq!(opcodes[2], 5); 118 | assert_eq!(opcodes[3], 1); 119 | } 120 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/compare.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait, OpcodeLocation, opcode_to_location}; 6 | 7 | 8 | #[derive(Clone)] 9 | /// Generate compare opcodes 10 | pub struct CompareGenerator { pub location: Rc } 11 | impl OpcodeGeneratorTrait for CompareGenerator { 12 | fn generate(&self, opcodes: &mut Vec) { 13 | opcodes.push(VmOpCode::Compare.into()); 14 | self.location.apply(opcodes); 15 | } 16 | 17 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, opcodes: &Vec) { 18 | let opcode_index = index.fetch_add(1, Ordering::SeqCst); 19 | let location = opcode_to_location(index, opcodes); 20 | builder.add(opcode_index, VmOpCode::Compare, location.to_string(), "".to_string(), "".to_string()); 21 | } 22 | } 23 | 24 | impl CompareGenerator { 25 | pub fn get(&self) -> usize { 26 | self.location.get() 27 | } 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | 34 | #[test] 35 | fn test_1() { 36 | let mut opcodes = Vec::new(); 37 | let location = Rc::new(OpcodeLocation::new(123)); 38 | let generator = CompareGenerator { 39 | location: location.clone() 40 | }; 41 | 42 | generator.generate(&mut opcodes); 43 | 44 | assert_eq!(opcodes.len(), 3); 45 | assert_eq!(opcodes[0], VmOpCode::Compare.into()); 46 | assert_eq!(opcodes[1], 123); 47 | assert_eq!(opcodes[2], 0); 48 | } 49 | 50 | #[test] 51 | fn test_2() { 52 | let mut opcodes = Vec::new(); 53 | let location = Rc::new(OpcodeLocation::new(123456789)); 54 | let generator = CompareGenerator { 55 | location: location.clone() 56 | }; 57 | 58 | generator.generate(&mut opcodes); 59 | 60 | assert_eq!(opcodes.len(), 3); 61 | assert_eq!(opcodes[0], VmOpCode::Compare.into()); 62 | assert_eq!(opcodes[1], 21); 63 | assert_eq!(opcodes[2], 205); 64 | } 65 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/constant.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 6 | 7 | 8 | #[derive(Clone)] 9 | pub struct ConstantGenerator { pub location: u8 } 10 | impl OpcodeGeneratorTrait for ConstantGenerator { 11 | fn generate(&self, opcodes: &mut Vec) { 12 | opcodes.push(VmOpCode::Constant.into()); 13 | opcodes.push(self.location); 14 | } 15 | 16 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, _: &Vec) { 17 | let opcode_index = index.fetch_add(2, Ordering::SeqCst); 18 | builder.add(opcode_index, VmOpCode::Constant, self.location.to_string(), "".to_string(), "".to_string()); 19 | } 20 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/function.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::function::FunctionReference; 4 | 5 | use super::{OpcodeGeneratorTrait, DumpBuilder}; 6 | 7 | 8 | #[derive(Clone)] 9 | /// Generate jump opcodes. 10 | pub struct FunctionGenerator { 11 | pub function: Rc 12 | } 13 | 14 | impl OpcodeGeneratorTrait for FunctionGenerator { 15 | fn generate(&self, opcodes: &mut Vec) { 16 | (*self.function).opcode_location.set(opcodes.len()); 17 | opcodes.push(self.function.arguments.len() as u8); 18 | } 19 | 20 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, opcodes: &Vec) { 21 | let opcode_index = index.fetch_add(1, Ordering::SeqCst); 22 | builder.add_with_text(opcode_index, format!("[FUNCTION: {}]", self.function.name), opcodes[opcode_index].to_string(), "".to_string(), "".to_string()); 23 | } 24 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/init_dict.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 6 | 7 | #[derive(Debug)] 8 | #[derive(Clone)] 9 | pub struct InitDictGenerator { 10 | pub argument_size: usize 11 | } 12 | 13 | impl OpcodeGeneratorTrait for InitDictGenerator { 14 | fn generate(&self, opcodes: &mut Vec) { 15 | opcodes.push(VmOpCode::Init.into()); 16 | opcodes.push(0); 17 | opcodes.push(self.argument_size as u8); 18 | } 19 | 20 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, _: &Vec) { 21 | let opcode_index = index.fetch_add(3, Ordering::SeqCst); 22 | builder.add(opcode_index, VmOpCode::Init, "0".to_string(), self.argument_size.to_string(), "".to_string()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/init_list.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 6 | 7 | #[derive(Debug)] 8 | #[derive(Clone)] 9 | pub struct InitListGenerator { 10 | pub argument_size: usize 11 | } 12 | 13 | impl OpcodeGeneratorTrait for InitListGenerator { 14 | fn generate(&self, opcodes: &mut Vec) { 15 | opcodes.push(VmOpCode::Init.into()); 16 | opcodes.push(1); 17 | opcodes.push(self.argument_size as u8); 18 | } 19 | 20 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, _: &Vec) { 21 | let opcode_index = index.fetch_add(3, Ordering::SeqCst); 22 | builder.add(opcode_index, VmOpCode::Init, "1".to_string(), self.argument_size.to_string(), "".to_string()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/jump.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait, OpcodeLocation, opcode_to_location}; 6 | 7 | #[derive(Clone)] 8 | /// Generate jump opcodes. 9 | pub struct JumpGenerator { pub location: Rc } 10 | impl OpcodeGeneratorTrait for JumpGenerator { 11 | fn generate(&self, opcodes: &mut Vec) { 12 | opcodes.push(VmOpCode::Jump.into()); 13 | self.location.apply(opcodes); 14 | } 15 | 16 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, opcodes: &Vec) { 17 | let opcode_index = index.fetch_add(1, Ordering::SeqCst); 18 | let location = opcode_to_location(index, opcodes); 19 | builder.add(opcode_index, VmOpCode::Jump, location.to_string(), "".to_string(), "".to_string()); 20 | } 21 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/load.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 6 | 7 | 8 | #[derive(Clone)] 9 | pub struct LoadGenerator { pub location: u8 } 10 | impl OpcodeGeneratorTrait for LoadGenerator { 11 | fn generate(&self, opcodes: &mut Vec) { 12 | opcodes.push(VmOpCode::Load.into()); 13 | opcodes.push(self.location); 14 | } 15 | 16 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, _: &Vec) { 17 | let opcode_index = index.fetch_add(2, Ordering::SeqCst); 18 | builder.add(opcode_index, VmOpCode::Load, self.location.to_string(), "".to_string(), "".to_string()); 19 | } 20 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/location.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | use std::sync::atomic::{AtomicUsize, Ordering}; 3 | 4 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 5 | 6 | #[cfg(debug_assertions)] 7 | static OPCODE_LOCATION_INDEXER: AtomicUsize = AtomicUsize::new(0); 8 | 9 | #[derive(Clone)] 10 | pub enum LocationType { 11 | Fixed(usize), 12 | Subtraction { 13 | left_hand: Rc, 14 | right_hand: Rc 15 | } 16 | } 17 | 18 | #[derive(Clone)] 19 | pub struct OpcodeLocation { 20 | #[cfg(debug_assertions)] 21 | index: usize, 22 | location: RefCell, 23 | used_location: RefCell> 24 | } 25 | 26 | impl OpcodeLocation { 27 | pub fn new(location: usize) -> Self { 28 | OpcodeLocation { 29 | #[cfg(debug_assertions)] 30 | index: OPCODE_LOCATION_INDEXER.fetch_add(1, Ordering::SeqCst), 31 | location: RefCell::new(LocationType::Fixed(location)), 32 | used_location: RefCell::new(Vec::new()) 33 | } 34 | } 35 | 36 | pub fn empty() -> Self { 37 | OpcodeLocation { 38 | #[cfg(debug_assertions)] 39 | index: OPCODE_LOCATION_INDEXER.fetch_add(1, Ordering::SeqCst), 40 | location: RefCell::new(LocationType::Fixed(0)), 41 | used_location: RefCell::new(Vec::new()) 42 | } 43 | } 44 | 45 | #[cfg(debug_assertions)] 46 | pub fn get_index(&self) -> usize { 47 | self.index 48 | } 49 | 50 | pub fn get(&self) -> usize { 51 | match &*self.location.borrow() { 52 | LocationType::Fixed(location) => *location, 53 | LocationType::Subtraction { left_hand, right_hand } => { 54 | println!("{} {}", left_hand.get(), right_hand.get()); 55 | left_hand.get() - right_hand.get() 56 | } 57 | } 58 | } 59 | 60 | pub fn set(&self, location: usize, opcodes: &mut Vec) { 61 | *self.location.borrow_mut() = LocationType::Fixed(location); 62 | 63 | for used_location in self.used_location.borrow().iter() { 64 | opcodes[*used_location] = location as u8; 65 | opcodes[*used_location + 1] = (location >> 8) as u8; 66 | } 67 | } 68 | 69 | pub fn subtraction(&self, left_hand: Rc, right_hand: Rc) { 70 | #[cfg(debug_assertions)] 71 | assert!(left_hand.get_index() != right_hand.get_index()); 72 | 73 | *self.location.borrow_mut() = LocationType::Subtraction { 74 | left_hand, 75 | right_hand 76 | }; 77 | } 78 | 79 | pub fn apply(&self, opcodes: &mut Vec) { 80 | // Save position 81 | self.used_location.borrow_mut().push(opcodes.len()); 82 | opcodes.push(self.get() as u8); 83 | opcodes.push((self.get() >> 8) as u8); 84 | } 85 | } 86 | 87 | #[derive(Clone)] 88 | pub struct CurrentLocationUpdateGenerator { pub location: Rc } 89 | impl OpcodeGeneratorTrait for CurrentLocationUpdateGenerator { 90 | fn generate(&self, opcodes: &mut Vec) { 91 | self.location.set(opcodes.len(), opcodes); 92 | } 93 | 94 | fn dump<'a>(&self, _: &'a DumpBuilder, _: Rc, _: &Vec) { 95 | // Dynamically location calculator 96 | } 97 | } 98 | 99 | #[derive(Clone)] 100 | pub struct DynamicLocationUpdateGenerator { 101 | pub target: Rc, 102 | pub source: Rc 103 | } 104 | 105 | impl OpcodeGeneratorTrait for DynamicLocationUpdateGenerator { 106 | fn generate(&self, opcodes: &mut Vec) { 107 | self.target.set(self.source.get(), opcodes); 108 | } 109 | 110 | fn dump<'a>(&self, _: &'a DumpBuilder, _: Rc, _: &Vec) { 111 | // Dynamically location calculator 112 | } 113 | } 114 | 115 | #[derive(Clone)] 116 | pub struct SubtractionGenerator { 117 | pub target: Rc, 118 | pub left_hand: Rc, 119 | pub right_hand: Rc 120 | } 121 | 122 | impl OpcodeGeneratorTrait for SubtractionGenerator { 123 | fn generate(&self, opcodes: &mut Vec) { 124 | let left = self.left_hand.get(); 125 | let right = self.right_hand.get(); 126 | 127 | self.target.set(left - right, opcodes); 128 | } 129 | 130 | fn dump<'a>(&self, _: &'a DumpBuilder, _: Rc, _: &Vec) { 131 | // Dynamically location calculator 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/location_group.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use super::OpcodeLocation; 4 | 5 | 6 | 7 | #[derive(Clone)] 8 | pub struct OpcodeLocationGroup { pub locations: RefCell>> } 9 | impl OpcodeLocationGroup { 10 | pub fn new() -> Self { 11 | OpcodeLocationGroup { 12 | locations: RefCell::new(Vec::new()) 13 | } 14 | } 15 | 16 | pub fn add(&self, location: Rc) { 17 | self.locations.borrow_mut().push(location.clone()); 18 | } 19 | 20 | pub fn clear(&self) { 21 | #[cfg(debug_assertions)] 22 | { 23 | for location in self.locations.borrow().iter() { 24 | assert_ne!(location.get(), 0); 25 | } 26 | } 27 | self.locations.borrow_mut().clear(); 28 | } 29 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/opcode_item.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::{compiler::VmOpCode}; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 6 | 7 | 8 | #[derive(Debug)] 9 | #[derive(Clone)] 10 | #[derive(PartialEq)] 11 | pub struct OpcodeItem { 12 | pub opcode: VmOpCode 13 | } 14 | 15 | impl OpcodeGeneratorTrait for OpcodeItem { 16 | fn generate(&self, opcodes: &mut Vec) { 17 | opcodes.push(self.opcode.into()); 18 | } 19 | 20 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, _: &Vec) { 21 | let opcode_index = index.fetch_add(1, Ordering::SeqCst); 22 | builder.add(opcode_index, self.opcode, "".to_string(), "".to_string(), "".to_string()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /karamellib/src/compiler/generator/store.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::atomic::{AtomicUsize, Ordering}}; 2 | 3 | use crate::compiler::VmOpCode; 4 | 5 | use super::{DumpBuilder, OpcodeGeneratorTrait}; 6 | 7 | #[derive(Debug)] 8 | #[derive(Clone)] 9 | pub enum StoreType { 10 | Store(u8), 11 | FastStore { 12 | destination: u8, 13 | source: u8 14 | }, 15 | CopyToStore(u8) 16 | } 17 | 18 | #[derive(Debug)] 19 | #[derive(Clone)] 20 | pub struct StoreGenerator { 21 | pub store_type: StoreType 22 | } 23 | 24 | impl OpcodeGeneratorTrait for StoreGenerator { 25 | fn generate(&self, opcodes: &mut Vec) { 26 | match self.store_type { 27 | StoreType::Store(destination) => { 28 | opcodes.push(VmOpCode::Store.into()); 29 | opcodes.push(destination); 30 | }, 31 | StoreType::CopyToStore(destination) => { 32 | opcodes.push(VmOpCode::CopyToStore.into()); 33 | opcodes.push(destination); 34 | }, 35 | StoreType::FastStore { destination, source} => { 36 | opcodes.push(VmOpCode::FastStore.into()); 37 | opcodes.push(destination); 38 | opcodes.push(source); 39 | } 40 | }; 41 | } 42 | 43 | fn dump<'a>(&self, builder: &'a DumpBuilder, index: Rc, _: &Vec) { 44 | let opcode_index = index.fetch_add(2, Ordering::SeqCst); 45 | 46 | match self.store_type { 47 | StoreType::Store(destination) => { 48 | builder.add(opcode_index, VmOpCode::Store, destination.to_string(), "".to_string(), "".to_string()); 49 | }, 50 | StoreType::CopyToStore(destination) => { 51 | builder.add(opcode_index, VmOpCode::CopyToStore, destination.to_string(), "".to_string(), "".to_string()); 52 | }, 53 | StoreType::FastStore { destination, source} => { 54 | builder.add(opcode_index, VmOpCode::FastStore, destination.to_string(), source.to_string(), "".to_string()); 55 | index.fetch_add(1, Ordering::SeqCst); 56 | } 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /karamellib/src/compiler/mod.rs: -------------------------------------------------------------------------------- 1 | mod compiler; 2 | mod static_storage; 3 | mod storage_builder; 4 | pub mod function; 5 | 6 | pub mod value; 7 | pub mod ast; 8 | pub mod module; 9 | pub mod scope; 10 | pub mod context; 11 | pub mod generator; 12 | 13 | pub use self::compiler::*; 14 | pub use self::static_storage::*; 15 | pub use self::value::*; 16 | pub use self::context::KaramelCompilerContext; 17 | 18 | use std::vec::Vec; 19 | use std::mem; 20 | use std::fmt; 21 | 22 | pub trait GetType { 23 | fn get_type(&self) -> String; 24 | } 25 | 26 | pub struct VmByte(pub u8); 27 | impl fmt::Debug for VmByte { 28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 29 | write!(f, "{:?}", self.decode_opcode()) 30 | } 31 | } 32 | 33 | impl VmByte { 34 | pub fn new_opcode(opcode: VmOpCode) -> VmByte { 35 | VmByte(opcode as u8) 36 | } 37 | 38 | #[allow(dead_code)] 39 | pub fn decode_opcode(&self) -> VmOpCode { 40 | let VmByte(bits) = *self; 41 | unsafe { mem::transmute::<_, VmOpCode>((bits & 0xff) as u8) } 42 | } 43 | } 44 | 45 | trait VmByteDecode { 46 | fn encode(&self) -> VmByte; 47 | } 48 | 49 | impl VmByteDecode for VmOpCode { 50 | fn encode(&self) -> VmByte { 51 | VmByte::new_opcode(*self) 52 | } 53 | } 54 | 55 | impl VmByteDecode for u8 { 56 | fn encode(&self) -> VmByte { 57 | VmByte(*self) 58 | } 59 | } 60 | 61 | 62 | #[derive(Clone, Copy, Debug, PartialEq)] 63 | #[repr(u8)] 64 | pub enum VmOpCode { 65 | Addition = 1, 66 | Subraction = 2, 67 | Multiply = 3, 68 | Division = 4, 69 | Module = 5, 70 | And = 6, 71 | Or = 7, 72 | Equal = 8, 73 | NotEqual = 9, 74 | GreaterThan = 10, 75 | GreaterEqualThan = 12, 76 | 77 | Call = 16, 78 | CallStack = 17, 79 | Return = 18, 80 | 81 | Increment = 19, 82 | Decrement = 20, 83 | Not = 21, 84 | 85 | /// Compare previous two opcode. 86 | /// If true, jump over 2 opcode and continue to execution. 87 | /// If false, read next 2 opcode than calculate false jump location via high and low byte. 88 | Compare = 22, 89 | Jump = 23, 90 | 91 | Init = 24, 92 | 93 | /// Copy value from memory to stack. 94 | Load = 26, 95 | 96 | /// Copy stack value to memory and remove value from stack. 97 | Store = 27, 98 | 99 | /// Dublicate value at memory. Take value from memory and copy to destination location. Stack not involved at this operation. 100 | FastStore = 28, 101 | 102 | /// Copy last stack value to memory and keep copied value at stack. 103 | CopyToStore = 29, 104 | Dublicate = 30, 105 | GetItem = 31, 106 | SetItem = 32, 107 | Constant = 33, 108 | Halt = 34 109 | } 110 | 111 | impl From for u8 { 112 | fn from(opcode: VmOpCode) -> Self { 113 | opcode as u8 114 | } 115 | } 116 | 117 | impl From<&VmOpCode> for u8 { 118 | fn from(opcode: &VmOpCode) -> Self { 119 | *opcode as u8 120 | } 121 | } 122 | 123 | 124 | impl fmt::Display for VmOpCode { 125 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 126 | write!(f, "{:?}", self) 127 | } 128 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/scope.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use crate::types::VmObject; 4 | 5 | 6 | #[derive(Clone)] 7 | pub struct Scope { 8 | pub location: *mut u8, 9 | pub call_return_assign_to_temp: bool, 10 | pub top_stack: *mut VmObject, 11 | pub constant_ptr: *const VmObject 12 | } 13 | 14 | impl Scope { 15 | pub fn empty() -> Scope { 16 | Scope { 17 | call_return_assign_to_temp: false, 18 | location: ptr::null_mut(), 19 | top_stack: ptr::null_mut(), 20 | constant_ptr: ptr::null() 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /karamellib/src/compiler/static_storage.rs: -------------------------------------------------------------------------------- 1 | use crate::buildin::Module; 2 | use crate::types::*; 3 | use crate::compiler::*; 4 | use std::rc::Rc; 5 | 6 | #[cfg(not(feature = "unittest"))] 7 | use crate::{debug_println}; 8 | 9 | use std::ptr; 10 | 11 | pub struct StaticStorage { 12 | pub index : usize, 13 | pub constants : Vec, 14 | pub constants_ptr : *const VmObject, 15 | pub variables : Vec, 16 | pub parent_location : Option 17 | } 18 | 19 | impl StaticStorage { 20 | pub fn new(index: usize) -> Self { 21 | let mut storage = StaticStorage { 22 | index: index, 23 | constants: Vec::with_capacity(128), 24 | constants_ptr: ptr::null(), 25 | variables: Vec::new(), 26 | parent_location: None 27 | }; 28 | storage.constants_ptr = storage.constants.as_ptr(); 29 | storage 30 | } 31 | pub fn get_variable_size(&self) -> u8 { self.variables.len() as u8 } 32 | 33 | pub fn set_parent_location(&mut self, parent_location: usize) { 34 | self.parent_location = Some(parent_location); 35 | } 36 | pub fn get_parent_location(&self) -> Option { 37 | self.parent_location 38 | } 39 | pub fn add_constant(&mut self, value: Rc) -> usize { 40 | let constant_position = self.constants.iter().position(|x| { 41 | *x.deref() == *value 42 | }); 43 | 44 | match constant_position { 45 | Some(position) => position, 46 | None => { 47 | self.constants.push(VmObject::convert(value)); 48 | self.constants.len() -1 49 | } 50 | } 51 | } 52 | 53 | pub fn add_variable(&mut self, name: &str) -> u8 { 54 | let result = self.variables.iter().position(|key| key == name); 55 | match result { 56 | Some(location) => location as u8, 57 | _ => { 58 | self.variables.push(name.to_string()); 59 | (self.variables.len()-1) as u8 60 | } 61 | } 62 | } 63 | 64 | pub fn get_variable_location(&self, name: &str) -> Option { 65 | let result = self.variables.iter().position(|key| key == name); 66 | match result { 67 | Some(location) => Some(location as u8), 68 | _ => None 69 | } 70 | } 71 | 72 | pub fn get_constant_location(&self, value: Rc) -> Option { 73 | return match self.constants.iter().position(|x| { *x.deref() == *value }) { 74 | Some(number) => Some(number as u8), 75 | _ => None 76 | }; 77 | } 78 | 79 | pub fn get_function_constant(&self, name: String, module: Rc) -> Option { 80 | 81 | for (index, item) in self.constants.iter().enumerate() { 82 | if let KaramelPrimative::Function(reference, _) = &*item.deref() { 83 | if reference.name == name && 84 | reference.module.get_path() == module.get_path() { 85 | return Some(index as u8); 86 | } 87 | } 88 | } 89 | 90 | None 91 | } 92 | 93 | pub fn get_class_constant(&self, name: String, _module_path: Rc) -> Option { 94 | 95 | for (index, item) in self.constants.iter().enumerate() { 96 | if let KaramelPrimative::Class(reference) = &*item.deref() { 97 | if reference.get_class_name() == name { 98 | return Some(index as u8); 99 | } 100 | } 101 | } 102 | 103 | None 104 | } 105 | 106 | #[cfg(feature = "unittest")] 107 | pub fn dump(&self) {} 108 | 109 | #[cfg(not(feature = "unittest"))] 110 | pub fn dump(&self) -> String { 111 | let mut buffer = String::new(); 112 | buffer.push_str("╔════════════════════════════════════════╗\n"); 113 | buffer.push_str("║ VARIABLE DUMP ║\n"); 114 | buffer.push_str("╠═════╦══════════════════════════════════╣\n"); 115 | for (index, variable) in self.variables.iter().enumerate() { 116 | buffer.push_str(&format!("║ {:3?} ║ {:32} ║\n", index, format!("{}", variable))[..]); 117 | } 118 | buffer.push_str("╚═════╩══════════════════════════════════╝\n"); 119 | buffer.push_str("╔════════════════════════════════════════╗\n"); 120 | buffer.push_str("║ CONSTANT DUMP ║\n"); 121 | buffer.push_str("╠═════╦══════════════════════════════════╣\n"); 122 | for (index, constant) in self.constants.iter().enumerate() { 123 | buffer.push_str(&format!("║ {:3?} ║ {:32} ║\n", index, format!("{}", constant))[..]); 124 | } 125 | buffer.push_str("╚═════╩══════════════════════════════════╝\n"); 126 | buffer 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /karamellib/src/constants/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | pub static KARAMEL_FILE_EXTENSION: &'static str = ".k"; 3 | pub static KARAMEL_VERSION: &'static str = "0.1"; 4 | pub static KARAMEL_CONTACT_EMAIL: &'static str = "erhanbaris@gmail.com"; 5 | pub static KARAMEL_TITLE: &'static str = "Karamel Programlama Dili"; 6 | pub static KARAMEL_HELP_ABOUT: &'static str = r#"Karamel Programlama Dili Derleyicisi. 7 | 8 | https://github.com/erhanbaris/karamel"#; 9 | 10 | pub static STARTUP_MODULE_NAME: &'static str = "baz.k"; 11 | 12 | 13 | pub static DUMP_OPCODE_TITLE: &'static str = "OPCODE DUMP"; 14 | pub static DUMP_INDEX_WIDTH: usize = 5; 15 | pub static DUMP_OPCODE_WIDTH: usize = 15; 16 | pub static DUMP_OPCODE_COLUMN_1: usize = 5; 17 | pub static DUMP_OPCODE_COLUMN_2: usize = 5; 18 | pub static DUMP_OPCODE_COLUMN_3: usize = 5; -------------------------------------------------------------------------------- /karamellib/src/file/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Borrow, fs::File}; 2 | use std::io::prelude::*; 3 | use std::path::Path; 4 | use std::fs::canonicalize; 5 | 6 | use crate::compiler::KaramelCompilerContext; 7 | use crate::constants::{KARAMEL_FILE_EXTENSION, STARTUP_MODULE_NAME}; 8 | use crate::error::KaramelErrorType; 9 | 10 | pub fn read_file>(file_name: T) -> Result { 11 | match File::open(file_name.borrow()) { 12 | Ok(mut file) => { 13 | let mut contents = String::new(); 14 | file.read_to_string(&mut contents).unwrap(); 15 | Ok(contents) 16 | }, 17 | Err(error) => return Err(KaramelErrorType::FileReadError { 18 | filename: file_name.borrow().to_owned(), 19 | error: error.to_string() 20 | }) 21 | } 22 | } 23 | 24 | fn read_script>(file_name: T, context: &KaramelCompilerContext) -> Result { 25 | let path = Path::new(file_name.borrow()); 26 | 27 | if path.exists() && path.is_file() { 28 | return read_file(file_name); 29 | } 30 | 31 | let script_path = Path::new(&context.execution_path.path); 32 | let calculated_path = script_path.join(Path::new(file_name.borrow())); 33 | 34 | match canonicalize(&calculated_path) { 35 | Ok(path) => match path.exists() && path.is_file() { 36 | true => return read_file(path.to_str().unwrap()), 37 | false => match calculated_path.to_str() { 38 | Some(filename) => Err(KaramelErrorType::FileNotFound(filename.to_string())), 39 | None => Err(KaramelErrorType::GeneralError("Dosya bulunamadi.".to_string())) 40 | }, 41 | }, 42 | Err(error) => Err(KaramelErrorType::GeneralError(format!("Dosya yolu okunurken hata ile karsilasildi. Hata bilgisi: {}", error))) 43 | } 44 | } 45 | 46 | pub fn read_module_or_script>(file_name: T, context: &KaramelCompilerContext) -> Result { 47 | let computed_file_name = match file_name.borrow().ends_with(KARAMEL_FILE_EXTENSION) { 48 | true => file_name.borrow().to_string(), 49 | false => format!("{}{}", file_name.borrow(), KARAMEL_FILE_EXTENSION) 50 | }; 51 | 52 | match read_script(computed_file_name, context) { 53 | Ok(content) => return Ok(content), 54 | Err(_) => () 55 | }; 56 | 57 | let script_path = Path::new(&context.execution_path.path); 58 | let calculated_path = script_path.join(Path::new(file_name.borrow())); 59 | 60 | match canonicalize(&calculated_path) { 61 | Ok(path) => match path.exists() && path.is_file() { 62 | true => return read_file(path.to_str().unwrap()), 63 | false => (), 64 | }, 65 | Err(_) => () 66 | }; 67 | 68 | match canonicalize(calculated_path.join(STARTUP_MODULE_NAME)) { 69 | Ok(path) => return read_file(path.to_str().unwrap()), 70 | Err(error) => Err(KaramelErrorType::GeneralError(format!("Dosya yolu okunurken hata ile karsilasildi. Hata bilgisi: {}", error))) 71 | } 72 | } -------------------------------------------------------------------------------- /karamellib/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate unicode_width; 2 | 3 | #[macro_use] 4 | pub mod macros; 5 | pub mod parser; 6 | pub mod syntax; 7 | pub mod types; 8 | pub mod vm; 9 | pub mod compiler; 10 | pub mod buildin; 11 | pub mod logger; 12 | pub mod error; 13 | pub mod file; 14 | pub mod constants; 15 | -------------------------------------------------------------------------------- /karamellib/src/logger/mod.rs: -------------------------------------------------------------------------------- 1 | use log::*; 2 | 3 | use crate::compiler::KaramelCompilerContext; 4 | 5 | pub struct ConsoleLogger; 6 | pub struct DummyLogger; 7 | 8 | pub static CONSOLE_LOGGER: ConsoleLogger = ConsoleLogger; 9 | pub static DUMMY_LOGGER: DummyLogger = DummyLogger; 10 | 11 | 12 | impl Log for DummyLogger { 13 | fn enabled(&self, metadata: &Metadata) -> bool { 14 | metadata.level() <= Level::Debug 15 | } 16 | fn log(&self, _: &Record) {} 17 | fn flush(&self) {} 18 | } 19 | 20 | impl Log for ConsoleLogger { 21 | fn enabled(&self, metadata: &Metadata) -> bool { 22 | metadata.level() <= Level::Debug 23 | } 24 | 25 | fn log(&self, record: &Record) { 26 | if self.enabled(record.metadata()) { 27 | #[cfg(all(not(target_arch = "wasm32"), not(test)))] 28 | println!("[{}] {}", record.level(), record.args()); 29 | } 30 | } 31 | 32 | fn flush(&self) {} 33 | } 34 | 35 | pub fn write_stdout(context: &KaramelCompilerContext, data: String) { 36 | match &context.stdout { 37 | Some(out) => match out.try_borrow_mut() { 38 | Ok(mut out_mut) => { out_mut.push_str(&data[..]) }, 39 | _ => () 40 | }, 41 | _ => () 42 | }; 43 | } 44 | 45 | 46 | pub fn write_stderr(context: &KaramelCompilerContext, data: String) { 47 | match &context.stderr { 48 | Some(out) => match out.try_borrow_mut() { 49 | Ok(mut out_mut) => { out_mut.push_str(&data[..]) }, 50 | _ => () 51 | }, 52 | _ => () 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /karamellib/src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! current_memory_index { 3 | ($context: expr) => {{ 4 | $context.stack_ptr.offset_from($context.stack.as_ptr()) 5 | }} 6 | } 7 | 8 | #[macro_export] 9 | #[cfg(any(feature = "dbg", feature = "dbg_level3"))] 10 | macro_rules! dump_data { 11 | ($context: expr, $message: expr) => {{ 12 | let location = current_memory_index!($context); 13 | println!("stack[{}] = {} ({:?})", location, *$context.stack_ptr, $message); 14 | }} 15 | } 16 | 17 | #[macro_export] 18 | #[cfg(any(feature = "dbg", feature = "dbg_level1", feature = "dbg_level2", feature = "dbg_level3"))] 19 | macro_rules! karamel_dbg_any { 20 | ($x:expr) => { dbg!($x) } 21 | } 22 | 23 | #[macro_export] 24 | #[cfg(all(not(feature = "dbg"), not(feature = "dbg_level1"), not(feature = "dbg_level2"), not(feature = "dbg_level3")))] 25 | macro_rules! karamel_dbg_any { 26 | ($x:expr) => { std::convert::identity($x) } 27 | } 28 | 29 | #[macro_export] 30 | #[cfg(all(not(feature = "dbg"), not(feature = "dbg_level3")))] 31 | macro_rules! dump_data { 32 | ($context: expr, $message: expr) => { } 33 | } 34 | 35 | #[macro_export] 36 | macro_rules! pop { 37 | ($context: expr, $message: expr) => {{ 38 | pop_raw!($context, $message).deref() 39 | }} 40 | } 41 | 42 | #[macro_export] 43 | macro_rules! pop_raw { 44 | ($context: expr, $message: expr) => {{ 45 | $context.stack_ptr = $context.stack_ptr.sub(1); 46 | dump_data!($context, $message); 47 | *$context.stack_ptr 48 | }} 49 | } 50 | 51 | #[macro_export] 52 | macro_rules! fetch_raw { 53 | ($context: expr) => {{ 54 | karamel_dbg!(*$context.stack_ptr.sub(1)) 55 | }} 56 | } 57 | 58 | #[macro_export] 59 | macro_rules! current_raw { 60 | ($context: expr) => {{ 61 | karamel_dbg!(*$context.stack_ptr) 62 | }} 63 | } 64 | 65 | #[macro_export] 66 | macro_rules! get_memory_index { 67 | ($context: expr) => {{ 68 | karamel_dbg!($context.stack_ptr.offset_from($context.stack.as_ptr())) 69 | }} 70 | } 71 | 72 | #[macro_export] 73 | macro_rules! inc_memory_index { 74 | ($context: expr, $count: expr) => {{ 75 | $context.stack_ptr = karamel_dbg!($context.stack_ptr.add($count)); 76 | }} 77 | } 78 | 79 | #[macro_export] 80 | macro_rules! dec_memory_index { 81 | ($context: expr, $count: expr) => {{ 82 | $context.stack_ptr = $context.stack_ptr.sub($count); 83 | }} 84 | } 85 | 86 | // The debug version 87 | #[allow(dead_code)] 88 | #[macro_export] 89 | #[cfg(not(feature = "unittest"))] 90 | macro_rules! debug_println { 91 | ($( $args:expr ),*) => { log::info!( $( $args ),* ); } 92 | } 93 | 94 | // Non-debug version 95 | #[allow(dead_code)] 96 | #[macro_export] 97 | #[cfg(feature = "unittest")] 98 | macro_rules! debug_println { 99 | ($( $args:expr ),*) => {} 100 | } 101 | 102 | /* 103 | DEBUG MACROS 104 | */ 105 | 106 | #[macro_export] 107 | #[cfg(any(feature = "dbg"))] 108 | macro_rules! karamel_dbg { 109 | ($x:expr) => { dbg!($x) } 110 | } 111 | 112 | #[macro_export] 113 | #[cfg(not(feature = "dbg"))] 114 | macro_rules! karamel_dbg { 115 | ($x:expr) => { std::convert::identity($x) } 116 | } 117 | 118 | #[macro_export] 119 | #[cfg(feature = "dbg_level1")] 120 | macro_rules! karamel_dbg_level1 { 121 | ($x:expr) => { dbg!($x) } 122 | } 123 | 124 | #[macro_export] 125 | #[cfg(not(feature = "dbg_level1"))] 126 | macro_rules! karamel_dbg_level1 { 127 | ($x:expr) => { std::convert::identity($x) } 128 | } 129 | 130 | #[macro_export] 131 | #[cfg(feature = "dbg_level2")] 132 | macro_rules! karamel_dbg_level2 { 133 | ($x:expr) => { dbg!($x) } 134 | } 135 | 136 | #[macro_export] 137 | #[cfg(not(feature = "dbg_level2"))] 138 | macro_rules! karamel_dbg_level2 { 139 | ($x:expr) => { std::convert::identity($x) } 140 | } 141 | 142 | 143 | #[macro_export] 144 | #[cfg(feature = "dbg_level3")] 145 | macro_rules! karamel_dbg_level3 { 146 | ($x:expr) => { dbg!($x) } 147 | } 148 | 149 | #[macro_export] 150 | #[cfg(not(feature = "dbg_level3"))] 151 | macro_rules! karamel_dbg_level3 { 152 | ($x:expr) => { std::convert::identity($x) } 153 | } 154 | 155 | #[macro_export] 156 | #[cfg(any(feature = "dbg", feature = "dbg_level1"))] 157 | macro_rules! karamel_print_level1 { 158 | ($($arg:tt)*) => (println!("DEBUG1: {}", std::format_args!($($arg)*))); 159 | } 160 | 161 | #[macro_export] 162 | #[cfg(all(not(feature = "dbg"), not(feature = "dbg_level1")))] 163 | macro_rules! karamel_print_level1 { 164 | ($($arg:tt)*) => { } 165 | } 166 | 167 | #[macro_export] 168 | #[cfg(any(feature = "dbg", feature = "dbg_level2"))] 169 | macro_rules! karamel_print_level2 { 170 | ($($arg:tt)*) => (println!("DEBUG2: {}", std::format_args!($($arg)*))); 171 | } 172 | 173 | #[macro_export] 174 | #[cfg(all(not(feature = "dbg"), not(feature = "dbg_level2")))] 175 | macro_rules! karamel_print_level2 { 176 | ($($arg:tt)*) => { } 177 | } 178 | 179 | 180 | #[macro_export] 181 | #[cfg(any(feature = "dbg", feature = "dbg_level3"))] 182 | macro_rules! karamel_print_level3 { 183 | ($($arg:tt)*) => (println!("DEBUG3: {}", std::format_args!($($arg)*))); 184 | } 185 | 186 | #[macro_export] 187 | #[cfg(all(not(feature = "dbg"), not(feature = "dbg_level3")))] 188 | macro_rules! karamel_print_level3 { 189 | ($($arg:tt)*) => { } 190 | } 191 | -------------------------------------------------------------------------------- /karamellib/src/parser/comment.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::error::KaramelErrorType; 3 | 4 | pub struct CommentParser; 5 | 6 | impl TokenParser for CommentParser { 7 | fn check(&self, tokinizer: &mut Tokinizer) -> bool { 8 | let ch = tokinizer.get_char(); 9 | let ch_next = tokinizer.get_next_char(); 10 | return (ch == '/' && ch_next == '*') || (ch == '/' && ch_next == '/'); 11 | } 12 | 13 | fn parse(&self, tokinizer: &mut Tokinizer) -> Result<(), KaramelErrorType> { 14 | let mut ch = tokinizer.get_char(); 15 | let mut ch_next = tokinizer.get_next_char(); 16 | 17 | if ch == '/' && ch_next == '*' { 18 | let mut comment_end = false; 19 | 20 | while !tokinizer.is_end() && !comment_end { 21 | tokinizer.increase_index(); 22 | 23 | if ch.is_new_line() { 24 | tokinizer.increate_line(); 25 | } 26 | 27 | ch = tokinizer.get_char(); 28 | ch_next = tokinizer.get_next_char(); 29 | 30 | if ch == '*' && ch_next == '/' { 31 | comment_end = true; 32 | tokinizer.increase_index(); 33 | tokinizer.increase_index(); 34 | } 35 | } 36 | 37 | if !comment_end { 38 | return Err(KaramelErrorType::CommentNotFinished); 39 | } 40 | } 41 | else { 42 | tokinizer.increase_index(); 43 | tokinizer.increase_index(); 44 | ch = tokinizer.get_char(); 45 | 46 | while !tokinizer.is_end() && ch != '\n' { 47 | tokinizer.increase_index(); 48 | 49 | if ch.is_new_line() { 50 | tokinizer.increate_line(); 51 | } 52 | 53 | ch = tokinizer.get_char(); 54 | } 55 | } 56 | 57 | return Ok(()); 58 | } 59 | } -------------------------------------------------------------------------------- /karamellib/src/parser/line.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::error::KaramelErrorType; 3 | 4 | pub struct LineParser; 5 | 6 | impl TokenParser for LineParser { 7 | fn check(&self, tokinizer: &mut Tokinizer) -> bool { 8 | let ch = tokinizer.get_char(); 9 | return ch.is_new_line(); 10 | } 11 | 12 | fn parse(&self, tokinizer: &mut Tokinizer) -> Result<(), KaramelErrorType> { 13 | tokinizer.increase_index(); 14 | 15 | let mut whitespace_count: u32 = 0; 16 | let start_column = tokinizer.column; 17 | let mut ch = tokinizer.get_char(); 18 | 19 | while !tokinizer.is_end() && ch == ' ' { 20 | tokinizer.increase_index(); 21 | whitespace_count += 1; 22 | ch = tokinizer.get_char(); 23 | } 24 | 25 | tokinizer.increate_line(); 26 | tokinizer.add_token(start_column, KaramelTokenType::NewLine(whitespace_count as u8)); 27 | tokinizer.column = whitespace_count; 28 | 29 | return Ok(()); 30 | } 31 | } -------------------------------------------------------------------------------- /karamellib/src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | mod number; 2 | mod text; 3 | mod operator; 4 | mod symbol; 5 | mod line; 6 | mod whitespace; 7 | mod comment; 8 | 9 | use std::str; 10 | use std::collections::HashMap; 11 | 12 | use crate::{error::KaramelError, types::*}; 13 | use self::number::NumberParser; 14 | use self::text::TextParser; 15 | use self::operator::OperatorParser; 16 | use self::symbol::SymbolParser; 17 | use self::line::LineParser; 18 | use self::whitespace::WhitespaceParser; 19 | use self::comment::CommentParser; 20 | use crate::error::KaramelErrorType; 21 | 22 | pub struct Parser<'a> { 23 | tokinizer: Tokinizer<'a> 24 | } 25 | 26 | impl<'a> Parser<'a> { 27 | pub fn new(data: &'a str) -> Parser { 28 | let mut parser = Parser { 29 | tokinizer: Tokinizer { 30 | column: 0, 31 | line: 0, 32 | tokens: Vec::new(), 33 | iter: data.chars().peekable(), 34 | iter_second: data.chars().peekable(), 35 | iter_third: data.chars().peekable(), 36 | data: data.to_string(), 37 | index: 0 38 | } 39 | }; 40 | 41 | parser.tokinizer.iter_second.next(); 42 | parser.tokinizer.iter_third.next(); 43 | parser.tokinizer.iter_third.next(); 44 | return parser; 45 | } 46 | 47 | pub fn tokens(&self) -> Vec { 48 | self.tokinizer.tokens.to_vec() 49 | } 50 | 51 | pub fn parse(&mut self) -> ParseResult { 52 | 53 | let line_parser = LineParser {}; 54 | let comment_parser = CommentParser {}; 55 | let whitespace_parser = WhitespaceParser {}; 56 | let number_parser = NumberParser {}; 57 | let text_parser_single = TextParser { tag:'\'' }; 58 | let text_parser_double = TextParser { tag:'"' }; 59 | let operator_parser = OperatorParser {}; 60 | let mut symbol_parser = SymbolParser { 61 | keywords: HashMap::new() 62 | }; 63 | 64 | symbol_parser.init_parser(); 65 | 66 | while self.tokinizer.is_end() == false { 67 | let status: Result<(), KaramelErrorType>; 68 | 69 | if line_parser.check(&mut self.tokinizer) { 70 | status = line_parser.parse(&mut self.tokinizer); 71 | } 72 | else if whitespace_parser.check(&mut self.tokinizer) { 73 | status = whitespace_parser.parse(&mut self.tokinizer); 74 | } 75 | else if comment_parser.check(&mut self.tokinizer) { 76 | status = comment_parser.parse(&mut self.tokinizer); 77 | } 78 | else if symbol_parser.check(&mut self.tokinizer) { 79 | status = symbol_parser.parse(&mut self.tokinizer); 80 | } 81 | else if text_parser_single.check(&mut self.tokinizer) { 82 | status = text_parser_single.parse(&mut self.tokinizer); 83 | } 84 | else if text_parser_double.check(&mut self.tokinizer) { 85 | status = text_parser_double.parse(&mut self.tokinizer); 86 | } 87 | else if number_parser.check(&mut self.tokinizer) { 88 | status = number_parser.parse(&mut self.tokinizer); 89 | } 90 | else { 91 | status = operator_parser.parse(&mut self.tokinizer); 92 | } 93 | 94 | if status.is_err() { 95 | return Err(KaramelError { 96 | error_type: status.err().unwrap(), 97 | line: self.tokinizer.line, 98 | column: self.tokinizer.column 99 | }); 100 | } 101 | } 102 | 103 | Ok(()) 104 | } 105 | } -------------------------------------------------------------------------------- /karamellib/src/parser/operator.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::error::KaramelErrorType; 3 | 4 | pub struct OperatorParser; 5 | 6 | impl TokenParser for OperatorParser { 7 | fn check(&self, _tokinizer: &mut Tokinizer) -> bool { 8 | true 9 | } 10 | 11 | fn parse(&self, tokinizer: &mut Tokinizer) -> Result<(), KaramelErrorType> { 12 | let ch = tokinizer.get_char(); 13 | let ch_next = tokinizer.get_next_char(); 14 | let start= tokinizer.column; 15 | 16 | tokinizer.increase_index(); 17 | 18 | let mut operator_type = match (ch, ch_next) { 19 | ('!', '=') => KaramelOperatorType::NotEqual, 20 | ('/', '=') => KaramelOperatorType::AssignDivision, 21 | ('/', '/') => KaramelOperatorType::CommentLine, 22 | ('/', '*') => KaramelOperatorType::CommentMultilineStart, 23 | ('+', '+') => KaramelOperatorType::Increment, 24 | ('+', '=') => KaramelOperatorType::AssignAddition, 25 | ('-', '-') => KaramelOperatorType::Deccrement, 26 | ('-', '=') => KaramelOperatorType::AssignSubtraction, 27 | ('<', '=') => KaramelOperatorType::LessEqualThan, 28 | ('>', '=') => KaramelOperatorType::GreaterEqualThan, 29 | ('*', '=') => KaramelOperatorType::AssignMultiplication, 30 | ('*', '/') => KaramelOperatorType::CommentMultilineEnd, 31 | ('=', '=') => KaramelOperatorType::Equal, 32 | _ => KaramelOperatorType::None 33 | }; 34 | 35 | if operator_type != KaramelOperatorType::None { 36 | tokinizer.increase_index(); 37 | } 38 | else { 39 | operator_type = match ch { 40 | '=' => KaramelOperatorType::Assign, 41 | '*' => KaramelOperatorType::Multiplication, 42 | '<' => KaramelOperatorType::LessThan, 43 | '>' => KaramelOperatorType::GreaterThan, 44 | '-' => KaramelOperatorType::Subtraction, 45 | '+' => KaramelOperatorType::Addition, 46 | '/' => KaramelOperatorType::Division, 47 | '?' => KaramelOperatorType::QuestionMark, 48 | ':' => KaramelOperatorType::ColonMark, 49 | '(' => KaramelOperatorType::LeftParentheses, 50 | ')' => KaramelOperatorType::RightParentheses, 51 | '[' => KaramelOperatorType::SquareBracketStart, 52 | ']' => KaramelOperatorType::SquareBracketEnd, 53 | '{' => KaramelOperatorType::CurveBracketStart, 54 | '}' => KaramelOperatorType::CurveBracketEnd, 55 | ',' => KaramelOperatorType::Comma, 56 | ';' => KaramelOperatorType::Semicolon, 57 | '.' => KaramelOperatorType::Dot, 58 | '!' => KaramelOperatorType::Not, 59 | _ => KaramelOperatorType::None 60 | }; 61 | } 62 | 63 | if ch == '\r' { 64 | return Ok(()); 65 | } 66 | 67 | if operator_type == KaramelOperatorType::None { 68 | log::debug!("'{}' not found", ch as usize); 69 | return Err(KaramelErrorType::CharNotValid); 70 | } 71 | 72 | tokinizer.add_token(start, KaramelTokenType::Operator(operator_type)); 73 | return Ok(()); 74 | } 75 | } -------------------------------------------------------------------------------- /karamellib/src/parser/symbol.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::rc::Rc; 3 | use crate::types::*; 4 | use crate::error::KaramelErrorType; 5 | 6 | pub struct SymbolParser { 7 | pub keywords: HashMap<&'static str, KaramelKeywordType> 8 | } 9 | 10 | impl SymbolParser { 11 | pub fn init_parser(&mut self) { 12 | for (keyword, keyword_enum) in KEYWORDS.iter() { 13 | self.keywords.insert(keyword, *keyword_enum); 14 | } 15 | } 16 | } 17 | 18 | impl TokenParser for SymbolParser { 19 | fn check(&self, tokinizer: &mut Tokinizer) -> bool { 20 | let ch = tokinizer.get_char(); 21 | return ch.is_symbol(); 22 | } 23 | 24 | fn parse(&self, tokinizer: &mut Tokinizer) -> Result<(), KaramelErrorType> { 25 | let mut ch: char; 26 | let start = tokinizer.index as usize; 27 | let mut end = start; 28 | let start_column = tokinizer.column; 29 | 30 | while !tokinizer.is_end() { 31 | ch = tokinizer.get_char(); 32 | 33 | if !ch.is_symbol() && !ch.is_integer() { 34 | break; 35 | } 36 | 37 | if ch.is_whitespace() || ch == '\'' || ch == '"' { 38 | break; 39 | } 40 | end += ch.len_utf8(); 41 | tokinizer.increase_index(); 42 | } 43 | if self.keywords.contains_key(&tokinizer.data[start..end]) { 44 | let keyword = match self.keywords.get(&tokinizer.data[start..end]) { 45 | Some(keyword) => keyword, 46 | None => &KaramelKeywordType::None 47 | }; 48 | 49 | let token_type = match keyword.to_operator() { 50 | KaramelOperatorType::None => KaramelTokenType::Keyword(*keyword), 51 | _ => KaramelTokenType::Operator(keyword.to_operator()) 52 | }; 53 | tokinizer.add_token(start_column as u32, token_type); 54 | return Ok(()); 55 | } 56 | 57 | tokinizer.add_token(start_column as u32, KaramelTokenType::Symbol(Rc::new(tokinizer.data[start..end].to_string()))); 58 | return Ok(()); 59 | } 60 | } -------------------------------------------------------------------------------- /karamellib/src/parser/text.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use crate::types::*; 3 | use crate::error::KaramelErrorType; 4 | 5 | pub struct TextParser { 6 | pub tag: char 7 | } 8 | 9 | impl TokenParser for TextParser { 10 | fn check(&self, tokinizer: &mut Tokinizer) -> bool { 11 | let ch = tokinizer.get_char(); 12 | return ch == self.tag; 13 | } 14 | 15 | fn parse(&self, tokinizer: &mut Tokinizer) -> Result<(), KaramelErrorType> { 16 | tokinizer.increase_index(); 17 | 18 | let mut ch: char = '\0'; 19 | let mut ch_next: char; 20 | let start = tokinizer.index as usize; 21 | let start_column = tokinizer.column; 22 | let mut end = start; 23 | 24 | while !tokinizer.is_end() { 25 | ch = tokinizer.get_char(); 26 | ch_next = tokinizer.get_next_char(); 27 | 28 | if ch == '\\' && ch_next == self.tag { 29 | end += ch.len_utf8(); 30 | end += 1; // for tag char 31 | tokinizer.increase_index(); 32 | } 33 | else if ch == self.tag { 34 | tokinizer.increase_index(); 35 | break; 36 | } 37 | else { 38 | end += ch.len_utf8(); 39 | } 40 | 41 | tokinizer.increase_index(); 42 | } 43 | 44 | if ch != self.tag { 45 | return Err(KaramelErrorType::MissingStringDeliminator); 46 | } 47 | 48 | tokinizer.add_token(start_column - 1, KaramelTokenType::Text(Rc::new(tokinizer.data[start..end].to_string()))); 49 | return Ok(()); 50 | } 51 | } 52 | 53 | 54 | #[cfg(test)] 55 | #[test] 56 | fn text_parse_test_1() { 57 | use crate::types::Tokinizer; 58 | 59 | let data = "\"merhaba dünya\""; 60 | let mut tokinizer = Tokinizer { 61 | column: 0, 62 | line: 0, 63 | tokens: Vec::new(), 64 | iter: data.chars().peekable(), 65 | iter_second: data.chars().peekable(), 66 | iter_third: data.chars().peekable(), 67 | data: data.to_string(), 68 | index: 0 69 | }; 70 | 71 | let parser = TextParser { tag: '"' }; 72 | let parse_result = parser.parse(&mut tokinizer); 73 | 74 | assert_eq!(parse_result.is_ok(), true); 75 | assert_eq!(tokinizer.tokens.len(), 1); 76 | assert_eq!(tokinizer.tokens[0].line, 0); 77 | assert_eq!(tokinizer.tokens[0].start, 0); 78 | assert_eq!(tokinizer.tokens[0].end, 15); 79 | 80 | match &tokinizer.tokens[0].token_type { 81 | KaramelTokenType::Text(data) => assert_eq!(&**data, "merhaba dünya"), 82 | _ => assert_eq!(true, false) 83 | }; 84 | } 85 | 86 | #[cfg(test)] 87 | #[test] 88 | fn text_parse_test_2() { 89 | use crate::types::Tokinizer; 90 | 91 | let data = "'merhaba dünya'"; 92 | let mut tokinizer = Tokinizer { 93 | column: 0, 94 | line: 0, 95 | tokens: Vec::new(), 96 | iter: data.chars().peekable(), 97 | iter_second: data.chars().peekable(), 98 | iter_third: data.chars().peekable(), 99 | data: data.to_string(), 100 | index: 0 101 | }; 102 | 103 | let parser = TextParser { tag: '\'' }; 104 | let parse_result = parser.parse(&mut tokinizer); 105 | 106 | assert_eq!(parse_result.is_ok(), true); 107 | assert_eq!(tokinizer.tokens.len(), 1); 108 | assert_eq!(tokinizer.tokens[0].line, 0); 109 | assert_eq!(tokinizer.tokens[0].start, 0); 110 | assert_eq!(tokinizer.tokens[0].end, 15); 111 | 112 | match &tokinizer.tokens[0].token_type { 113 | KaramelTokenType::Text(data) => assert_eq!(&**data, "merhaba dünya"), 114 | _ => assert_eq!(true, false) 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /karamellib/src/parser/whitespace.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::error::KaramelErrorType; 3 | 4 | pub struct WhitespaceParser; 5 | 6 | impl TokenParser for WhitespaceParser { 7 | fn check(&self, tokinizer: &mut Tokinizer) -> bool { 8 | let ch = tokinizer.get_char(); 9 | return ch == ' '; 10 | } 11 | 12 | fn parse(&self, tokinizer: &mut Tokinizer) -> Result<(), KaramelErrorType> { 13 | let mut whitespace_count: u8 = 0; 14 | let mut ch = tokinizer.get_char(); 15 | let start_column = tokinizer.column; 16 | 17 | while !tokinizer.is_end() && ch == ' ' { 18 | tokinizer.increase_index(); 19 | whitespace_count += 1; 20 | ch = tokinizer.get_char(); 21 | } 22 | 23 | tokinizer.add_token(start_column, KaramelTokenType::WhiteSpace(whitespace_count)); 24 | return Ok(()); 25 | } 26 | } -------------------------------------------------------------------------------- /karamellib/src/syntax/assignment.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::types::*; 4 | use crate::syntax::{SyntaxParser, SyntaxParserTrait, SyntaxFlag}; 5 | use crate::syntax::expression::ExpressionParser; 6 | use crate::compiler::ast::KaramelAstType; 7 | 8 | use super::util::with_flag; 9 | 10 | pub struct AssignmentParser; 11 | 12 | impl SyntaxParserTrait for AssignmentParser { 13 | fn parse(parser: &SyntaxParser) -> AstResult { 14 | let index_backup = parser.get_index(); 15 | parser.indentation_check()?; 16 | 17 | let variable = ExpressionParser::parse(parser)?; 18 | 19 | match variable { 20 | KaramelAstType::Symbol(_) => (), 21 | KaramelAstType::Indexer{ body: _, indexer: _ } => (), 22 | _ => { 23 | parser.set_index(index_backup); 24 | return Ok(KaramelAstType::None); 25 | } 26 | }; 27 | 28 | parser.cleanup_whitespaces(); 29 | 30 | if let Some(operator) = parser.match_operator(&[KaramelOperatorType::Assign, 31 | KaramelOperatorType::AssignAddition, 32 | KaramelOperatorType::AssignDivision, 33 | KaramelOperatorType::AssignMultiplication, 34 | KaramelOperatorType::AssignSubtraction]) { 35 | parser.cleanup_whitespaces(); 36 | 37 | let expression = with_flag(SyntaxFlag::IN_ASSIGNMENT, parser, || ExpressionParser::parse(parser)); 38 | match expression { 39 | Ok(KaramelAstType::None) => return expression, 40 | Ok(_) => (), 41 | Err(_) => return expression 42 | }; 43 | 44 | let assignment_ast = KaramelAstType::Assignment { 45 | variable: Rc::new(variable), 46 | operator, 47 | expression: Rc::new(expression.unwrap()) 48 | }; 49 | 50 | return Ok(assignment_ast); 51 | } 52 | parser.set_index(index_backup); 53 | return Ok(KaramelAstType::None); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /karamellib/src/syntax/binary.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::types::*; 4 | use crate::syntax::{SyntaxParser, SyntaxParserTrait, SyntaxFlag}; 5 | use crate::syntax::unary::UnaryParser; 6 | use crate::syntax::util::update_functions_for_temp_return; 7 | use crate::compiler::ast::KaramelAstType; 8 | use crate::error::KaramelErrorType; 9 | 10 | use super::util::with_flag; 11 | 12 | pub struct ModuloParser; 13 | pub struct MultiplyDivideParser; 14 | pub struct AddSubtractParser; 15 | 16 | impl SyntaxParserTrait for ModuloParser { 17 | fn parse(parser: &SyntaxParser) -> AstResult { 18 | return parse_binary::(parser, &[KaramelOperatorType::Modulo]); 19 | } 20 | } 21 | 22 | impl SyntaxParserTrait for MultiplyDivideParser { 23 | fn parse(parser: &SyntaxParser) -> AstResult { 24 | return parse_binary::(parser, &[KaramelOperatorType::Multiplication, KaramelOperatorType::Division]); 25 | } 26 | } 27 | 28 | impl SyntaxParserTrait for AddSubtractParser { 29 | fn parse(parser: &SyntaxParser) -> AstResult { 30 | parse_binary::(parser, &[KaramelOperatorType::Addition, KaramelOperatorType::Subtraction]) 31 | } 32 | } 33 | 34 | pub fn parse_binary(parser: &SyntaxParser, operators: &[KaramelOperatorType]) -> AstResult { 35 | let mut functions_updated_for_temp = false; 36 | let mut left_expr = T::parse(parser)?; 37 | match left_expr { 38 | KaramelAstType::None => return Ok(left_expr), 39 | _ => () 40 | }; 41 | 42 | loop { 43 | let index_backup = parser.get_index(); 44 | parser.cleanup_whitespaces(); 45 | 46 | if let Some(operator) = parser.match_operator(operators) { 47 | if !functions_updated_for_temp { 48 | update_functions_for_temp_return(&left_expr); 49 | functions_updated_for_temp = true; 50 | } 51 | 52 | parser.cleanup_whitespaces(); 53 | 54 | let right_expr = with_flag(SyntaxFlag::IN_EXPRESSION, parser, || T::parse(parser)); 55 | match right_expr { 56 | Ok(KaramelAstType::None) => return Err(KaramelErrorType::RightSideOfExpressionNotFound), 57 | Ok(_) => (), 58 | Err(_) => return right_expr 59 | }; 60 | 61 | left_expr = KaramelAstType::Binary { 62 | left: Rc::new(left_expr), 63 | operator, 64 | right: Rc::new(right_expr.unwrap()) 65 | }; 66 | } 67 | else { 68 | parser.set_index(index_backup); 69 | break; 70 | } 71 | } 72 | 73 | Ok(left_expr) 74 | } 75 | -------------------------------------------------------------------------------- /karamellib/src/syntax/block.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::types::*; 4 | use crate::syntax::{SyntaxParser, SyntaxParserTrait}; 5 | use crate::syntax::expression::ExpressionParser; 6 | use crate::syntax::newline::NewlineParser; 7 | use crate::syntax::util::map_parser; 8 | use crate::compiler::ast::KaramelAstType; 9 | use crate::syntax::statement::StatementParser; 10 | use crate::syntax::function_defination::FunctionDefinationParser; 11 | 12 | struct BlockParser; 13 | pub struct SingleLineBlockParser; 14 | pub struct MultiLineBlockParser; 15 | 16 | impl SyntaxParserTrait for SingleLineBlockParser { 17 | fn parse(parser: &SyntaxParser) -> AstResult { 18 | BlockParser::parse(parser, false) 19 | } 20 | } 21 | 22 | 23 | impl SyntaxParserTrait for MultiLineBlockParser { 24 | fn parse(parser: &SyntaxParser) -> AstResult { 25 | BlockParser::parse(parser, true) 26 | } 27 | } 28 | 29 | 30 | impl BlockParser { 31 | fn parse(parser: &SyntaxParser, multiline: bool) -> AstResult { 32 | let mut block_asts: Vec> = Vec::new(); 33 | let current_indentation = parser.get_indentation(); 34 | 35 | loop { 36 | parser.indentation_check()?; 37 | let ast = map_parser(parser, &[FunctionDefinationParser::parse, StatementParser::parse, ExpressionParser::parse, NewlineParser::parse])?; 38 | 39 | match ast { 40 | KaramelAstType::None => break, 41 | KaramelAstType::NewLine => (), 42 | _ => block_asts.push(Rc::new(ast)) 43 | }; 44 | 45 | if !multiline { break; } 46 | 47 | parser.cleanup(); 48 | if !parser.is_same_indentation(current_indentation) { 49 | break; 50 | } 51 | } 52 | 53 | return match block_asts.len() { 54 | 0 => Ok(KaramelAstType::None), 55 | 1 => Ok((&*block_asts[0]).clone()), 56 | _ => Ok(KaramelAstType::Block(block_asts.to_vec())) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /karamellib/src/syntax/control.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::types::*; 4 | use crate::syntax::{SyntaxParser, SyntaxParserTrait, SyntaxFlag}; 5 | use crate::syntax::binary::AddSubtractParser; 6 | use crate::syntax::util::update_functions_for_temp_return; 7 | use crate::compiler::ast::KaramelAstType; 8 | use crate::error::KaramelErrorType; 9 | 10 | use super::util::with_flag; 11 | 12 | pub struct OrParser; 13 | pub struct AndParser; 14 | pub struct EqualityParser; 15 | pub struct ControlParser; 16 | 17 | impl SyntaxParserTrait for OrParser { 18 | fn parse(parser: &SyntaxParser) -> AstResult { 19 | parse_control::(parser, &[KaramelOperatorType::Or]) 20 | } 21 | } 22 | 23 | impl SyntaxParserTrait for AndParser { 24 | fn parse(parser: &SyntaxParser) -> AstResult { 25 | parse_control::(parser, &[KaramelOperatorType::And]) 26 | } 27 | } 28 | 29 | impl SyntaxParserTrait for EqualityParser { 30 | fn parse(parser: &SyntaxParser) -> AstResult { 31 | parse_control::(parser, &[KaramelOperatorType::Equal, KaramelOperatorType::NotEqual]) 32 | } 33 | } 34 | 35 | impl SyntaxParserTrait for ControlParser { 36 | fn parse(parser: &SyntaxParser) -> AstResult { 37 | special_control(parser) 38 | } 39 | } 40 | 41 | pub fn special_control(parser: &SyntaxParser) -> AstResult { 42 | let mut functions_updated_for_temp = false; 43 | let mut left_expr = AddSubtractParser::parse(parser)?; 44 | let operators = [KaramelOperatorType::GreaterEqualThan, 45 | KaramelOperatorType::GreaterThan, 46 | KaramelOperatorType::LessEqualThan, 47 | KaramelOperatorType::LessThan]; 48 | match left_expr { 49 | KaramelAstType::None => return Ok(left_expr), 50 | _ => () 51 | }; 52 | 53 | loop { 54 | let index_backup = parser.get_index(); 55 | parser.cleanup_whitespaces(); 56 | if let Some(operator) = parser.match_operator(&operators) { 57 | if !functions_updated_for_temp { 58 | update_functions_for_temp_return(&left_expr); 59 | functions_updated_for_temp = true; 60 | } 61 | 62 | parser.cleanup_whitespaces(); 63 | let right_expr = with_flag(SyntaxFlag::IN_EXPRESSION, parser, || AddSubtractParser::parse(parser)); 64 | match right_expr { 65 | Ok(KaramelAstType::None) => return Err(KaramelErrorType::RightSideOfExpressionNotFound), 66 | Ok(_) => (), 67 | Err(_) => return right_expr 68 | }; 69 | 70 | left_expr = match operator { 71 | KaramelOperatorType::LessEqualThan => KaramelAstType::Control { 72 | left: Rc::new(right_expr.unwrap()), 73 | operator: KaramelOperatorType::GreaterEqualThan, 74 | right: Rc::new(left_expr) 75 | }, 76 | KaramelOperatorType::LessThan => KaramelAstType::Control { 77 | left: Rc::new(right_expr.unwrap()), 78 | operator: KaramelOperatorType::GreaterThan, 79 | right: Rc::new(left_expr) 80 | }, 81 | _ => KaramelAstType::Control { 82 | left: Rc::new(left_expr), 83 | operator, 84 | right: Rc::new(right_expr.unwrap()) 85 | } 86 | }; 87 | } 88 | else { 89 | parser.set_index(index_backup); 90 | break; 91 | } 92 | } 93 | 94 | Ok(left_expr) 95 | } 96 | 97 | pub fn parse_control(parser: &SyntaxParser, operators: &[KaramelOperatorType]) -> AstResult { 98 | let mut functions_updated_for_temp = false; 99 | let mut left_expr = T::parse(parser)?; 100 | match left_expr { 101 | KaramelAstType::None => return Ok(left_expr), 102 | _ => () 103 | }; 104 | 105 | loop { 106 | let index_backup = parser.get_index(); 107 | parser.cleanup_whitespaces(); 108 | if let Some(operator) = parser.match_operator(operators) { 109 | if !functions_updated_for_temp { 110 | update_functions_for_temp_return(&left_expr); 111 | functions_updated_for_temp = true; 112 | } 113 | 114 | parser.cleanup_whitespaces(); 115 | let right_expr = with_flag(SyntaxFlag::IN_EXPRESSION, parser, || T::parse(parser)); 116 | match right_expr { 117 | Ok(KaramelAstType::None) => return Err(KaramelErrorType::RightSideOfExpressionNotFound), 118 | Ok(_) => (), 119 | Err(_) => return right_expr 120 | }; 121 | 122 | left_expr = KaramelAstType::Control { 123 | left: Rc::new(left_expr), 124 | operator, 125 | right: Rc::new(right_expr.unwrap()) 126 | }; 127 | } 128 | else { 129 | parser.set_index(index_backup); 130 | break; 131 | } 132 | } 133 | 134 | Ok(left_expr) 135 | } 136 | -------------------------------------------------------------------------------- /karamellib/src/syntax/expression.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::types::*; 4 | use crate::syntax::{SyntaxParser, SyntaxParserTrait, SyntaxFlag, ExtensionSyntaxParser}; 5 | use crate::syntax::func_call::FuncCallParser; 6 | use crate::syntax::unary::UnaryParser; 7 | use crate::syntax::control::OrParser; 8 | use crate::syntax::util::update_functions_for_temp_return; 9 | use crate::compiler::ast::KaramelAstType; 10 | use crate::compiler::value::KaramelPrimative; 11 | use crate::error::KaramelErrorType; 12 | 13 | use super::util::{mut_with_flag, with_flag}; 14 | 15 | pub struct ExpressionParser; 16 | 17 | impl SyntaxParserTrait for ExpressionParser { 18 | fn parse(parser: &SyntaxParser) -> AstResult { 19 | let mut ast = OrParser::parse(parser)?; 20 | 21 | loop { 22 | let index_backup = parser.get_index(); 23 | 24 | /* parse for 'object()()' */ 25 | if FuncCallParser::parsable(parser) { 26 | update_functions_for_temp_return(&ast); 27 | ast = mut_with_flag(SyntaxFlag::IN_DICT_INDEXER, parser, || FuncCallParser::parse_suffix(&mut ast, parser))?; 28 | } 29 | 30 | /* parse for 'object.method' */ 31 | else if let Some(_) = parser.match_operator(&[KaramelOperatorType::Dot]) { 32 | 33 | let sub_ast = with_flag(SyntaxFlag::IN_DICT_INDEXER, parser, || ExpressionParser::parse(parser))?; 34 | ast = match &sub_ast { 35 | KaramelAstType::Symbol(symbol) => { 36 | KaramelAstType::Indexer 37 | { 38 | body: Rc::new(ast), 39 | 40 | /* Convert symbol to text */ 41 | indexer: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Text(Rc::new(symbol.to_string()))))) 42 | } 43 | }, 44 | _ => return Err(KaramelErrorType::FunctionCallSyntaxNotValid) 45 | }; 46 | } 47 | 48 | /* parse for '["data"]' */ 49 | else if parser.check_operator(&KaramelOperatorType::SquareBracketStart) { 50 | ast = UnaryParser::parse_indexer(Rc::new(ast), parser)?; 51 | } else { 52 | parser.set_index(index_backup); 53 | break; 54 | } 55 | } 56 | 57 | Ok(ast) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /karamellib/src/syntax/function_defination.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::syntax::{SyntaxParser, SyntaxParserTrait, SyntaxFlag}; 3 | use crate::syntax::primative::PrimativeParser; 4 | use crate::compiler::ast::{KaramelAstType}; 5 | use crate::syntax::block::{SingleLineBlockParser, MultiLineBlockParser}; 6 | use crate::error::KaramelErrorType; 7 | use std::rc::Rc; 8 | 9 | pub struct FunctionDefinationParser; 10 | 11 | impl SyntaxParserTrait for FunctionDefinationParser { 12 | fn parse(parser: &SyntaxParser) -> AstResult { 13 | let index_backup = parser.get_index(); 14 | parser.indentation_check()?; 15 | 16 | if parser.match_keyword(KaramelKeywordType::Fn) { 17 | let indentation = parser.get_indentation(); 18 | 19 | parser.cleanup_whitespaces(); 20 | 21 | let mut arguments = Vec::new(); 22 | let name_expression = PrimativeParser::parse_symbol(parser)?; 23 | let function_name = match name_expression { 24 | KaramelAstType::Symbol(text) => text, 25 | _ => { 26 | return Err(KaramelErrorType::FunctionNameNotDefined); 27 | } 28 | }; 29 | 30 | parser.cleanup_whitespaces(); 31 | 32 | /* Arguments */ 33 | if let Some(_) = parser.match_operator(&[KaramelOperatorType::LeftParentheses]) { 34 | loop { 35 | parser.cleanup_whitespaces(); 36 | 37 | if parser.check_operator(&KaramelOperatorType::RightParentheses) { 38 | break; 39 | } 40 | 41 | let argument = PrimativeParser::parse_symbol(parser)?; 42 | match argument { 43 | KaramelAstType::Symbol(text) => arguments.push(text), 44 | _ => return Err(KaramelErrorType::ArgumentMustBeText) 45 | }; 46 | 47 | parser.cleanup_whitespaces(); 48 | if let None = parser.match_operator(&[KaramelOperatorType::Comma]) { 49 | break; 50 | } 51 | } 52 | 53 | if let None = parser.match_operator(&[KaramelOperatorType::RightParentheses]) { 54 | return Err(KaramelErrorType::RightParanthesesMissing); 55 | } 56 | } 57 | 58 | parser.cleanup_whitespaces(); 59 | if let None = parser.match_operator(&[KaramelOperatorType::ColonMark]) { 60 | return Err(KaramelErrorType::ColonMarkMissing); 61 | } 62 | 63 | parser.cleanup_whitespaces(); 64 | let parser_flags = parser.flags.get(); 65 | parser.flags.set(parser_flags | SyntaxFlag::FUNCTION_DEFINATION); 66 | 67 | let mut body = match parser.get_newline() { 68 | (true, _) => { 69 | parser.in_indication()?; 70 | MultiLineBlockParser::parse(parser) 71 | }, 72 | (false, _) => SingleLineBlockParser::parse(parser) 73 | }?; 74 | 75 | let has_return = match &body { 76 | KaramelAstType::Return(_) => true, 77 | KaramelAstType::Block(blocks) => 78 | match &*blocks[blocks.len() - 1] { 79 | KaramelAstType::Return(_) => true, 80 | _ => false 81 | }, 82 | KaramelAstType::None => return Err(KaramelErrorType::FunctionConditionBodyNotFound), 83 | _ => false 84 | }; 85 | 86 | if !has_return { 87 | body = match body { 88 | KaramelAstType::Block(mut blocks) => { 89 | blocks.push(Rc::new(KaramelAstType::Return(Rc::new(KaramelAstType::None)))); 90 | KaramelAstType::Block(blocks) 91 | }, 92 | _ => { 93 | KaramelAstType::Block([Rc::new(body), Rc::new(KaramelAstType::Return(Rc::new(KaramelAstType::None)))].to_vec()) 94 | } 95 | } 96 | } 97 | 98 | parser.set_indentation(indentation); 99 | parser.flags.set(parser_flags); 100 | 101 | let function_defination_ast = KaramelAstType::FunctionDefination { 102 | name: function_name, 103 | body: Rc::new(body), 104 | arguments: arguments 105 | }; 106 | 107 | parser.set_indentation(indentation); 108 | return Ok(function_defination_ast); 109 | } 110 | 111 | parser.set_index(index_backup); 112 | return Ok(KaramelAstType::None); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /karamellib/src/syntax/function_return.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::types::*; 4 | use crate::syntax::{SyntaxParser, SyntaxParserTrait, SyntaxFlag}; 5 | use crate::compiler::ast::KaramelAstType; 6 | use crate::syntax::expression::ExpressionParser; 7 | use crate::error::KaramelErrorType; 8 | 9 | pub struct FunctionReturnParser; 10 | 11 | impl SyntaxParserTrait for FunctionReturnParser { 12 | fn parse(parser: &SyntaxParser) -> AstResult { 13 | let index_backup = parser.get_index(); 14 | parser.cleanup_whitespaces(); 15 | 16 | if parser.match_keyword(KaramelKeywordType::Return) { 17 | if !parser.flags.get().contains(SyntaxFlag::FUNCTION_DEFINATION) { 18 | parser.set_index(index_backup); 19 | return Err(KaramelErrorType::ReturnMustBeUsedInFunction); 20 | } 21 | 22 | parser.cleanup_whitespaces(); 23 | 24 | let parser_flags = parser.flags.get(); 25 | parser.flags.set(parser_flags | SyntaxFlag::IN_RETURN); 26 | 27 | let ast = ExpressionParser::parse(parser)?; 28 | let return_ast = KaramelAstType::Return(Rc::new(ast)); 29 | parser.flags.set(parser_flags); 30 | 31 | return Ok(return_ast); 32 | } 33 | 34 | parser.set_index(index_backup); 35 | return Ok(KaramelAstType::None); 36 | } 37 | } -------------------------------------------------------------------------------- /karamellib/src/syntax/if_condition.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::types::*; 4 | use crate::syntax::{SyntaxParser, SyntaxParserTrait}; 5 | use crate::syntax::expression::ExpressionParser; 6 | use crate::compiler::ast::{KaramelAstType, KaramelIfStatementElseItem}; 7 | use crate::syntax::block::{SingleLineBlockParser, MultiLineBlockParser}; 8 | use crate::error::KaramelErrorType; 9 | use crate::syntax::control::OrParser; 10 | 11 | pub struct IfConditiontParser; 12 | 13 | impl SyntaxParserTrait for IfConditiontParser { 14 | fn parse(parser: &SyntaxParser) -> AstResult { 15 | let index_backup = parser.get_index(); 16 | parser.indentation_check()?; 17 | 18 | let indentation = parser.get_indentation(); 19 | let expression = OrParser::parse(parser)?; 20 | parser.cleanup_whitespaces(); 21 | 22 | if parser.match_keyword(KaramelKeywordType::If) { 23 | parser.cleanup_whitespaces(); 24 | if let None = parser.match_operator(&[KaramelOperatorType::ColonMark]) { 25 | return Err(KaramelErrorType::ColonMarkMissing); 26 | } 27 | 28 | parser.cleanup_whitespaces(); 29 | let true_body = match parser.get_newline() { 30 | (true, _) => { 31 | parser.in_indication()?; 32 | MultiLineBlockParser::parse(parser) 33 | }, 34 | (false, _) => SingleLineBlockParser::parse(parser) 35 | }?; 36 | parser.set_indentation(indentation); 37 | 38 | if true_body == KaramelAstType::None { 39 | return Err(KaramelErrorType::IfConditionBodyNotFound); 40 | } 41 | 42 | parser.cleanup_whitespaces(); 43 | 44 | let mut else_body: Option> = None; 45 | let mut else_if: Vec> = Vec::new(); 46 | 47 | while parser.is_same_indentation(indentation) { 48 | if let Some(_) = parser.match_operator(&[KaramelOperatorType::Or]) { 49 | parser.cleanup_whitespaces(); 50 | 51 | let else_condition = ExpressionParser::parse(parser)?; 52 | 53 | if else_body.is_some() { 54 | return Err(KaramelErrorType::ElseIsUsed); 55 | } 56 | 57 | match else_condition { 58 | KaramelAstType::None => (), 59 | _ => { 60 | parser.cleanup_whitespaces(); 61 | if !parser.match_keyword(KaramelKeywordType::If) { 62 | return Err(KaramelErrorType::MissingIf); 63 | } 64 | } 65 | }; 66 | 67 | parser.cleanup_whitespaces(); 68 | if let None = parser.match_operator(&[KaramelOperatorType::ColonMark]) { 69 | return Err(KaramelErrorType::ColonMarkMissing); 70 | } 71 | 72 | else if !else_body.is_none() { 73 | return Err(KaramelErrorType::MultipleElseUsageNotValid); 74 | } 75 | parser.cleanup_whitespaces(); 76 | 77 | let body = match parser.get_newline() { 78 | (true, _) => { 79 | parser.in_indication()?; 80 | MultiLineBlockParser::parse(parser) 81 | }, 82 | (false, _) => SingleLineBlockParser::parse(parser) 83 | }?; 84 | 85 | if body == KaramelAstType::None { 86 | return Err(KaramelErrorType::IfConditionBodyNotFound); 87 | } 88 | 89 | parser.set_indentation(indentation); 90 | 91 | match else_condition { 92 | KaramelAstType::None => else_body = Some(Rc::new(body)), 93 | _ => else_if.push(Rc::new(KaramelIfStatementElseItem::new(Rc::new(else_condition), Rc::new(body)))) 94 | }; 95 | } 96 | else { 97 | break; 98 | } 99 | 100 | if let Err(_) = parser.indentation_check() { 101 | break; 102 | } 103 | } 104 | 105 | let assignment_ast = KaramelAstType::IfStatement { 106 | condition: Rc::new(expression), 107 | body: Rc::new(true_body), 108 | else_body, 109 | else_if: else_if.to_vec() 110 | }; 111 | 112 | parser.set_indentation(indentation); 113 | return Ok(assignment_ast); 114 | } 115 | 116 | parser.set_index(index_backup); 117 | return Ok(KaramelAstType::None); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /karamellib/src/syntax/load_module.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::syntax::{SyntaxParser, SyntaxParserTrait}; 3 | use crate::compiler::ast::KaramelAstType; 4 | use super::primative::PrimativeParser; 5 | use super::util::map_parser; 6 | 7 | pub struct LoadModuleParser; 8 | 9 | impl SyntaxParserTrait for LoadModuleParser { 10 | fn parse(parser: &SyntaxParser) -> AstResult { 11 | let index_backup = parser.get_index(); 12 | parser.indentation_check()?; 13 | 14 | if parser.peek_token().is_ok() { 15 | let module_path = map_parser(parser, &[PrimativeParser::parse_module_path, PrimativeParser::parse_symbol])?; 16 | match module_path { 17 | 18 | /* module1::module2::module3 */ 19 | KaramelAstType::ModulePath(path) => { 20 | parser.cleanup_whitespaces(); 21 | 22 | if parser.match_keyword(KaramelKeywordType::Load) { 23 | if path.len() > 0 { 24 | return Ok(KaramelAstType::Load(path.to_vec())); 25 | } 26 | } 27 | }, 28 | 29 | /* module1 */ 30 | KaramelAstType::Symbol(path) => { 31 | parser.cleanup_whitespaces(); 32 | 33 | if parser.match_keyword(KaramelKeywordType::Load) { 34 | if path.len() > 0 { 35 | return Ok(KaramelAstType::Load([path].to_vec())); 36 | } 37 | } 38 | } 39 | _ => () 40 | }; 41 | } 42 | 43 | parser.set_index(index_backup); 44 | return Ok(KaramelAstType::None); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /karamellib/src/syntax/loop_item.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::syntax::{SyntaxParser, SyntaxParserTrait, SyntaxFlag}; 3 | use crate::compiler::ast::KaramelAstType; 4 | use crate::error::KaramelErrorType; 5 | 6 | pub struct LoopItemParser; 7 | 8 | impl SyntaxParserTrait for LoopItemParser { 9 | fn parse(parser: &SyntaxParser) -> AstResult { 10 | let index_backup = parser.get_index(); 11 | parser.cleanup_whitespaces(); 12 | 13 | if parser.check_keyword(KaramelKeywordType::Break) || 14 | parser.check_keyword(KaramelKeywordType::Continue) { 15 | if parser.flags.get().contains(SyntaxFlag::LOOP) { 16 | let keyword = parser.peek_token().unwrap().token_type.get_keyword(); 17 | parser.consume_token(); 18 | match keyword { 19 | KaramelKeywordType::Break => return Ok(KaramelAstType::Break), 20 | KaramelKeywordType::Continue => return Ok(KaramelAstType::Continue), 21 | _ => () 22 | }; 23 | } 24 | else { 25 | parser.set_index(index_backup); 26 | return Err(KaramelErrorType::BreakAndContinueBelongToLoops); 27 | } 28 | } 29 | 30 | parser.set_index(index_backup); 31 | return Ok(KaramelAstType::None); 32 | } 33 | } -------------------------------------------------------------------------------- /karamellib/src/syntax/newline.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::syntax::{SyntaxParser, SyntaxParserTrait}; 3 | use crate::compiler::ast::KaramelAstType; 4 | 5 | pub struct NewlineParser; 6 | 7 | impl SyntaxParserTrait for NewlineParser { 8 | fn parse(parser: &SyntaxParser) -> AstResult { 9 | let mut result = KaramelAstType::None; 10 | loop { 11 | if let Ok(token) = parser.peek_token() { 12 | match token.token_type { 13 | KaramelTokenType::NewLine(_) => { 14 | parser.indentation_check()?; 15 | result = KaramelAstType::NewLine; 16 | parser.consume_token(); 17 | continue; 18 | }, 19 | KaramelTokenType::WhiteSpace(_) => { 20 | result = KaramelAstType::NewLine; 21 | parser.consume_token(); 22 | continue; 23 | }, 24 | _ => { 25 | result = KaramelAstType::None; 26 | break; 27 | } 28 | } 29 | } 30 | break; 31 | } 32 | 33 | Ok(result) 34 | } 35 | } -------------------------------------------------------------------------------- /karamellib/src/syntax/statement.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::syntax::{SyntaxParser, SyntaxParserTrait}; 3 | use crate::syntax::util::map_parser; 4 | use crate::syntax::if_condition::IfConditiontParser; 5 | use crate::syntax::assignment::AssignmentParser; 6 | use crate::syntax::load_module::LoadModuleParser; 7 | use crate::syntax::function_return::FunctionReturnParser; 8 | use crate::syntax::loop_item::LoopItemParser; 9 | use crate::syntax::loops::WhileLoopParser; 10 | 11 | pub struct StatementParser; 12 | 13 | impl SyntaxParserTrait for StatementParser { 14 | fn parse(parser: &SyntaxParser) -> AstResult { 15 | return map_parser(parser, &[LoadModuleParser::parse, LoopItemParser::parse, WhileLoopParser::parse, FunctionReturnParser::parse, AssignmentParser::parse, IfConditiontParser::parse]); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /karamellib/src/syntax/util.rs: -------------------------------------------------------------------------------- 1 | use crate::types::*; 2 | use crate::syntax::{SyntaxParser}; 3 | use crate::syntax::ParseType; 4 | use crate::compiler::ast::KaramelAstType; 5 | use crate::error::KaramelErrorType; 6 | use crate::syntax::SyntaxFlag; 7 | 8 | // https://github.com/rust-lang/rust/issues/75429 9 | 10 | pub fn map_parser(parser: &SyntaxParser, parser_funcs: &[ParseType]) -> AstResult { 11 | for parser_func in parser_funcs { 12 | match parser_func(parser) { 13 | Ok(KaramelAstType::None) => (), 14 | Ok(ast) => return Ok(ast), 15 | Err(err) => return Err(err) 16 | } 17 | } 18 | 19 | Ok(KaramelAstType::None) 20 | } 21 | 22 | pub fn map_parser_with_flag(flag: SyntaxFlag, parser: &SyntaxParser, parser_funcs: &[ParseType]) -> AstResult { 23 | for parser_func in parser_funcs { 24 | match with_flag(flag, parser, || parser_func(parser)) { 25 | Ok(KaramelAstType::None) => (), 26 | Ok(ast) => return Ok(ast), 27 | Err(err) => return Err(err) 28 | } 29 | } 30 | 31 | Ok(KaramelAstType::None) 32 | } 33 | 34 | pub fn is_ast_empty(ast: &AstResult) -> bool { 35 | match ast { 36 | Ok(KaramelAstType::None) => true, 37 | Ok(_) => false, 38 | Err(_) => true 39 | } 40 | } 41 | 42 | pub fn err_or_message(ast: AstResult, none_error: KaramelErrorType) -> AstResult { 43 | match ast { 44 | Ok(KaramelAstType::None) => Err(none_error), 45 | Ok(_) => Ok(KaramelAstType::None), 46 | Err(error) => Err(error) 47 | } 48 | } 49 | 50 | pub fn update_functions_for_temp_return(ast: &KaramelAstType) { 51 | match ast { 52 | KaramelAstType::FuncCall { func_name_expression: _, arguments: _, assign_to_temp } => { 53 | assign_to_temp.set(true); 54 | }, 55 | KaramelAstType::AccessorFuncCall { 56 | source, 57 | indexer, 58 | assign_to_temp 59 | } => { 60 | update_functions_for_temp_return(source); 61 | update_functions_for_temp_return(indexer); 62 | assign_to_temp.set(true); 63 | }, 64 | KaramelAstType::Block(blocks) => { 65 | for block in blocks { 66 | update_functions_for_temp_return(&block); 67 | } 68 | }, 69 | _ => () 70 | }; 71 | } 72 | 73 | pub fn with_flag AstResult>(flag: SyntaxFlag, parser: &SyntaxParser, func: F) -> AstResult { 74 | let parser_flags = parser.flags.get(); 75 | parser.flags.set(parser_flags | flag); 76 | let loop_control = func()?; 77 | parser.flags.set(parser_flags); 78 | Ok(loop_control) 79 | } 80 | 81 | pub fn mut_with_flag AstResult>(flag: SyntaxFlag, parser: &SyntaxParser, mut func: F) -> AstResult { 82 | let parser_flags = parser.flags.get(); 83 | parser.flags.set(parser_flags | flag); 84 | let loop_control = func()?; 85 | parser.flags.set(parser_flags); 86 | Ok(loop_control) 87 | } -------------------------------------------------------------------------------- /karamellib/src/vm/executer.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::cell::RefCell; 3 | 4 | use crate::compiler::context::{ExecutionPathInfo, KaramelCompilerContext}; 5 | use crate::file::read_module_or_script; 6 | use crate::{types::Token, vm::interpreter::run_vm}; 7 | use crate::parser::*; 8 | use crate::compiler::*; 9 | use crate::syntax::SyntaxParser; 10 | use crate::logger::{CONSOLE_LOGGER, write_stderr}; 11 | use crate::error::generate_error_message; 12 | 13 | use log; 14 | use crate::types::VmObject; 15 | 16 | 17 | pub enum ExecutionSource { 18 | Code(String), 19 | File(String) 20 | } 21 | 22 | pub struct ExecutionParameters { 23 | pub source: ExecutionSource, 24 | pub return_opcode: bool, 25 | pub return_output: bool, 26 | pub dump_opcode: bool, 27 | pub dump_memory: bool 28 | } 29 | 30 | #[derive(Default)] 31 | pub struct ExecutionStatus { 32 | pub compiled: bool, 33 | pub executed: bool, 34 | pub memory_output: Option>, 35 | pub stdout: Option>, 36 | pub stderr: Option>, 37 | pub opcodes: Option>, 38 | pub memory_dump: Option, 39 | pub opcode_dump: Option 40 | } 41 | 42 | pub fn get_execution_path>(source: T) -> ExecutionPathInfo { 43 | ExecutionPathInfo { 44 | path: match source.borrow() { 45 | ExecutionSource::Code(_) => match std::env::current_exe() { 46 | Ok(path) => match path.parent() { 47 | Some(parent_path) => parent_path.to_str().unwrap().to_string(), 48 | _ => String::from(".") 49 | }, 50 | _ => String::from(".") 51 | }, 52 | ExecutionSource::File(file_name) => file_name.to_string() 53 | }, 54 | script: None 55 | } 56 | } 57 | 58 | pub fn code_executer(parameters: ExecutionParameters) -> ExecutionStatus { 59 | let mut status = ExecutionStatus::default(); 60 | match log::set_logger(&CONSOLE_LOGGER) { 61 | Ok(_) => { 62 | if cfg!(debug_assertions) { 63 | log::set_max_level(log::LevelFilter::Debug) 64 | } else { 65 | log::set_max_level(log::LevelFilter::Info) 66 | } 67 | }, 68 | _ => () 69 | }; 70 | 71 | let mut context: KaramelCompilerContext = KaramelCompilerContext::new(); 72 | context.execution_path = get_execution_path(¶meters.source); 73 | log::debug!("Execution path: {}", context.execution_path.path); 74 | 75 | if parameters.return_output { 76 | context.stdout = Some(RefCell::new(String::new())); 77 | context.stderr = Some(RefCell::new(String::new())); 78 | } 79 | 80 | let data = match parameters.source { 81 | ExecutionSource::Code(code) => code, 82 | ExecutionSource::File(filename) => { 83 | match read_module_or_script(filename, &context) { 84 | Ok(content) => content, 85 | Err(error) => { 86 | write_stderr(&context, format!("Program hata ile sonlandırıldı: {}", error)); 87 | log::error!("Program hata ile sonlandırıldı: {}", error); 88 | status.stdout = context.stdout; 89 | status.stderr = context.stderr; 90 | 91 | status.executed = false; 92 | return status 93 | } 94 | } 95 | } 96 | }; 97 | 98 | let mut parser = Parser::new(&data); 99 | match parser.parse() { 100 | Err(error) => { 101 | write_stderr(&context, generate_error_message(&data, &error)); 102 | log::error!("{}", generate_error_message(&data, &error)); 103 | status.stdout = context.stdout; 104 | status.stderr = context.stderr; 105 | 106 | return status; 107 | }, 108 | _ => () 109 | }; 110 | 111 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 112 | let ast = match syntax.parse() { 113 | Ok(ast) => ast, 114 | Err(error) => { 115 | write_stderr(&context, generate_error_message(&data, &error)); 116 | log::error!("{}", generate_error_message(&data, &error)); 117 | status.stdout = context.stdout; 118 | status.stderr = context.stderr; 119 | 120 | return status; 121 | } 122 | }; 123 | 124 | let opcode_compiler = InterpreterCompiler {}; 125 | let execution_status = match opcode_compiler.compile(ast.clone(), &mut context) { 126 | Ok(_) => unsafe { run_vm(&mut context, parameters.dump_opcode, parameters.dump_memory) }, 127 | Err(message) => { 128 | write_stderr(&context, format!("Program hata ile sonlandırıldı: {}", message)); 129 | log::error!("Program hata ile sonlandırıldı: {}", message); 130 | status.stdout = context.stdout; 131 | status.stderr = context.stderr; 132 | 133 | return status; 134 | } 135 | }; 136 | 137 | match execution_status { 138 | Ok(memory) => { 139 | status.compiled = true; 140 | status.executed = true; 141 | status.memory_output = Some(memory) 142 | }, 143 | Err(error) => { 144 | write_stderr(&context, format!("Program hata ile sonlandırıldı: {}", error)); 145 | log::error!("Program hata ile sonlandırıldı: {}", error); 146 | status.stdout = context.stdout; 147 | status.stderr = context.stderr; 148 | 149 | return status; 150 | } 151 | }; 152 | 153 | log::info!("Program başarıyla çalıştırıldı"); 154 | if parameters.return_opcode { 155 | status.opcodes = Some(parser.tokens()); 156 | } 157 | 158 | status.stdout = context.stdout; 159 | status.stderr = context.stderr; 160 | status.memory_dump = context.memory_dump; 161 | status.opcode_dump = context.opcode_dump; 162 | 163 | status 164 | } 165 | -------------------------------------------------------------------------------- /karamellib/src/vm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod interpreter; 2 | pub mod executer; -------------------------------------------------------------------------------- /karamellib/test_files/fail_func_call_1.k: -------------------------------------------------------------------------------- 1 | fonk test: döndür 1 2 | fonk test: döndür 1 -------------------------------------------------------------------------------- /karamellib/test_files/fail_test_2.k: -------------------------------------------------------------------------------- 1 | gç::satıryaz('merhaba dünya')asd -------------------------------------------------------------------------------- /karamellib/test_files/pass_bool.k: -------------------------------------------------------------------------------- 1 | sözlüğüm1 = { 'açar_1': yanlış } 2 | hataayıklama::doğrula(gç::biçimlendir(sözlüğüm1["açar_1"]), "yanlış") 3 | hataayıklama::doğrula(gç::biçimlendir(doğru), "doğru") 4 | hataayıklama::doğrula(gç::biçimlendir(sözlüğüm1), '{"açar_1": yanlış}') -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_1.k: -------------------------------------------------------------------------------- 1 | fonk test: 2 | döndür 'erhan' 3 | 4 | my_dict = { 'fn': test } 5 | hataayıklama::doğrula(my_dict['fn'](), 'erhan') -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_2.k: -------------------------------------------------------------------------------- 1 | my_dict = { 'key_1': yanlış } 2 | my_dict['key_1'] = doğru 3 | hataayıklama::doğrula(my_dict['key_1'], doğru) -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_3.k: -------------------------------------------------------------------------------- 1 | my_dict = { 'key_1': yanlış } 2 | my_dict['key_1'] = {'merhaba': 'dünya'} 3 | hataayıklama::doğrula(my_dict['key_1']['merhaba'], 'dünya') -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_4.k: -------------------------------------------------------------------------------- 1 | fonk a: döndür { 'key_1': 'evet' } 2 | hataayıklama::doğrula(a()['key_1'], 'evet') -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_5.k: -------------------------------------------------------------------------------- 1 | fonk a: 2 | döndür { 'key_1': 'evet' } 3 | hataayıklama::doğrula(a().key_1, 'evet') -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_6.k: -------------------------------------------------------------------------------- 1 | data = { 'key_1': 'evet' } 2 | data.key_1 = 'hayır' 3 | hataayıklama::doğrula(data.key_1, 'hayır') -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_7.k: -------------------------------------------------------------------------------- 1 | data = { 'key_1': 'evet' } 2 | data.key_1 = 'hayır' 3 | hataayıklama::doğrula(data.key_1, 'hayır') 4 | hataayıklama::doğrula(data['key_1'], 'hayır') 5 | hataayıklama::doğrula(data.getir('key_1'), 'hayır') 6 | hataayıklama::doğrula(data.getir('key_2'), boş) 7 | 8 | data.güncelle('key_1', 'evet') 9 | hataayıklama::doğrula(data.getir('key_1'), 'evet') 10 | 11 | data.güncelle('key_2', 'erhan') 12 | hataayıklama::doğrula(data.getir('key_2'), 'erhan') 13 | hataayıklama::doğrula(data.uzunluk(), 2) 14 | hataayıklama::doğrula(data.anahtarlar() == ['key_1', 'key_2'] veya data.anahtarlar() == ['key_2', 'key_1'] , doğru) 15 | 16 | hataayıklama::doğrula(data.içeriyormu("key_1"), doğru) 17 | hataayıklama::doğrula(data.içeriyormu("key"), yanlış) 18 | 19 | data.temizle() 20 | hataayıklama::doğrula(data.uzunluk(), 0) 21 | 22 | 23 | fonk a(): döndür 10 24 | data.key_2 = a 25 | hataayıklama::doğrula(data.getir('key_2')(), 10) -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_8.k: -------------------------------------------------------------------------------- 1 | data = { 'key_1': 'evet' } 2 | data.key_1 = 'hayır' 3 | hataayıklama::doğrula(data.key_1, 'hayır') 4 | hataayıklama::doğrula(data['key_1'], 'hayır') -------------------------------------------------------------------------------- /karamellib/test_files/pass_dict_fonk_1.k: -------------------------------------------------------------------------------- 1 | fonk test(değer): döndür değer * 2 2 | 3 | data = {} 4 | data.f = test 5 | 6 | hataayıklama::doğrula(data.f(2), 4) 7 | hataayıklama::doğrula(data['f'](2), 4) 8 | 9 | hataayıklama::doğrula(data.f(10), 20) 10 | hataayıklama::doğrula(data['f'](10), 20) -------------------------------------------------------------------------------- /karamellib/test_files/pass_func_call_1.k: -------------------------------------------------------------------------------- 1 | fonk test: 2 | fonk test_erhan: 3 | döndür 'erhan' 4 | döndür test_erhan 5 | 6 | hataayıklama::doğrula(test()(), 'erhan') 7 | hataayıklama::doğrula(baz::tür_bilgisi(test()), 'fonksiyon') 8 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_func_call_2.k: -------------------------------------------------------------------------------- 1 | fonk test: 2 | fonk test_erhan: 3 | döndür 'erhan' 4 | döndür test_erhan 5 | 6 | data = { } 7 | data.func = test 8 | 9 | hataayıklama::doğrula(data.func()(), 'erhan') -------------------------------------------------------------------------------- /karamellib/test_files/pass_func_call_3.k: -------------------------------------------------------------------------------- 1 | fonk func: 2 | fonk inner_1: 3 | fonk inner_2: 4 | döndür 'oldu' 5 | döndür inner_2 6 | döndür inner_1 7 | hataayıklama::doğrula(func()()(), "oldu") 8 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_func_call_4.k: -------------------------------------------------------------------------------- 1 | fonk test(a): 2 | döndür a == 1 3 | 4 | a = 1 5 | döngü test(a): 6 | gç::satıryaz('Çalıştı') 7 | a = 0 8 | 9 | hataayıklama::doğrula(a, 0) 10 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_if_1.k: -------------------------------------------------------------------------------- 1 | giriş_durumu = doğru 2 | giriş_durumu ise: 3 | hataayıklama::doğrula(doğru, doğru) 4 | veya: 5 | hataayıklama::doğrula(doğru, yanlış) -------------------------------------------------------------------------------- /karamellib/test_files/pass_if_2.k: -------------------------------------------------------------------------------- 1 | yaş = 1 2 | mesaj = boş 3 | 4 | yaş < 18 ise: 5 | mesaj = '18 yaşından küçüksün' 6 | 7 | veya yaş < 40 ise: 8 | mesaj = '40 yaşından küçüksün' 9 | 10 | veya yaş < 65 ise: 11 | mesaj = '65 yaşından küçüksün' 12 | veya: 13 | mesaj = '65 yaş üstüsün' 14 | 15 | hataayıklama::doğrula(mesaj, '18 yaşından küçüksün') -------------------------------------------------------------------------------- /karamellib/test_files/pass_list_1.k: -------------------------------------------------------------------------------- 1 | listem = ['erhan', 'barış', 1, doğru] 2 | listem.temizle() 3 | hataayıklama::doğrula(listem.uzunluk(), 0) 4 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_list_2.k: -------------------------------------------------------------------------------- 1 | listem = ['dünya'] 2 | beklenen = ['merhaba', 'dünya'] 3 | listem.arayaekle(0, 'merhaba') 4 | hataayıklama::doğrula(listem, beklenen) 5 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_list_3.k: -------------------------------------------------------------------------------- 1 | listem = ['dünya'] 2 | listem.arayaekle(0, 'merhaba') 3 | 4 | hataayıklama::doğrula(listem.pop(), "dünya") 5 | hataayıklama::doğrula(listem.uzunluk(), 1) 6 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_list_4.k: -------------------------------------------------------------------------------- 1 | listem = ['merhaba', 'dünya'] 2 | 3 | hataayıklama::doğrula(listem.sil(0), "merhaba") 4 | hataayıklama::doğrula(listem.uzunluk(), 1) 5 | 6 | hataayıklama::doğrula(listem.sil(0), "dünya") 7 | hataayıklama::doğrula(listem.uzunluk(), 0) 8 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_list_5.k: -------------------------------------------------------------------------------- 1 | listem = ['merhaba', 'dünya'] 2 | 3 | hataayıklama::doğrula(listem.getir(0), "merhaba") 4 | hataayıklama::doğrula(listem.getir(1), "dünya") 5 | hataayıklama::doğrula(listem.uzunluk(), 2) 6 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_list_6.k: -------------------------------------------------------------------------------- 1 | listem = ['merhaba', 'dünya'] 2 | 3 | hataayıklama::doğrula(listem.guncelle(1, 'erhan'), doğru) 4 | hataayıklama::doğrula(listem.getir(0), "merhaba") 5 | hataayıklama::doğrula(listem.getir(1), "erhan") 6 | hataayıklama::doğrula(listem.uzunluk(), 2) 7 | hataayıklama::doğrula(listem.guncelle(10, 'erhan'), yanlış) 8 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_list_7.k: -------------------------------------------------------------------------------- 1 | listem = ['merhaba', 'dünya'] 2 | 3 | hataayıklama::doğrula(listem[0], listem.getir(0)) 4 | hataayıklama::doğrula(listem[1], listem.getir(1)) 5 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_loop_1.k: -------------------------------------------------------------------------------- 1 | fonk test(a): 2 | döndür a == 1 3 | 4 | a = 1 5 | döngü test(a): 6 | gç::satıryaz('Çalıştı') 7 | a = 0 8 | 9 | hataayıklama::doğrula(a, 0) 10 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_loop_2.k: -------------------------------------------------------------------------------- 1 | sayaç = 1 2 | döngü sayaç != 10: 3 | sayaç += 1 4 | hataayıklama::doğrula(sayaç, 10) -------------------------------------------------------------------------------- /karamellib/test_files/pass_loop_3.k: -------------------------------------------------------------------------------- 1 | sayaç = 0 2 | döngü a = 0, a < 10, ++a: 3 | sayaç += 1 4 | hataayıklama::doğrula(sayaç, 10) -------------------------------------------------------------------------------- /karamellib/test_files/pass_loop_4.k: -------------------------------------------------------------------------------- 1 | fonk ata(): döndür 0 2 | fonk kontrol(a): döndür a < 10 3 | 4 | sayaç = 0 5 | döngü a = ata(), kontrol(a), ++a: 6 | sayaç += 1 7 | 8 | hataayıklama::doğrula(sayaç, 10) -------------------------------------------------------------------------------- /karamellib/test_files/pass_modulo.k: -------------------------------------------------------------------------------- 1 | hataayıklama::doğrula(2 mod 2 == 0, doğru) 2 | hataayıklama::doğrula(3 mod 2 == 0, yanlış) 3 | hataayıklama::doğrula(11 mod 2 == 1, doğru) 4 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_number.k: -------------------------------------------------------------------------------- 1 | hataayıklama::doğrula(1024.yazı(), '1024') 2 | hataayıklama::doğrula(1024.1.yazı(), '1024.1') 3 | 4 | hataayıklama::doğrula(123.yazı(), "123") 5 | hataayıklama::doğrula((123.00000001 - 0.00000001).yazı(), "123") 6 | hataayıklama::doğrula(123.00001.yazı(), "123.00001") -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_1.k: -------------------------------------------------------------------------------- 1 | sayaç = 1 2 | sonsuz: 3 | sayaç == 10 ise: 4 | kır 5 | gç::satıryaz(sayaç) 6 | sayaç += 1 7 | devam 8 | hataayıklama::doğrula(sayaç, 10) -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_2.k: -------------------------------------------------------------------------------- 1 | hataayıklama::doğrula(0, 0) 2 | hataayıklama::doğrula(doğru, !yanlış) -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_3.k: -------------------------------------------------------------------------------- 1 | kayıt = 10 2 | toplam = 0 3 | döngü kayıt: 4 | gç::satıryaz('doğru') 5 | gç::satıryaz(kayıt) 6 | kayıt -= 1 7 | toplam += 1 8 | hataayıklama::doğrula(toplam, 10) 9 | hataayıklama::doğrula(kayıt, 0) -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_4.k: -------------------------------------------------------------------------------- 1 | fonk test_1: 2 | döndür 'erhan' 3 | erhan = test_1 4 | barış = erhan 5 | hataayıklama::doğrula(barış(), erhan()) 6 | hataayıklama::doğrula(barış() + " barış", 'erhan barış') 7 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_5.k: -------------------------------------------------------------------------------- 1 | fonk test_1: 2 | fonk inner(): 3 | döndür 'gizli kayıtlar' 4 | döndür inner 5 | 6 | gizli_fonksiyon = test_1() 7 | hataayıklama::doğrula(gizli_fonksiyon(), 'gizli kayıtlar') 8 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_6.k: -------------------------------------------------------------------------------- 1 | fonk test_1: 2 | fonk inner(): 3 | döndür 'gizli kayıtlar' 4 | döndür inner 5 | 6 | gizli_fonksiyon = test_1() 7 | assert = hataayıklama::doğrula 8 | assert(gizli_fonksiyon(), 'gizli kayıtlar') 9 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_7.k: -------------------------------------------------------------------------------- 1 | gç::satıryaz -------------------------------------------------------------------------------- /karamellib/test_files/pass_test_8.k: -------------------------------------------------------------------------------- 1 | fonk a: 2 | döndür 1 3 | hataayıklama::doğrula(baz::tür_bilgisi('erhan'), 'yazı') 4 | hataayıklama::doğrula(baz::tür_bilgisi(1_204), 'sayı') 5 | hataayıklama::doğrula(baz::tür_bilgisi(doğru), 'bool') 6 | hataayıklama::doğrula(baz::tür_bilgisi([]), 'liste') 7 | hataayıklama::doğrula(baz::tür_bilgisi({}), 'sözlük') 8 | hataayıklama::doğrula(baz::tür_bilgisi(a), 'fonksiyon') 9 | hataayıklama::doğrula(baz::tür_bilgisi(boş), 'boş') 10 | -------------------------------------------------------------------------------- /karamellib/test_files/pass_text.k: -------------------------------------------------------------------------------- 1 | isim = "erhan" 2 | isim[0] = "E" 3 | hataayıklama::doğrula(isim, "Erhan") 4 | 5 | isim[5] = "E" 6 | hataayıklama::doğrula(isim, "Erhan") 7 | 8 | soyisim = "barış" 9 | soyisim[0] = "B" 10 | soyisim[3] = "i" 11 | soyisim[4] = "s" 12 | hataayıklama::doğrula(soyisim, "Baris") 13 | 14 | soyisim[-1] = "!" 15 | hataayıklama::doğrula(soyisim, "Baris") 16 | 17 | soyisim[10] = "s" 18 | hataayıklama::doğrula(soyisim, "Baris") 19 | 20 | hataayıklama::doğrula("1024".sayi(), 1024) 21 | hataayıklama::doğrula("1024.1".sayi(), 1024.1) 22 | hataayıklama::doğrula("a1024.1".sayi(), boş) 23 | hataayıklama::doğrula("erhan".sayi(), boş) 24 | hataayıklama::doğrula("+123".sayi(), 123) 25 | hataayıklama::doğrula("-123".sayi(), -123) 26 | 27 | hataayıklama::doğrula("1 Ocak\"ta işlerim var".uzunluk(), 22) 28 | hataayıklama::doğrula('1 Ocak\'ta işlerim var'.uzunluk(), 22) 29 | 30 | cümle1 = "1 Ocak\"ta işlerim var" 31 | hataayıklama::doğrula(cümle1[cümle1.uzunluk() -1], 'r') 32 | 33 | cümle2 = '1 Ocak\'ta işlerim var' 34 | hataayıklama::doğrula(cümle2[cümle2.uzunluk() -1], 'r') 35 | 36 | -------------------------------------------------------------------------------- /karamellib/test_modules/fail_module_1/baz.k: -------------------------------------------------------------------------------- 1 | hataayıklama::doğrula(doğru, yanlış) -------------------------------------------------------------------------------- /karamellib/test_modules/pass_module_1/baz.k: -------------------------------------------------------------------------------- 1 | hataayıklama::doğrula(doğru, doğru) -------------------------------------------------------------------------------- /karamellib/test_modules/pass_module_2/baz.k: -------------------------------------------------------------------------------- 1 | hesapmakinesi yükle 2 | hataayıklama::doğrula(hesapmakinesi::topla(10, 20), 30) -------------------------------------------------------------------------------- /karamellib/test_modules/pass_module_2/hesapmakinesi.k: -------------------------------------------------------------------------------- 1 | fonk topla(bir, iki): 2 | döndür bir + iki 3 | 4 | fonk çarp(bir, iki): 5 | döndür bir * iki -------------------------------------------------------------------------------- /karamellib/test_modules/pass_module_3/baz.k: -------------------------------------------------------------------------------- 1 | hesapmakinesi yükle 2 | hata yükle 3 | 4 | hata::doğrula(hesapmakinesi::topla(10, 20) == 30) 5 | hata::doğrula(hesapmakinesi::çarp(10, 20) == 200) -------------------------------------------------------------------------------- /karamellib/test_modules/pass_module_3/hata.k: -------------------------------------------------------------------------------- 1 | fonk doğrula(sonuç): 2 | hataayıklama::doğrula(sonuç, doğru) 3 | -------------------------------------------------------------------------------- /karamellib/test_modules/pass_module_3/hesapmakinesi.k: -------------------------------------------------------------------------------- 1 | fonk topla(bir, iki): 2 | döndür bir + iki 3 | 4 | fonk çarp(bir, iki): 5 | döndür bir * iki -------------------------------------------------------------------------------- /karamellib/test_modules/pass_sub_module_1/baz.k: -------------------------------------------------------------------------------- 1 | hesapmakinesi yükle 2 | hata yükle 3 | 4 | hata::doğrula(hesapmakinesi::topla(10, 20) == 30) 5 | hata::doğrula(hesapmakinesi::çarp(10, 20) == 200) -------------------------------------------------------------------------------- /karamellib/test_modules/pass_sub_module_1/hata.k: -------------------------------------------------------------------------------- 1 | fonk doğrula(sonuç): 2 | hataayıklama::doğrula(sonuç, doğru) 3 | -------------------------------------------------------------------------------- /karamellib/test_modules/pass_sub_module_1/hesapmakinesi/baz.k: -------------------------------------------------------------------------------- 1 | fonk topla(bir, iki): 2 | gç::satıryaz("Toplama fonksiyonu çağrıldı") 3 | döndür bir + iki 4 | 5 | fonk çarp(bir, iki): 6 | gç::satıryaz("Çarpma fonksiyonu çağrıldı") 7 | döndür bir * iki -------------------------------------------------------------------------------- /karamellib/test_modules/pass_sub_module_2/baz.k: -------------------------------------------------------------------------------- 1 | foo::bar yükle 2 | 3 | hataayıklama::doğrula(foo::bar::baz() == 1024) 4 | -------------------------------------------------------------------------------- /karamellib/test_modules/pass_sub_module_2/foo/bar/baz.k: -------------------------------------------------------------------------------- 1 | 2 | fonk baz(): döndür 1024 -------------------------------------------------------------------------------- /karamellib/test_modules/pass_sub_module_3/baz.k: -------------------------------------------------------------------------------- 1 | foo::bar::baz::more yükle 2 | 3 | hataayıklama::doğrula(foo::bar::baz::more::fonksiyon('erhan') == 'erhan') 4 | hataayıklama::doğrula(foo::bar::baz::more::fonksiyon('barış') == 'barış') 5 | hataayıklama::doğrula(foo::bar::baz::more::fonksiyon(2000) == 2000) 6 | 7 | -------------------------------------------------------------------------------- /karamellib/test_modules/pass_sub_module_3/foo/bar/baz/more/baz.k: -------------------------------------------------------------------------------- 1 | 2 | fonk fonksiyon(değişken): döndür değişken -------------------------------------------------------------------------------- /karamellib/tests/assignment_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use crate::karamellib::parser::*; 6 | use crate::karamellib::types::*; 7 | use crate::karamellib::syntax::SyntaxParser; 8 | use crate::karamellib::compiler::value::KaramelPrimative; 9 | use crate::karamellib::compiler::ast::KaramelAstType; 10 | use std::rc::Rc; 11 | 12 | #[warn(unused_macros)] 13 | macro_rules! test_compare { 14 | ($name:ident, $text:expr, $result:expr) => { 15 | #[test] 16 | fn $name () { 17 | let mut parser = Parser::new($text); 18 | match parser.parse() { 19 | Err(_) => assert_eq!(true, false), 20 | _ => () 21 | }; 22 | 23 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 24 | assert_eq!(syntax.parse(), $result); 25 | } 26 | }; 27 | } 28 | 29 | test_compare!(assignment_1, "erhan = 2020", Ok(Rc::new(KaramelAstType::Assignment { 30 | variable: Rc::new(KaramelAstType::Symbol("erhan".to_string())), 31 | operator: KaramelOperatorType::Assign, 32 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2020.0)))) 33 | }))); 34 | 35 | test_compare!(assignment_2, "erhan = ('erhan' * 2)", Ok(Rc::new(KaramelAstType::Assignment { 36 | variable: Rc::new(KaramelAstType::Symbol("erhan".to_string())), 37 | operator: KaramelOperatorType::Assign, 38 | expression: Rc::new(KaramelAstType::Binary { 39 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Text(Rc::new("erhan".to_string()))))), 40 | operator: KaramelOperatorType::Multiplication, 41 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2.0)))) 42 | }) 43 | }))); 44 | } -------------------------------------------------------------------------------- /karamellib/tests/block_test.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use crate::karamellib::parser::*; 6 | use crate::karamellib::types::*; 7 | use crate::karamellib::syntax::*; 8 | use crate::karamellib::compiler::value::KaramelPrimative; 9 | use crate::karamellib::compiler::ast::KaramelAstType; 10 | use std::rc::Rc; 11 | 12 | #[warn(unused_macros)] 13 | macro_rules! test_compare { 14 | ($name:ident, $text:expr, $result:expr) => { 15 | #[test] 16 | fn $name () { 17 | let mut parser = Parser::new($text); 18 | match parser.parse() { 19 | Err(_) => assert_eq!(true, false), 20 | _ => () 21 | }; 22 | 23 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 24 | assert_eq!(syntax.parse(), $result); 25 | } 26 | }; 27 | } 28 | 29 | test_compare!(block_1, "+1024", Ok(Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1024.0)))))); 30 | test_compare!(block_2, r#"erhan=1024 31 | baris=2048"#, Ok(Rc::new(KaramelAstType::Block([Rc::new(KaramelAstType::Assignment { 32 | variable: Rc::new(KaramelAstType::Symbol("erhan".to_string())), 33 | operator: KaramelOperatorType::Assign, 34 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1024.0)))) 35 | }), 36 | Rc::new(KaramelAstType::Assignment { 37 | variable: Rc::new(KaramelAstType::Symbol("baris".to_string())), 38 | operator: KaramelOperatorType::Assign, 39 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2048.0)))) 40 | })].to_vec())))); 41 | 42 | test_compare!(block_3, "erhan=1024", Ok(Rc::new(KaramelAstType::Assignment { 43 | variable: Rc::new(KaramelAstType::Symbol("erhan".to_string())), 44 | operator: KaramelOperatorType::Assign, 45 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1024.0)))) 46 | }))); 47 | } -------------------------------------------------------------------------------- /karamellib/tests/compiler_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use crate::karamellib::parser::*; 6 | use crate::karamellib::syntax::*; 7 | use crate::karamellib::compiler::*; 8 | use std::rc::Rc; 9 | 10 | #[warn(unused_macros)] 11 | macro_rules! memory_check { 12 | ($name:ident, $text:expr, $result:expr) => { 13 | #[test] 14 | fn $name () { 15 | let mut converted_memory = Vec::new(); 16 | let mut parser = Parser::new($text); 17 | match parser.parse() { 18 | Err(_) => assert_eq!(true, false), 19 | _ => () 20 | }; 21 | 22 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 23 | let syntax_result = syntax.parse(); 24 | match syntax_result { 25 | Err(_) => assert_eq!(true, false), 26 | _ => () 27 | }; 28 | 29 | let opcode_compiler = InterpreterCompiler {}; 30 | let mut compiler_options: KaramelCompilerContext = KaramelCompilerContext::new(); 31 | 32 | if let Ok(_) = opcode_compiler.compile(syntax_result.unwrap().clone(), &mut compiler_options) { 33 | for object in compiler_options.storages[0].constants.iter() { 34 | converted_memory.push((*object.deref()).clone()); 35 | } 36 | assert_eq!(converted_memory, $result); 37 | } 38 | else { 39 | assert!(false); 40 | } 41 | } 42 | } 43 | } 44 | 45 | memory_check!(memory_1, "10 + 10", vec![KaramelPrimative::Number(10.0)]); 46 | memory_check!(memory_2, "10 + 123", vec![KaramelPrimative::Number(10.0), KaramelPrimative::Number(123.0)]); 47 | memory_check!(memory_3, "11 + 12 + 13", vec![KaramelPrimative::Number(11.0), KaramelPrimative::Number(12.0), KaramelPrimative::Number(13.0)]); 48 | memory_check!(memory_4, "11 + 12 + 13 + 14", vec![KaramelPrimative::Number(11.0), KaramelPrimative::Number(12.0), KaramelPrimative::Number(13.0), KaramelPrimative::Number(14.0)]); 49 | memory_check!(memory_5, "'erhan' + 'barış'", vec![KaramelPrimative::Text(Rc::new("erhan".to_string())), KaramelPrimative::Text(Rc::new("barış".to_string()))]); 50 | memory_check!(memory_6, "'erhan' + '-' + 'barış'", vec![KaramelPrimative::Text(Rc::new("erhan".to_string())), KaramelPrimative::Text(Rc::new("-".to_string())), KaramelPrimative::Text(Rc::new("barış".to_string()))]); 51 | memory_check!(memory_7, "doğru == yanlış", vec![KaramelPrimative::Bool(true), KaramelPrimative::Bool(false)]); 52 | } -------------------------------------------------------------------------------- /karamellib/tests/control_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use crate::karamellib::parser::*; 6 | use crate::karamellib::types::*; 7 | use crate::karamellib::syntax::*; 8 | use crate::karamellib::compiler::value::KaramelPrimative; 9 | use crate::karamellib::compiler::ast::KaramelAstType; 10 | use std::rc::Rc; 11 | 12 | #[warn(unused_macros)] 13 | macro_rules! test_compare { 14 | ($name:ident, $text:expr, $result:expr) => { 15 | #[test] 16 | fn $name () { 17 | let mut parser = Parser::new($text); 18 | match parser.parse() { 19 | Err(_) => assert_eq!(true, false), 20 | _ => () 21 | }; 22 | 23 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 24 | assert_eq!(syntax.parse(), $result); 25 | } 26 | }; 27 | } 28 | 29 | test_compare!(equality_1, "10 == 10", Ok(Rc::new(KaramelAstType::Control { 30 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))), 31 | operator: KaramelOperatorType::Equal, 32 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))) 33 | }))); 34 | 35 | test_compare!(equality_2, "10 != 10", Ok(Rc::new(KaramelAstType::Control { 36 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))), 37 | operator: KaramelOperatorType::NotEqual, 38 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))) 39 | }))); 40 | 41 | test_compare!(equality_3, "10+2 != 10", Ok(Rc::new(KaramelAstType::Control { 42 | left: Rc::new(KaramelAstType::Binary { 43 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))), 44 | operator: KaramelOperatorType::Addition, 45 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2.0)))) 46 | }), 47 | operator: KaramelOperatorType::NotEqual, 48 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))) 49 | }))); 50 | 51 | test_compare!(equality_4, "10 == 10+2", Ok(Rc::new(KaramelAstType::Control { 52 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))), 53 | operator: KaramelOperatorType::Equal, 54 | right: Rc::new(KaramelAstType::Binary { 55 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))), 56 | operator: KaramelOperatorType::Addition, 57 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2.0)))) 58 | }) 59 | }))); 60 | 61 | test_compare!(and_1, "10 ve 10", Ok(Rc::new(KaramelAstType::Control { 62 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))), 63 | operator: KaramelOperatorType::And, 64 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))) 65 | }))); 66 | 67 | test_compare!(or_1, "10 veya 10", Ok(Rc::new(KaramelAstType::Control { 68 | left: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))), 69 | operator: KaramelOperatorType::Or, 70 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(10.0)))) 71 | }))); 72 | } -------------------------------------------------------------------------------- /karamellib/tests/endless_loop_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use karamellib::error::{KaramelError, KaramelErrorType}; 6 | 7 | use crate::karamellib::parser::*; 8 | use crate::karamellib::syntax::*; 9 | #[warn(unused_macros)] 10 | macro_rules! test_compare { 11 | ($name:ident, $text:expr, $result:expr) => { 12 | #[test] 13 | fn $name () { 14 | let mut parser = Parser::new($text); 15 | match parser.parse() { 16 | Err(_) => assert_eq!(true, false), 17 | _ => () 18 | }; 19 | 20 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 21 | assert_eq!(syntax.parse(), $result); 22 | } 23 | }; 24 | } 25 | 26 | /*test_compare!(endless_1, r#"sonsuz: 27 | erhan=123 28 | "#, Ok(Rc::new(KaramelAstType::EndlessLoop(Rc::new(KaramelAstType::Assignment { 29 | variable: Rc::new(KaramelAstType::Symbol("erhan".to_string())), 30 | operator: KaramelOperatorType::Assign, 31 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(123.0)))) 32 | })))));*/ 33 | /*test_compare!(endless_2, r#"sonsuz: 34 | erhan=123 35 | print(1)"#, Ok(Rc::new(KaramelAstType::EndlessLoop(Rc::new(KaramelAstType::Block([Rc::new(KaramelAstType::Assignment { 36 | variable: Rc::new(KaramelAstType::Symbol("erhan".to_string())), 37 | operator: KaramelOperatorType::Assign, 38 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(123.0)))) 39 | }), 40 | Rc::new(KaramelAstType::FuncCall { 41 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 42 | arguments: [Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0))))].to_vec(), 43 | assign_to_temp: Cell::new(false) 44 | }) 45 | ].to_vec())))))); 46 | test_compare!(endless_3, r#"sonsuz 47 | erhan=123 48 | print(1)"#, Err(KaramelError { 49 | error_type: KaramelErrorType::ColonMarkMissing, 50 | line: 0, 51 | column: 6 52 | }));*/ 53 | /*test_compare!(endless_4, r#"sonsuz: 54 | erhan=123 55 | print(1) 56 | kır"#, Ok(Rc::new(KaramelAstType::EndlessLoop(Rc::new(KaramelAstType::Block([Rc::new(KaramelAstType::Assignment { 57 | variable: Rc::new(KaramelAstType::Symbol("erhan".to_string())), 58 | operator: KaramelOperatorType::Assign, 59 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(123.0)))) 60 | }), 61 | Rc::new(KaramelAstType::FuncCall { 62 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 63 | arguments: [Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0))))].to_vec(), 64 | assign_to_temp: Cell::new(false) 65 | }), 66 | Rc::new(KaramelAstType::Break) 67 | ].to_vec()))))));*/ 68 | test_compare!(endless_5, r#"kır"#, Err(KaramelError { 69 | error_type: KaramelErrorType::BreakAndContinueBelongToLoops, 70 | column: 3, 71 | line: 0 72 | })); 73 | test_compare!(endless_6, r#"devam"#, Err(KaramelError { 74 | error_type: KaramelErrorType::BreakAndContinueBelongToLoops, 75 | column: 5, 76 | line: 0 77 | })); 78 | } 79 | -------------------------------------------------------------------------------- /karamellib/tests/func_call_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use karamellib::error::{KaramelError, KaramelErrorType}; 6 | 7 | use crate::karamellib::parser::*; 8 | use crate::karamellib::syntax::*; 9 | use crate::karamellib::compiler::value::KaramelPrimative; 10 | use crate::karamellib::compiler::ast::KaramelAstType; 11 | use std::cell::Cell; 12 | use std::rc::Rc; 13 | 14 | #[warn(unused_macros)] 15 | macro_rules! test_compare { 16 | ($name:ident, $text:expr, $result:expr) => { 17 | #[test] 18 | fn $name () { 19 | let mut parser = Parser::new($text); 20 | match parser.parse() { 21 | Err(_) => assert_eq!(true, false), 22 | _ => () 23 | }; 24 | 25 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 26 | assert_eq!(syntax.parse(), $result); 27 | } 28 | }; 29 | } 30 | 31 | test_compare!(func_call_1, "print()", Ok(Rc::new(KaramelAstType::FuncCall { 32 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 33 | arguments: Vec::new(), 34 | assign_to_temp: Cell::new(false) 35 | }))); 36 | 37 | test_compare!(func_call_2, "print(1)", Ok(Rc::new(KaramelAstType::FuncCall { 38 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 39 | arguments: [Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0))))].to_vec(), 40 | assign_to_temp: Cell::new(false) 41 | }))); 42 | 43 | test_compare!(func_call_3, "print( 1 )", Ok(Rc::new(KaramelAstType::FuncCall { 44 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 45 | arguments: [Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0))))].to_vec(), 46 | assign_to_temp: Cell::new(false) 47 | }))); 48 | 49 | test_compare!(func_call_4, "print( 1 , 2 )", Ok(Rc::new(KaramelAstType::FuncCall { 50 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 51 | arguments: [Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0)))), Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2.0))))].to_vec(), 52 | assign_to_temp: Cell::new(false) 53 | }))); 54 | 55 | test_compare!(func_call_5, "print(1,2)", Ok(Rc::new(KaramelAstType::FuncCall { 56 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 57 | arguments: [Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0)))), Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2.0))))].to_vec(), 58 | assign_to_temp: Cell::new(false) 59 | }))); 60 | 61 | test_compare!(func_call_6, "print(1,2,'erhan')", Ok(Rc::new(KaramelAstType::FuncCall { 62 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 63 | arguments: [Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0)))), 64 | Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(2.0)))), 65 | Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Text(Rc::new("erhan".to_string())))))].to_vec(), 66 | assign_to_temp: Cell::new(false) 67 | }))); 68 | 69 | test_compare!(func_call_7, "print(,2,'erhan')", Err(KaramelError { 70 | error_type: KaramelErrorType::SyntaxError, 71 | column: 6, 72 | line: 0 73 | })); 74 | test_compare!(func_call_8, "print(", Err(KaramelError { 75 | error_type: KaramelErrorType::RightParanthesesMissing, 76 | column: 6, 77 | line: 0 78 | })); 79 | test_compare!(func_call_9, "data=print()", Ok(Rc::new(KaramelAstType::Assignment { 80 | variable: Rc::new(KaramelAstType::Symbol("data".to_string())), 81 | operator: karamellib::types::KaramelOperatorType::Assign, 82 | expression: Rc::new(KaramelAstType::FuncCall { 83 | func_name_expression: Rc::new(KaramelAstType::Symbol("print".to_string())), 84 | arguments: Vec::new(), 85 | assign_to_temp: Cell::new(true) 86 | }) 87 | }))); 88 | test_compare!(func_call_10, "data1() + data2()", Ok(Rc::new(KaramelAstType::Binary { 89 | left: Rc::new(KaramelAstType::FuncCall { 90 | func_name_expression: Rc::new(KaramelAstType::Symbol("data1".to_string())), 91 | arguments: Vec::new(), 92 | assign_to_temp: Cell::new(true) 93 | }), 94 | operator: karamellib::types::KaramelOperatorType::Addition, 95 | right: Rc::new(KaramelAstType::FuncCall { 96 | func_name_expression: Rc::new(KaramelAstType::Symbol("data2".to_string())), 97 | arguments: Vec::new(), 98 | assign_to_temp: Cell::new(true) 99 | }) 100 | }))); 101 | test_compare!(func_call_11, "data1() > data2()", Ok(Rc::new(KaramelAstType::Control { 102 | left: Rc::new(KaramelAstType::FuncCall { 103 | func_name_expression: Rc::new(KaramelAstType::Symbol("data1".to_string())), 104 | arguments: Vec::new(), 105 | assign_to_temp: Cell::new(true) 106 | }), 107 | operator: karamellib::types::KaramelOperatorType::GreaterThan, 108 | right: Rc::new(KaramelAstType::FuncCall { 109 | func_name_expression: Rc::new(KaramelAstType::Symbol("data2".to_string())), 110 | arguments: Vec::new(), 111 | assign_to_temp: Cell::new(true) 112 | }) 113 | }))); 114 | test_compare!(func_call_12, "gç::satıryaz", Ok(Rc::new(KaramelAstType::ModulePath(["gç".to_string(), "satıryaz".to_string()].to_vec())))); 115 | } -------------------------------------------------------------------------------- /karamellib/tests/indentation_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use crate::karamellib::parser::*; 6 | use crate::karamellib::syntax::*; 7 | 8 | #[warn(unused_macros)] 9 | macro_rules! test_success { 10 | ($name:ident, $text:expr) => { 11 | #[test] 12 | fn $name () { 13 | let mut parser = Parser::new($text); 14 | let _parse_result = parser.parse(); 15 | 16 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 17 | let parse_result = syntax.parse(); 18 | 19 | match parse_result { 20 | Ok(_) => { 21 | assert_eq!(true, true); 22 | }, 23 | Err(_) => { 24 | assert_eq!(false, true); 25 | } 26 | }; 27 | } 28 | }; 29 | } 30 | 31 | #[warn(unused_macros)] 32 | macro_rules! test_fail { 33 | ($name:ident, $text:expr) => { 34 | #[test] 35 | fn $name () { 36 | let mut parser = Parser::new($text); 37 | let _parse_result = parser.parse(); 38 | 39 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 40 | let parse_result = syntax.parse(); 41 | 42 | match parse_result { 43 | Ok(_) => { 44 | println!("'{}'", $text); 45 | assert_eq!(false, true); 46 | }, 47 | Err(_) => { 48 | assert_eq!(true, true); 49 | } 50 | }; 51 | } 52 | }; 53 | } 54 | 55 | test_success!(indentation_1, "123"); 56 | test_fail!(indentation_2, " 123"); 57 | test_fail!(indentation_3, r#" 58 | 123"#); 59 | test_success!(indentation_4, r#" 60 | 123"#); 61 | test_fail!(indentation_5, r#" 62 | 123"#); 63 | test_fail!(indentation_6, r#" 64 | 123"#); 65 | test_success!(indentation_7, r#"1024 * 123 ise: 66 | erhan=123 67 | veya: 68 | erhan=1234"#); 69 | test_success!(indentation_8, r#"1024 * 123 ise: 70 | erhan=123 71 | veya: 72 | erhan=1234 73 | "#); 74 | test_success!(indentation_9, r#"1024 * 123 ise: 75 | erhan=123 76 | veya: 77 | erhan=1234 78 | erhan=22"#); 79 | test_success!(indentation_10, r#"1024 * 123 ise: 80 | erhan=123 81 | veya: 82 | erhan=1234 83 | erhan=22"#); 84 | test_success!(indentation_11, r#"1024 * 123 ise: 85 | erhan=123 86 | doğru ise: 87 | erhan=123 88 | veya: 89 | erhan=1234 90 | erhan=22"#); 91 | 92 | test_success!(indentation_12, r#" 93 | 1024 * 123 ise: 94 | erhan=123 95 | 96 | doğru ise: 97 | io::print('merhaba dünya') 98 | erhan=123 99 | veya: 100 | erhan=1234 101 | erhan=22"#); 102 | } -------------------------------------------------------------------------------- /karamellib/tests/mod.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turkce-yazilim-konati/karamel/5c655238fefcb70abb7b2f4579e1d86cac8b993d/karamellib/tests/mod.rs -------------------------------------------------------------------------------- /karamellib/tests/test_executers.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use std::fs::Metadata; 6 | 7 | use crate::karamellib::vm::*; 8 | use crate::karamellib::{vm::executer::{ExecutionParameters, ExecutionSource}}; 9 | 10 | enum ExecuterType { 11 | File, 12 | Module 13 | } 14 | 15 | impl ExecuterType { 16 | pub fn get_folder_name(&self) -> String { 17 | match &self { 18 | ExecuterType::File => "test_files".to_string(), 19 | ExecuterType::Module => "test_modules".to_string(), 20 | } 21 | } 22 | 23 | pub fn is_valid(&self, metadata: &Metadata) -> bool { 24 | match &self { 25 | ExecuterType::File => metadata.is_file(), 26 | ExecuterType::Module => metadata.is_dir(), 27 | } 28 | } 29 | } 30 | 31 | fn executer(executer_type: ExecuterType) -> Result<(), String> { 32 | use std::env; 33 | use std::fs; 34 | use std::path::Path; 35 | use colored::*; 36 | 37 | let mut failed_cases = Vec::new(); 38 | let current_dir = env::current_dir().unwrap(); 39 | let test_files = fs::read_dir(Path::new(¤t_dir).join(executer_type.get_folder_name())).unwrap(); 40 | 41 | for test_file in test_files { 42 | match test_file { 43 | Ok(path) => { 44 | let is_valid = match path.metadata() { 45 | Ok(metadata) => executer_type.is_valid(&metadata), 46 | _ => false 47 | }; 48 | 49 | if !is_valid { continue; } 50 | let is_pass = path.path().file_name().unwrap().to_str().unwrap().starts_with("pass_"); 51 | 52 | match path.path().to_str() { 53 | Some(path_str) => { 54 | let parameters = ExecutionParameters { 55 | source: ExecutionSource::File(path_str.to_string()), 56 | return_opcode: false, 57 | return_output: false, 58 | dump_opcode: false, 59 | dump_memory: false 60 | }; 61 | 62 | let result = executer::code_executer(parameters); 63 | match result.compiled && result.executed { 64 | true => { 65 | if !is_pass { 66 | failed_cases.push(format!("# {} failed ({})", path_str, "Not failed".red())); 67 | } 68 | }, 69 | false => { 70 | if is_pass { 71 | failed_cases.push(format!("# {} failed", path_str)); 72 | } 73 | } 74 | } 75 | }, 76 | _ => () 77 | } 78 | }, 79 | _ => () 80 | }; 81 | } 82 | 83 | 84 | if !failed_cases.is_empty() { 85 | return Err(failed_cases.join("\r\n")); 86 | } 87 | 88 | return Ok(()) 89 | } 90 | 91 | #[test] 92 | fn test_file_executer() -> Result<(), String> { 93 | executer(ExecuterType::File) 94 | } 95 | 96 | 97 | #[test] 98 | fn test_module_executer() -> Result<(), String> { 99 | executer(ExecuterType::Module) 100 | } 101 | } -------------------------------------------------------------------------------- /karamellib/tests/types.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use crate::karamellib::types::CharTraits; 6 | 7 | #[test] 8 | fn is_new_line() { 9 | assert_eq!(true, '\n'.is_new_line()); 10 | assert_eq!(false, ' '.is_new_line()); 11 | } 12 | 13 | #[test] 14 | fn is_integer() { 15 | for ch in '0'..'9' { 16 | assert_eq!(true, ch.is_integer()); 17 | } 18 | assert_eq!(false, 'a'.is_integer()); 19 | } 20 | 21 | #[test] 22 | fn is_symbol() { 23 | assert_eq!(true, '_'.is_symbol()); 24 | for ch in 'a'..'z' { 25 | assert_eq!(true, ch.is_symbol()); 26 | } 27 | for ch in 'A'..'Z' { 28 | assert_eq!(true, ch.is_symbol()); 29 | } 30 | 31 | assert_eq!(true, '$'.is_symbol()); 32 | 33 | } 34 | 35 | #[test] 36 | fn is_whitespace() { 37 | assert_eq!(true, ' '.is_whitespace()); 38 | assert_eq!(true, '\r'.is_whitespace()); 39 | assert_eq!(true, '\t'.is_whitespace()); 40 | assert_eq!(false, '2'.is_whitespace()); 41 | } 42 | } -------------------------------------------------------------------------------- /karamellib/tests/unary_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use karamellib::error::{KaramelError, KaramelErrorType}; 6 | 7 | use crate::karamellib::parser::*; 8 | use crate::karamellib::types::*; 9 | use crate::karamellib::syntax::*; 10 | use crate::karamellib::compiler::value::KaramelPrimative; 11 | use crate::karamellib::compiler::ast::KaramelAstType; 12 | use std::rc::Rc; 13 | use std::cell::Cell; 14 | 15 | #[warn(unused_macros)] 16 | macro_rules! test_compare { 17 | ($name:ident, $text:expr, $result:expr) => { 18 | #[test] 19 | fn $name () { 20 | let mut parser = Parser::new($text); 21 | match parser.parse() { 22 | Err(_) => assert_eq!(true, false), 23 | _ => () 24 | }; 25 | 26 | let syntax = SyntaxParser::new(parser.tokens().to_vec()); 27 | assert_eq!(syntax.parse(), $result); 28 | } 29 | }; 30 | } 31 | 32 | test_compare!(unary_1, "+1024", Ok(Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1024.0)))))); 33 | test_compare!(unary_2, "-1024", Ok(Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(-1024.0)))))); 34 | test_compare!(unary_3, "+1024.0", Ok(Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1024.0)))))); 35 | test_compare!(unary_4, "-1024.0", Ok(Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(-1024.0)))))); 36 | test_compare!(unary_5, "değil doğru", Ok(Rc::new(KaramelAstType::PrefixUnary{ 37 | operator: KaramelOperatorType::Not, 38 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Bool(true)))), 39 | assign_to_temp: Cell::new(false) 40 | }))); 41 | test_compare!(unary_6, "değil yanlış", Ok(Rc::new(KaramelAstType::PrefixUnary 42 | { 43 | operator: KaramelOperatorType::Not, 44 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Bool(false)))), 45 | assign_to_temp: Cell::new(false) 46 | }))); 47 | test_compare!(unary_7, "değil doğru", Ok(Rc::new(KaramelAstType::PrefixUnary 48 | { 49 | operator: KaramelOperatorType::Not, 50 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Bool(true)))), 51 | assign_to_temp: Cell::new(false) 52 | }))); 53 | test_compare!(unary_8, "değil yanlış", Ok(Rc::new(KaramelAstType::PrefixUnary { 54 | operator: KaramelOperatorType::Not, 55 | expression: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Bool(false)))), 56 | assign_to_temp: Cell::new(false) 57 | }))); 58 | 59 | test_compare!(unary_9, "+[]", Err(KaramelError { 60 | error_type: KaramelErrorType::UnaryWorksWithNumber, 61 | column: 1, 62 | line: 0 63 | })); 64 | test_compare!(unary_10, "++100", Err(KaramelError { 65 | error_type: KaramelErrorType::InvalidUnaryOperation, 66 | column: 2, 67 | line: 0 68 | })); 69 | test_compare!(unary_11, "--100", Err(KaramelError { 70 | error_type: KaramelErrorType::InvalidUnaryOperation, 71 | column: 2, 72 | line: 0 73 | })); 74 | test_compare!(unary_12, "--doğru", Err(KaramelError { 75 | error_type: KaramelErrorType::InvalidUnaryOperation, 76 | column: 2, 77 | line: 0 78 | })); 79 | 80 | test_compare!(unary_13, "++data", Ok(Rc::new(KaramelAstType::PrefixUnary { 81 | operator: KaramelOperatorType::Increment, 82 | expression: Rc::new(KaramelAstType::Symbol("data".to_string())), 83 | assign_to_temp: Cell::new(false) 84 | }))); 85 | test_compare!(unary_14, "--data", Ok(Rc::new(KaramelAstType::PrefixUnary { 86 | operator: KaramelOperatorType::Deccrement, 87 | expression: Rc::new(KaramelAstType::Symbol("data".to_string())), 88 | assign_to_temp: Cell::new(false) 89 | }))); 90 | test_compare!(unary_15, "--data", Ok(Rc::new(KaramelAstType::PrefixUnary { 91 | operator: KaramelOperatorType::Deccrement, 92 | expression: Rc::new(KaramelAstType::Symbol("data".to_string())), 93 | assign_to_temp: Cell::new(false) 94 | }))); 95 | test_compare!(unary_16, "data--", Ok(Rc::new(KaramelAstType::SuffixUnary(KaramelOperatorType::Deccrement, Rc::new(KaramelAstType::Symbol("data".to_string())))))); 96 | test_compare!(unary_17, "data++", Ok(Rc::new(KaramelAstType::SuffixUnary(KaramelOperatorType::Increment, Rc::new(KaramelAstType::Symbol("data".to_string())))))); 97 | 98 | test_compare!(unary_18, "+ 1024", Ok(Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1024.0)))))); 99 | test_compare!(unary_19, "++data - 1", Ok(Rc::new(KaramelAstType::Binary { 100 | left: Rc::new(KaramelAstType::PrefixUnary { 101 | operator: KaramelOperatorType::Increment, 102 | expression: Rc::new(KaramelAstType::Symbol("data".to_string())), 103 | assign_to_temp: Cell::new(false) 104 | }), 105 | operator: KaramelOperatorType::Subtraction, 106 | right: Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Number(1.0)))) 107 | }))); 108 | //test_compare!(unary_19, "doğru değil", Ok(Rc::new(KaramelAstType::SuffixUnary(KaramelOperatorType::Not, Rc::new(KaramelAstType::Primative(Rc::new(KaramelPrimative::Bool(true))))))); 109 | } -------------------------------------------------------------------------------- /karamelweb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "karamelweb" 3 | version = "0.1.0" 4 | authors = ["Erhan BARIS "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | karamellib = { path = "../karamellib" } 11 | wasm-bindgen = "0.2" 12 | js-sys = "0.3.47" 13 | 14 | [lib] 15 | crate-type = ["cdylib", "rlib"] 16 | doctest = false -------------------------------------------------------------------------------- /karamelweb/build_command: -------------------------------------------------------------------------------- 1 | cd karamelweb/ 2 | cargo build --target wasm32-unknown-unknown --release 3 | wasm-pack build --out-dir src/www/pkg/ --target web --no-typescript 4 | wasm-gc src/www/pkg/karamelweb_bg.wasm 5 | cd src/www/ 6 | python3 -m http.server 7 | python -m SimpleHTTPServer -------------------------------------------------------------------------------- /karamelweb/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate karamellib; 2 | 3 | use karamellib::{compiler::KaramelPrimative, vm::executer::{ExecutionParameters, ExecutionSource}}; 4 | use wasm_bindgen::prelude::*; 5 | use js_sys::*; 6 | 7 | #[wasm_bindgen] 8 | pub fn execute_code(name: &str) -> Object { 9 | let response = js_sys::Object::new(); 10 | 11 | /* JS referance object */ 12 | let status_ref = JsValue::from("status"); 13 | let results_ref = JsValue::from("results"); 14 | let stdout_ref = JsValue::from("stdout"); 15 | let stderr_ref = JsValue::from("stderr"); 16 | let opcode_dump_ref = JsValue::from("code_dump"); 17 | let memory_dump_ref = JsValue::from("memory_dump"); 18 | 19 | let parameters = ExecutionParameters { 20 | source: ExecutionSource::Code(name.to_string()), 21 | return_opcode: true, 22 | return_output: true, 23 | dump_opcode: true, 24 | dump_memory: true 25 | }; 26 | 27 | let result = karamellib::vm::executer::code_executer(parameters); 28 | match result.compiled && result.executed { 29 | true => { 30 | Reflect::set(response.as_ref(), status_ref.as_ref(), JsValue::from_bool(true).as_ref()).unwrap(); 31 | let results = Array::new(); 32 | let stdouts = Array::new(); 33 | match result.memory_output { 34 | Some(opjects) => { 35 | for object in opjects.iter() { 36 | match &*object.deref() { 37 | KaramelPrimative::Text(text) => results.push(&JsValue::from(&**text).into()), 38 | KaramelPrimative::Number(number) => results.push(&JsValue::from_f64(*number).into()), 39 | KaramelPrimative::Bool(bool) => results.push(&JsValue::from_bool(*bool).into()), 40 | KaramelPrimative::Empty => results.push(&JsValue::undefined().into()), 41 | _ => 0 42 | }; 43 | } 44 | }, 45 | None=> () 46 | }; 47 | 48 | match result.stdout { 49 | Some(stdout) => { stdouts.push(&JsValue::from(stdout.borrow().clone()).into()); }, 50 | _ => () 51 | }; 52 | 53 | if let Some(memory_dump) = result.memory_dump { 54 | Reflect::set(response.as_ref(), memory_dump_ref.as_ref(), JsValue::from_str(&memory_dump).as_ref()).unwrap(); 55 | } 56 | 57 | if let Some(opcode_dump) = result.opcode_dump { 58 | Reflect::set(response.as_ref(), opcode_dump_ref.as_ref(), JsValue::from_str(&opcode_dump).as_ref()).unwrap(); 59 | } 60 | 61 | Reflect::set(response.as_ref(), results_ref.as_ref(), results.as_ref()).unwrap(); 62 | Reflect::set(response.as_ref(), results_ref.as_ref(), results.as_ref()).unwrap(); 63 | Reflect::set(response.as_ref(), stdout_ref.as_ref(), stdouts.as_ref()).unwrap(); 64 | }, 65 | false => { 66 | let stderrs = Array::new(); 67 | let stdouts = Array::new(); 68 | 69 | match result.stdout { 70 | Some(stdout) => { stdouts.push(&JsValue::from(stdout.borrow().clone()).into()); }, 71 | _ => () 72 | }; 73 | 74 | match result.stderr { 75 | Some(stderr) => { stderrs.push(&JsValue::from(stderr.borrow().clone()).into()); }, 76 | _ => () 77 | }; 78 | 79 | Reflect::set(response.as_ref(), status_ref.as_ref(), JsValue::from_bool(false).as_ref()).unwrap(); 80 | Reflect::set(response.as_ref(), stdout_ref.as_ref(), stdouts.as_ref()).unwrap(); 81 | Reflect::set(response.as_ref(), stderr_ref.as_ref(), stderrs.as_ref()).unwrap(); 82 | } 83 | }; 84 | 85 | response 86 | } 87 | -------------------------------------------------------------------------------- /karamelweb/src/www/pkg/karamelweb_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turkce-yazilim-konati/karamel/5c655238fefcb70abb7b2f4579e1d86cac8b993d/karamelweb/src/www/pkg/karamelweb_bg.wasm --------------------------------------------------------------------------------