├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── docs ├── arithmetic.md ├── cppext.md ├── data.md ├── flow.md ├── index.md ├── io.md ├── list.md ├── manList ├── map.md ├── naming.md ├── procedure.md ├── requirements.txt ├── structure.md ├── text.md └── time.md ├── images ├── big-ldpl4.0-logo.png ├── ldpl-4.0-badge.png ├── ldpl-4.0-logo.png ├── ldpl-logo-white.png ├── ldpl-logo.png ├── ldpl-open-graph.png ├── ldplsaur-4.0.png ├── ldplsaur-briefcase.png ├── ldplsaur-giant.png ├── ldplsaur.png ├── lpm-logo.png ├── reference-logo.png ├── release-logos │ ├── 5.1.0.png │ ├── active-argentinosaurus-white.png │ ├── active-argentinosaurus.png │ ├── busy-brontosaurus-white.png │ ├── busy-brontosaurus.png │ ├── creative-carnotaurus-white.png │ ├── creative-carnotaurus.png │ ├── diligent-dreadnoughtus-white.png │ ├── diligent-dreadnoughtus.png │ ├── eloquent-eoraptor-white.png │ ├── eloquent-eoraptor.png │ ├── friendly-falcarius-white.png │ ├── friendly-falcarius.png │ ├── groovy-gualicho.png │ └── source │ │ └── 5.1.0.acorn └── tutorial-logo.png ├── makefile ├── man ├── README ├── compileman.php ├── generateMan.sh ├── ldpl.1 └── ldplman-intro ├── mkdocs.yml ├── snapcraft.yaml └── src ├── CMakeLists.txt ├── aux ├── aux_c_format.cpp ├── aux_code.cpp ├── aux_compile_line.cpp ├── aux_container.cpp ├── aux_format.cpp ├── aux_info.cpp ├── aux_line_like.cpp ├── aux_state.cpp ├── aux_tokenizer.cpp └── aux_typecheck.cpp ├── data_types ├── code_location.h └── compiler_state.h ├── ldpl.cpp ├── ldpl.h ├── ldpl_lib ├── BigInt.hpp └── ldpl_lib.cpp └── libraries ├── cpptrim.h └── vector_contains.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ldpl 3 | ldpl.o 4 | ldpl.exe 5 | ldpl_included_lib.cpp 6 | lpm 7 | src/ldpl.1 8 | testing/* 9 | testing 10 | build 11 | .vscode 12 | *-bin 13 | Icon 14 | test 15 | test/* 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Welcome, mighty one! You are about to learn the ways of the dinosaur tamers. Please take a moment to familiarize yourself with them before trying to tame any dinosaurs yourself. 4 | 5 | ## Nomenclature 6 | 7 | In this document we use the term **dinosaur tamer** to refer anyone who contributes 8 | to LDPL, including (but not limited to) contributors to the repository code, 9 | mantainers, bug reporters, people who code in LDPL, people who submit issues 10 | and anyone interested and/or involved with the language in any way. 11 | 12 | ![Hello there!](https://www.ldpl-lang.org/images/Visitor.png) 13 | 14 | ## Our Pledge 15 | 16 | In the interest of fostering an open and welcoming environment, we as 17 | dinosaur tamers pledge to making participation in our project and 18 | our community a harassment-free experience for everyone, regardless of age, body 19 | size, disability, ethnicity, sex characteristics, gender identity and expression, 20 | level of experience, education, socio-economic status, nationality, personal 21 | appearance, race, religion, dislikeness of COBOL or sexual identity, and orientation. 22 | 23 | ## Our Standards 24 | 25 | Examples of behavior that contributes to creating a positive environment 26 | include: 27 | 28 | * Using welcoming and inclusive language 29 | * Being respectful of differing viewpoints and experiences 30 | * Gracefully accepting constructive criticism 31 | * Focusing on what is best for the community 32 | * Showing empathy towards other community members 33 | 34 | Examples of unacceptable behavior by participants include: 35 | 36 | * The use of sexualized language or imagery and unwelcome sexual attention or 37 | advances 38 | * Trolling, insulting/derogatory comments, and personal or political attacks 39 | * Public or private harassment 40 | * Publishing others' private information, such as a physical or electronic 41 | address, without explicit permission 42 | * Other conduct which could reasonably be considered inappropriate in a 43 | professional setting 44 | 45 | ## Our Responsibilities 46 | 47 | Project maintaining dinosaur tamers are responsible for clarifying the standards of acceptable 48 | behavior and are expected to take appropriate and fair corrective action in 49 | response to any instances of unacceptable behavior. 50 | 51 | Project maintaining dinosaur tamers have the right and responsibility to remove, edit, or 52 | reject comments, commits, code, wiki edits, issues, and other contributions 53 | that are not aligned to this Code of Conduct, or to ban temporarily or 54 | permanently any contributor for other behaviors that they deem inappropriate, 55 | threatening, offensive, or harmful. 56 | 57 | ## Scope 58 | 59 | This Code of Conduct applies both within project spaces and in public spaces 60 | when an individual is representing the LDPL project or its community. Examples of 61 | representing the project or community include using an official LDPL e-mail 62 | address, posting via an official LDPL social media account, or acting as an appointed LDPL 63 | representative at an online or offline event. Representation of the LDPL project may be 64 | further defined and clarified by project maintaining dinosaur tamers. 65 | 66 | ## Enforcement 67 | 68 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 69 | reported by contacting the project team at ***martin (at) ldpl-lang.org***. All 70 | complaints will be reviewed and investigated and will result in a response that 71 | is deemed necessary and appropriate to the circumstances. The project team is 72 | obligated to maintain confidentiality with regard to the reporter of an incident. 73 | Further details of specific enforcement policies may be posted separately. 74 | 75 | Project maintainers who do not follow or enforce the Dinosaur Tamers Code in good 76 | faith may face temporary or permanent repercussions as determined by other 77 | members of the project's leadership. 78 | 79 | ## Attribution 80 | 81 | This Code of Conduct is ~stolen~ adapted from the [Contributor Covenant][homepage], version 1.4, 82 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 83 | 84 | [homepage]: https://www.contributor-covenant.org 85 | 86 | For answers to common questions about this code of conduct, see 87 | https://www.contributor-covenant.org/faq 88 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Hello there! 2 | 3 | Welcome to the LDPL contributors guide, thank you for being here! This guide will tell you all you need to know in order to contribute to the LDPL project. 4 | 5 | ### Who can contribute to LDPL? 6 | 7 | Anyone can contribute to the LDPL project! As stated on the README.md file, we accept all kinds of contributions anyone can make. From adding statements to the language (that's in fact really easy!) to fixing bugs, adding issues, writing examples, documentation writing some software in LDPL, etc. Anything you consider will make the project grow and better, you are welcome to contribute it. Even a simple drawing or telling your friends about LDPL! 8 | 9 | ### What do I need to know to contribute to LDPL? 10 | 11 | Please take a moment to familiarize yourself with the [Code Of Conduct](/CODE_OF_CONDUCT) (you are expected and required to behave by this code) and the [LDPL reference](https://docs.ldpl-lang.org/) (not really a *must* per se, but that's where all the language is documented). 12 | 13 | C++ knowledge is a must if you want to contribute to the compiler code, and LDPL knowledge is a must if you want to write examples or LDPL software. Drawing is not really a must if you want to submit a drawing, but you may get better results if you know how to draw. We'll love your art anyway, though. 14 | 15 | ### Will you accept my pull request? 16 | 17 | We tend to accept most pull requests, unless they break the language in some way or another. In that case, we'll discuss with you what can be done to have it accepted. We wouldn't like your hard made contribution to go to waste! 18 | 19 | ### Steps for creating good issues or pull requests. 20 | 21 | Please take a moment to understand the LDPL compiler source code if you are submiting a new feature to it. It's made of just three files: `ldpl.cpp` where the compiler is mostly written, `ldpl.h` with variable and function definitions (and an occasional struct) and `ldpl_lib.cpp` that is a library that is included in every binary compiled with the LDPL compiler, that includes definitions for many functions used within the language. 22 | 23 | LDPL is one big `if` statement, with one `if` for every statement of the language. Check the ones that are already written if you want to submit new statements, is really easy. **Test your new statements** before submiting them, please! 24 | 25 | If you are writing documentation, try to make it as clear as possible. It's nice when you don't know nothing about a certain technology and you find a tutorial that explains everything from the ground up in a way you can understand without reading pages and pages of man documentation and shady html files. You love the guy who wrote that tutorial. Be that guy. 26 | 27 | If you are writing LDPL examples, please comment them. Comment every line if you deem it necessary. But make them as clear as possible. 28 | 29 | If you are writing LDPL software, please try to make your code clear as well. It's your code, though, we won't bite you if you don't want to make it nice. 30 | 31 | If you are submiting issues, PLEASE be as clear as possible. Tell us where the error happened, tell us what were you doing, what code triggered it, etc. It just doesn't work when someone posts "please fix this function it doesn't work thx bye". 32 | 33 | If you are submiting a drawing, feel free to take any artistic license you like. "Look mom, I drew the LDPL dinosaur!". 34 | 35 | If you are submiting LDPL fanfiction, please ask yourself why am I doing this. Then kindly create a pull request. 36 | 37 | ### Links to external documentation, mailing lists, or a code of conduct. 38 | 39 | - Official LDPL Website: [https://www.ldpl-lang.org/](https://www.ldpl-lang.org/) 40 | - LDPL Reference: [https://docs.ldpl-lang.org/](https://docs.ldpl-lang.org/) 41 | - LDPL Reddit Community: [https://reddit.com/r/ldpl](https://reddit.com/r/ldpl) 42 | - IRC Channel: #ldpl at [irc.freenode.net](http://irc.freenode.net) 43 | - [Dinosaur Tamers Code](/CODE_OF_CONDUCT) 44 | - You can also send me an email at *mdelrio (at) dc.uba.ar*. 45 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest as build_stage 2 | 3 | RUN apk add --no-cache make g++ cmake 4 | 5 | WORKDIR /app 6 | 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 | 6 | 7 | 8 | 9 | 10 |

11 | 12 | [**LDPL**](https://www.ldpl-lang.org/) is a powerful, general-purpose compiled programming language designed 13 | from the ground up to be excessively **expressive**, **readable**, **fast** and **easy** to learn. 14 | It mimics plain English, inspired by the best aspects of older programming languages like COBOL and FoxPRO. 15 | It even supports UTF-8 out of the box. 16 | 17 | This repository contains the source code and [releases](https://github.com/Lartu/ldpl/releases) of the LDPL compiler. 18 | 19 | ### Example Code 20 | 21 | ```ruby 22 | # Hello There Example 23 | DATA: 24 | name IS TEXT 25 | i IS NUMBER 26 | 27 | PROCEDURE: 28 | DISPLAY "Hello there, what's your name?" 29 | ACCEPT name 30 | FOR i FROM 0 TO 10 STEP 1 DO 31 | DISPLAY "你好, " name "!" CRLF 32 | REPEAT 33 | ``` 34 | 35 | This code greets the user and asks them to enter their name, then it greets them in Chinese, ten times. Easy as pie and super legible. 36 | 37 | LDPL supports multiple datatypes and programming patterns. It is a full-fledged programming language. 38 | Check the [documentation](https://docs.ldpl-lang.org) for more information, and the [official website](https://www.ldpl-lang.org/) 39 | to see other examples, including a BF interpreter and Bellman-Ford's Algorithm! 40 | 41 | ## LDPL Philosophy 42 | 43 | LDPL is a language designed to be easy to understand, learn, write, and use. We believe coding should be like that. Compiling code should be effortless and straightforward: a single, flagless command should be enough to compile any source. Every statement in the language should perform one, and only one, function, independent of the context. The compiler should handle complex, low-level tasks like encoding, sockets, floating-point number comparison, etc., transparently to the user. Difficult things shouldn’t feel difficult. 44 | 45 | We understand that this philosophy may result in longer source code, more verbose statements, and additional steps to reach a solution, but it aims to make coding in LDPL easier and more enjoyable than in other languages. 46 | 47 | As one user once said: 48 | 49 | >*"Usually when I'm programming, I feel like I'm in a big fancy jet and there's a lot of turbulence and it's not going well but then all of a sudden it's smooth air again and the drink cart comes along and I get a ginger ale and it's great. But with LDPL, I feel like I'm a cub scout out in the woods with a box of matches and a hatchet and my Scout's Handbook (the LDPL Docs) just exploring and figuring it out as I go. Whenever I run into a problem I just check my handbook and, sure enough, there's a solution right there waiting for me!"* 50 | 51 | We want LDPL to be a language you’ll love — not because it lets you do many things in one line or because it’s modern, but because it’s designed to stay by your side and reassure you that everything will be okay, even when things look rough. 52 | 53 | ## Installing LDPL 54 | 55 | To install LDPL, clone this repository, navigate to the cloned directory, open a terminal, and run `make` followed by `make install`. That's it! 56 | 57 | You can also optionally run `make man` to re-build the man documentation (PHP is required for this). 58 | This process will install LDPL and its documentation (`man ldpl`) on your system. 59 | LDPL only requires **C++11** to compile and run. 60 | 61 | ### Alternative Installation Methods 62 | 63 | Bear in mind that these other installation methods may install an outdated version of LDPL, as they are maintained by external contributors. 64 | Make sure that the version you are about to install matches the last LDPL release. 65 | 66 | * **🍺 Homebrew**: If you prefer [Homebrew](https://brew.sh), you can install LDPL by running `brew install ldpl`. 67 | * **🐦 Snap**: If you prefer [Snap](https://snapcraft.io/), you can install LDPL by running `snap install ldpl-lang`. 68 | 69 | 70 | ## Learning LDPL and Read the Docs 71 | 72 | ![Learning Dinosaur](https://github.com/Lartu/ldpl/blob/master/images/reference-logo.png) 73 | 74 | If you want to learn how to code in LDPL, there is a brief tutorial available at [https://learnxinyminutes.com/docs/ldpl](https://learnxinyminutes.com/docs/ldpl). Additionally, be sure to check out the examples on the official LDPL website. 75 | 76 | [The **LDPL Documentation** is available here](https://docs.ldpl-lang.org/). 77 | The documentation is also uploaded to the [docs](docs) folder of this repository and can be read and modified from there. 78 | 79 | The LDPL documentation can also be found on your system using `man ldpl` when you install LDPL using `make install`. 80 | The man page is also available in the [man](/man) folder of this repository. 81 | 82 | ## How to use this compiler 83 | 84 | To use the compiler, you must have a C++ compiler installed on your system and ensure it is accessible as `c++` in your PATH. 85 | The LDPL compiler translates LDPL code into C++ code, making this a necessary requirement for it to function. 86 | 87 | Once the compiler is set up, write some LDPL source code — for example, `source.ldpl`. 88 | Then, compile the source code using `ldpl source.ldpl`. The compiled executable binary will be saved as `source-bin`. 89 | For more information about the compiler, run `ldpl -h` or refer to the [docs](https://docs.ldpl-lang.org/#the-ldpl-compiler). 90 | 91 | ### C++ extensions 92 | 93 | LDPL supports extensions written in C++. Extensions are `.cpp`, `.o`, or `.a` files that can be imported into your program. 94 | 95 | For a guide on writing and building C++ extensions, refer to the [LDPL Docs](https://docs.ldpl-lang.org/cppext/). 96 | 97 | ## How can I contribute to LDPL? 98 | 99 | ![Contributing Dinosaur](https://github.com/Lartu/ldpl/blob/master/images/tutorial-logo.png) 100 | 101 | There are many ways to contribute to the LDPL project. 102 | You can fix bugs, open issues, write examples, develop software in LDPL, and more. 103 | Check the [contribution guide](https://www.ldpl-lang.org/contribute.html) for details. 104 | Almost any contribution is welcome — even simply telling your friends about LDPL is an easy and valuable way to help. 105 | 106 | Contributors are expected to adhere to the [LDPL Code of Conduct](https://www.ldpl-lang.org/conduct.html). In short: be nice to everyone. 107 | 108 | ### Community 109 | 110 | If you’d like to chat with us, join the LDPL community in the [LDPL Programming Language **Telegram** Group](https://t.me/ldpllang)! 111 | There's also [r/LDPL](https://reddit.com/r/LDPL) on **Reddit**. 112 | 113 | You are also welcome to create new LDPL channels on any other platform. 114 | 115 | ## Merchandise 116 | 117 | Due to popular demand, [LDPL merchandise is available](https://www.teepublic.com/user/lartu). We’ve partnered with TeePublic to offer a variety of items, from shirts and mousepads to coffee mugs. 118 | 119 | ## Getting Help 120 | 121 | f you have any questions about the LDPL project, feel free to submit an issue in this repository, visit the [LDPL website](https://www.ldpl-lang.org) or join the community channels mentioned in the previous section. Remember, there are no dumb questions — just ask! 122 | 123 | ## 📜 License 124 | 125 | The LDPL Compiler is distributed under the Apache 2.0 License. 126 | All LDPL Dinosaur logos were created by [Lartu](https://github.com/Lartu) and are released under the Creative Commons Attribution 4.0 International (CC BY 4.0) license. 127 | -------------------------------------------------------------------------------- /docs/arithmetic.md: -------------------------------------------------------------------------------- 1 | !!!Note 2 | While this section is up-to-date and complete, it has to be reformated 3 | to be easier on the eyes. All UPPERCASE statement names and code should 4 | be changed to lowercase. 5 | 6 | ## `IN _ SOLVE _` 7 | 8 | The `IN - SOLVE` statement will solve a simple arithmetic expression and place the result in a NUMBER variable. Only `+`, `-`, `/`, `*` operators, NUMBER values, and TEXT values can be used in a MATH-EXPRESSION. Other LDPL arithmetic functions, like `floor` and `modulo`, are not supported by this statement and should be used as standalone statements. TEXT values will be implicitly converted to NUMBERs using the same algorithm as the one used in `store _ in _`. 9 | 10 | Spaces **must** be used to separate numbers, variables and operators. 11 | 12 | As in actual arithmetic, `*` and `/` have higher precedence than `+` and `-` , while parens `()` can be used to group expressions. 13 | 14 | **Syntax:** 15 | 16 | ```coffeescript 17 | IN SOLVE 18 | ``` 19 | 20 | **Example:** 21 | 22 | ```coffeescript 23 | IN myNumVariable SOLVE 1 + 1 24 | ``` 25 | 26 | Will set the value of `myNumVariable` to `2`. 27 | 28 | **Area of Circle:** 29 | 30 | ```coffeescript 31 | DATA: 32 | Radius is NUMBER 33 | Area is NUMBER 34 | 35 | PROCEDURE: 36 | DISPLAY "Enter Radius: " 37 | ACCEPT Radius 38 | 39 | IN Area SOLVE 3.14159 * (Radius * Radius) 40 | DISPLAY "Area is: " Area CRLF 41 | ``` 42 | 43 | Outputs: 44 | 45 | ```text 46 | Enter Radius: 0.5 47 | Area is: 0.7853975 48 | ``` 49 | 50 | ## `FLOOR` 51 | 52 | The `FLOOR` statement rounds down the value of NUMBER-VAR to the nearest lower integer. 53 | 54 | **Syntax:** 55 | 56 | ```coffeescript 57 | FLOOR 58 | ``` 59 | 60 | ## `CEIL` 61 | 62 | The `CEIL` statement rounds up the value of NUMBER-VAR to the nearest higher integer. 63 | 64 | **Syntax:** 65 | 66 | ```coffeescript 67 | CEIL 68 | ``` 69 | 70 | ## `FLOOR _ IN _` 71 | 72 | The `FLOOR _ IN _` statement rounds down the value of NUMBER-VAR to the nearest lower integer 73 | and stores the result in a NUMBER variable. 74 | 75 | **Syntax:** 76 | 77 | ```coffeescript 78 | FLOOR IN 79 | ``` 80 | 81 | ## `CEIL _ IN _` 82 | 83 | The `CEIL _ IN _` statement rounds up the value of NUMBER-VAR to the nearest higher integer 84 | and stores the result in a NUMBER variable. 85 | 86 | **Syntax:** 87 | 88 | ```coffeescript 89 | CEIL IN 90 | ``` 91 | 92 | ## `ADD _ AND _ IN _` 93 | 94 | The `ADD` statement adds two NUMBER values and stores the result in a NUMBER variable. 95 | 96 | **Syntax:** 97 | 98 | ```coffeescript 99 | ADD AND IN 100 | ``` 101 | 102 | ## `SUBTRACT _ FROM _ IN _` 103 | 104 | The `SUBTRACT` statement subtracts two NUMBER values and stores the result in a NUMBER variable. 105 | 106 | **Syntax:** 107 | 108 | ```coffeescript 109 | SUBTRACT FROM IN 110 | ``` 111 | 112 | ## `MULTIPLY _ BY _ IN _` 113 | 114 | The `MULTIPLY` statement multiplies two NUMBER values and stores the result in a NUMBER variable. 115 | 116 | **Syntax:** 117 | 118 | ```coffeescript 119 | MULTIPLY BY IN 120 | ``` 121 | 122 | 123 | ## `DIVIDE _ BY _ IN _` 124 | 125 | The `DIVIDE` statement divides two NUMBER values and stores the result in a NUMBER variable. 126 | 127 | **Syntax:** 128 | 129 | ```coffeescript 130 | DIVIDE BY IN 131 | ``` 132 | 133 | ## `MODULO _ BY _ IN _` 134 | 135 | The `MODULO` statement calculates the remainder of the modulo operation between two NUMBER values and stores the result in a NUMBER variable. 136 | 137 | **Syntax:** 138 | 139 | ```coffeescript 140 | MODULO BY IN 141 | ``` 142 | 143 | 144 | ## `GET RANDOM IN _` 145 | 146 | The `GET RANDOM` statement stores a random value between 0 \(inclusive\) and 1 \(noninclusive\) in a NUMBER variable. 147 | 148 | **Syntax:** 149 | 150 | ```coffeescript 151 | GET RANDOM IN 152 | ``` 153 | 154 | ## `RAISE _ TO _ IN _` 155 | 156 | The `RAISE TO IN ` statement calculates `a^b` and stores the result in `c`. 157 | 158 | **Syntax:** 159 | 160 | ```coffeescript 161 | RAISE TO IN 162 | ``` 163 | 164 | ## `LOG _ IN _` 165 | 166 | The `LOG _ IN _` statement calculates the natural logarithm of a NUMBER and stores the result in a NUMBER variable. 167 | 168 | **Syntax:** 169 | 170 | ```coffeescript 171 | LOG IN 172 | ``` 173 | 174 | ## `SIN _ IN _` 175 | 176 | The `SIN _ IN _` statement calculates the sine of a NUMBER and stores the result in a NUMBER variable. 177 | 178 | **Syntax:** 179 | 180 | ```coffeescript 181 | SIN IN 182 | ``` 183 | 184 | ## `COS _ IN _` 185 | 186 | The `COS _ IN _` statement calculates the cosine of a NUMBER and stores the result in a NUMBER variable. 187 | 188 | **Syntax:** 189 | 190 | ```coffeescript 191 | COS IN 192 | ``` 193 | 194 | ## `TAN _ IN _` 195 | 196 | The `TAN _ IN _` statement calculates the tangent of a NUMBER and stores the result in a NUMBER variable. 197 | 198 | **Syntax:** 199 | 200 | ```coffeescript 201 | TAN IN 202 | ``` -------------------------------------------------------------------------------- /docs/cppext.md: -------------------------------------------------------------------------------- 1 | ## About C++ Extensions 2 | 3 | **C++ extensions** are packages of code written in C++ that can interface with LDPL 4 | code and be added to your LDPL projects. 5 | 6 | Because LDPL programs compile down to C++, there is no need for a translation layer or bridge: extensions can be included directly into LDPL programs and manipulate, share, and access subprocedures and variables natively. All that's needed is a few naming conventions on the C++ side and the use of the `external` syntax for variables and subprocedures on the LDPL side. External variable and sub-procedure naming conventions are explained in detail in the **Naming Schemes** section of this documentation. 7 | 8 | Extensions contain sub-procedures and variables that are considered to be _external_. 9 | This means that they are not part of an LDPL source code and thus must be accessed and called in a different way to what you might be used to. **External sub-procedures** should be called using the `call external` statement. While it is explained in greater detail in the **Control Flow Statements** section of this documentation, the statement works just like the normal `call` statement with the exception that it doesn't accept parameters. 10 | 11 | ```c++ 12 | // In C++ 13 | void CPPFUNCTION(){ 14 | //... 15 | } 16 | ``` 17 | 18 | ```coffeescript 19 | # In LDPL 20 | call external cppFunction 21 | ``` 22 | 23 | **External variables** (declared in a C++ file) should be **re-declared** again in your LDPL data section with the same type they have in the C++ file, appending to their type the `extenal` keyword. This allows programmers to extend LDPL with new features or to wrap 3rd party libraries and re-use their functionality. 24 | 25 | ```c++ 26 | // In C++ 27 | ldpl_number MYNUMBER = 9; 28 | ``` 29 | 30 | ```coffeescript 31 | # In LDPL 32 | data: 33 | myNumber is external number 34 | ``` 35 | 36 | This will be explained in greater detail in the following sections. 37 | 38 | ## Writing C++ Extensions 39 | 40 | If LDPL lacks some feature that C++ might offer, for example graphics or networking, 41 | you might find yourself in the need of writing a C++ extension (provided an extension 42 | for what you are looking for has not been already written, of course). 43 | 44 | Extensions can create variables and functions that are accessible from LDPL through the `call external` and `external` data type keyword, as explained in the previous section. Typically, all you need is a single `.cpp` file that you include from your LDPL source using the `extension` keyword (explained in the **Code Structure** section of this documentation), but you may also include `.o` files, `.a` files, or any combination of them all. 45 | 46 | ### Functions 47 | 48 | To create a function in C++ that can be called from an LDPL program, you must follow four rules: 49 | 50 | 1. The function type must be `void(void)`, ex: `void MY_FUNC();` 51 | 2. The function name must conform to LDPL's external identifier naming conventions explained in the **Naming Schemes** section of this documentation. 52 | 3. The function must not take any parameters. 53 | 4. The function must not return any values. 54 | 55 | Because LDPL does not _know_ the name of any variables or functions declared in non-LDPL files, it allows the programmer to call any function or variable, existing or not, by using the `external` syntax. If the variable or function you are trying to access does not exist, the C++ linker will throw a nasty error. Also, all C++ variable and function names must contain only `A-Z`, `0-9`, and the `_` character. Every character on the LDPL side will be converted to upper case, and non alpha-numeric characters will be converted to an underscore \(`_`\) when referencing the C++ side (again, as stated in the **Naming Schemes** section of this documentation). 56 | 57 | **Example:** 58 | 59 | For example, this function in a file called **add.cpp** 60 | 61 | ```cpp 62 | void PROMPT_ADD() 63 | { 64 | int a, b, sum; 65 | cout << "1st number: "; 66 | cin >> a; 67 | cout << "2nd number: "; 68 | cin >> b; 69 | cout << "sum: " << sum << end; 70 | } 71 | ``` 72 | 73 | can be accessed from LDPL in the following way 74 | 75 | ```coffeescript 76 | external "add.cpp" 77 | 78 | procedure: 79 | call external prompt_add 80 | ``` 81 | 82 | ### Variables 83 | 84 | To create a variable in a C++ extension that can be accessed from LDPL code, you must follow two rules: 85 | 86 | 1. The variable's name must conform to the LDPL external identifier naming convention stated in the **Naming Schemes** section of this documentation. 87 | 2. The C++ type of the variable must match the type used by LDPL for the data type represented by that variable. 88 | 89 | The first rule should be familiar from the previous section: all C++ variable and function names must contain only `A-Z`, `0-9`, and the `_` character. Everything else on the LDPL side will get converted to an underscore \(`_`\). 90 | 91 | For the second rule, the LDPL compiler provides the data types `ldpl_number`, `ldpl_text`, `ldpl_list` and `ldpl_map`. These will be explained in greater detail in the next section. 92 | 93 | **Example:** 94 | 95 | Declaring variables is easy on the C++ side: 96 | 97 | ```cpp 98 | ldpl_text NAME; 99 | ldpl_number AGE; 100 | ldpl_text STREET_ADDRESS; 101 | ``` 102 | 103 | These will be available to an LDPL program when declared as external in its `data:` section: 104 | 105 | ```coffeescript 106 | data: 107 | name is external text 108 | age is external number 109 | street_address is external text 110 | ``` 111 | 112 | ### The LDPL Data Types 113 | 114 | The LDPL compiler provides the data types `ldpl_number`, `ldpl_text`, `ldpl_list` and `ldpl_map`. To use 115 | them, just use them as if they were declared and the LDPL compiler will add the declarations and definitions for you 116 | once the extension is compiled. 117 | 118 | * The `ldpl_number` data type is just a rename for `double`. 119 | * The `ldpl_text` data type is a special UTF-8 string class used just like the regular C++ `std::string` class, with the exception that if you want to get an `std::string` from an `ldpl_text` you can call the `ldpl_text` method `ldpl_text::str_rep()` to get the `std::string` representation of an `ldpl_text`. Just like you would use `std::string::c_str()` to get the C `char*` representation of an `std::string`. 120 | * The `ldpl_list` data type represents an `std::vector` with a special `[]` operator overload. 121 | * The `ldpl_map` data type represents an `std::unordered_map` with special `[]` operator overloadings. 122 | 123 | You can read the definition of these data types on the [ldpl_lib.cpp](https://github.com/Lartu/ldpl/blob/master/src/ldpl_lib.cpp) file of the LDPL source repository. 124 | 125 | ### Accessing Variables in C++ Functions 126 | 127 | Since LDPL and C++ are using the same variable when you use the `external` keyword (say, for example, `MY_VAR` in C++ and external `my-var` in LDPL), any changes you make to the content of said variables are shared. Use them just like you would use any regular variable, both in C++ and LDPL. 128 | 129 | ```cpp 130 | ldpl_number A, B, SUM; 131 | void ADD() 132 | { 133 | SUM = A + B; 134 | } 135 | ``` 136 | 137 | ```coffeescript 138 | data: 139 | a is external number 140 | b is external number 141 | sum is external number 142 | 143 | procedure: 144 | store 100 in a 145 | store 250 in b 146 | call external add 147 | display sum lf 148 | ``` 149 | 150 | Building and running this program will print `350`. 151 | 152 | ## Building C++ Extensions 153 | 154 | Extensions are easy to build: when compiling your LDPL program, use the `extension` keyword (explained in detail in the **Code Structure** section of this documentation) to add `.cpp` files, `.o` files, or `.a` files to your LDPL project. They will be included in your program and become available using the `external` statements. 155 | 156 | If your C++ extension files require extra flags to be passed to the C++ compiler in order to compile \(for example, `-lSDL` when working with SDL\) you can use the `flag` statement (explained in detail in the **Code Structure** section of this documentation) to pass flags to the C++ compiler. 157 | 158 | :::coffeescript 159 | external "otherFile.cpp" 160 | flag "-fpermisive" 161 | flag "-lSDL2" 162 | data: 163 | #... 164 | procedure: 165 | #... 166 | 167 | ## "Hello World" C++ Example 168 | 169 | File **simple.cpp**: 170 | ```cpp 171 | #include 172 | void SIMPLE(){ 173 | std::cout << "Very simple!" << std::endl; 174 | } 175 | ``` 176 | 177 | File **simple.ldpl**: 178 | ```coffeescript 179 | extension "simple.cpp" 180 | 181 | procedure: 182 | call external simple 183 | 184 | ``` 185 | 186 | Console: 187 | 188 | ```bash 189 | $ ldpl simple.ldpl 190 | LDPL: Compiling... 191 | * File(s) compiled successfully. 192 | * Saved as simple-bin 193 | $ ./simple-bin 194 | Very simple! 195 | ``` 196 | 197 | ## External Sub-procedures 198 | 199 | Sometimes when writing C++ Extensions you'll find yourself in the need of declaring a function in C++ but coding it in LDPL. This is the opposite of writing C++ functions and calling them from LDPL, it's writing LDPL sub-procedures and calling them from C++. 200 | 201 | These C++ callable sub-procedures are called **external sub-procedures**, as they can be called from an external medium. 202 | 203 | In order to declare an external sub-procedure, you must first forward-declare it in your C++ source code. Say, for example, that you want to declare a sub-procedure called `helloWorld`. In your C++ you should write the following line: 204 | 205 | ```cpp 206 | void HELLOWORLD(); 207 | ``` 208 | 209 | Note that external sub-procedures **cannot receive any kind of parameters** and must be declared as `void`. You may then call the external sub-procedure from C++ code like this: 210 | 211 | ```cpp 212 | int myCPPFunction(){ 213 | HELLOWORLD(); 214 | return 1; 215 | } 216 | ``` 217 | 218 | Once that's taken care of, you can declare your external sub-procedure as any other sub-procedure in LDPL by prepending the identifier `external` to the sub-procedure declaration: 219 | 220 | ```coffeescript 221 | external sub-procedure myExternalSub 222 | #... 223 | end sub-procedure 224 | ``` 225 | 226 | or just 227 | 228 | ```coffeescript 229 | external sub myExternalSub 230 | #... 231 | end sub 232 | ``` 233 | 234 | These sub-procedures can be called from LDPL like you would call any other sub-procedure, but their names must follow the external identifier naming scheme detailed in the **Naming Schemes** section of this documentation as any other C++ interfacing identifier. 235 | 236 | 237 | ## External Variables 238 | 239 | Variables defined in extensions can be accessed by prefacing their data type declaration with the `external` keyword. This must occur in the **data** section of your LDPL code. Once an external variable is declared, it can be used just like any other LDPL variable. 240 | 241 | **Syntax:** 242 | 243 | ```text 244 | is external 245 | ``` 246 | 247 | **Example:** 248 | 249 | ```coffeescript 250 | data: 251 | rl-prompt is external text 252 | window.size is external number 253 | ``` 254 | 255 | 256 | -------------------------------------------------------------------------------- /docs/data.md: -------------------------------------------------------------------------------- 1 | ## The Data Section 2 | 3 | The **data** section is where global variables are declared (you can use them 4 | anywhere in your program). If no variables are declared, the data section can 5 | be skipped altogether. 6 | 7 | The data section is defined and preceded by the `data:` keyword. 8 | An empty data section looks like this: 9 | 10 | :::coffeescript 11 | data: 12 | 13 | On every line within the data section (that is, on every line after the `data:` 14 | keyword and before the `procedure` keyword) one and only one variable can be 15 | declared. 16 | 17 | The syntax for declaring a variable in LDPL is: 18 | 19 | :::coffeescript 20 | variable-name is data-type 21 | 22 | **Variable naming schemes** and **data types** will be explained later on this 23 | document. 24 | 25 | A data section cannot contain anything but variable declarations, comments and 26 | empty lines. An example data section may end up looking like this: 27 | 28 | :::coffeescript 29 | data: # this is an example data section 30 | foo is number 31 | bar is text map 32 | 33 | # foobar is my number list 34 | foobar is number list 35 | 36 | ## Variable Data Types 37 | 38 | LDPL natively supports the scalar **number** and **text** data types. It also 39 | supports containers of said scalar types: **maps** and **lists**, combined in 40 | any way. 41 | 42 | Variables may be declared, as stated above, using the syntax: 43 | 44 | :::coffeescript 45 | variable-name is data-type 46 | 47 | within the data section. The name of a data type is composed of either a scalar 48 | data type name (**number** or **text**) or a container type plus one or more 49 | scalar or container type names. For example: 50 | 51 | :::coffeescript 52 | foo is number 53 | bar is text 54 | foobar is map of lists of text 55 | 56 | In the case above, **foobar** is a **map of lists of text values**. 57 | 58 | ### The Number Data Type 59 | 60 | The **number** data type, as its name suggests, depicts numeric values. 61 | It's recommended that it be represented internally as a binary64 62 | double-precision floating-point format number as defined by the IEEE 754. 63 | 64 | Both variables and numeric constants can be members of the number type. 65 | 66 | Valid number literals must begin with a decimal value (for example 5 or 0.12, 67 | .12 wouldn't be a valid number) and may be preceded by a minus sign for 68 | negative numbers (-5, -567.912). Numbers may not be preceded by a plus sign 69 | (+5 is not a valid number literal). The literal -0 is implicitly transformed 70 | into 0. 71 | 72 | :::coffeescript 73 | -231897.123 # is an example number 74 | 75 | ### The Text Data Type 76 | 77 | The **text** data type, as its name suggests, represents alphanumeric strings. 78 | In the interest of supporting as many locales as possible, texts should be utf-8 79 | encoded to be compatible with Unicode. A text maximum length for text values is 80 | explicitly not defined and it should be limited only by the amount of available 81 | memory on the system. Strings in LDPL are enclosed between two "quotes". 82 | 83 | :::coffeescript 84 | "This is an example string!" 85 | 86 | LDPL strings may contain multiple escape sequences / control characters in 87 | them. Each escape sequence counts as only one character. The available escape 88 | sequences are: 89 | 90 | * `\a` = alert (bell) 91 | * `\b` = backspace 92 | * `\t` = horizontal tab 93 | * `\n` = newline / line feed 94 | * `\v` = vertical tab 95 | * `\f` = form feed 96 | * `\r` = carriage return 97 | * `\e` = non-standard GCC escape 98 | * `\0` = null byte 99 | * `\\` = \ character 100 | * `\"` = " character 101 | 102 | For example, the string `"hello,\nworld"` will be displayed as 103 | 104 | :::text 105 | hello, 106 | world 107 | 108 | when printed to the console. 109 | 110 | ### The List Data Type 111 | 112 | The **list** data type is a collection of number or text values, or containers 113 | of possibly more containers of said types. Values can be pushed to lists and 114 | then be accessed and modified using the `:` operator. List indexes consist of 115 | integer numbers. The first index of a list is index 0, and the rest count up to 116 | the length of the list minus one. 117 | 118 | Lists, as collections of number or text values (or collections of said types), 119 | can only have one defined type at any given time: text or number. A single list 120 | is not capable of storing both numeric and alphanumeric values. 121 | 122 | :::coffeescript 123 | data: 124 | foo is number list # This is a list 125 | 126 | procedure: 127 | push 10 to foo 128 | display foo:0 lf 129 | 130 | !!!note 131 | The `display` statement prints values to the screen. While it will be 132 | explained in more detail later, the line 133 | 134 | :::python 135 | `display foo:0 lf` 136 | 137 | prints 138 | the value of `foo:0` (the index `0` of the list `foo`) followed by 139 | a line break (`lf`). 140 | 141 | In most other languages, you may have to declare a list and specify how many 142 | elements or components it contains. In such languages, the declaration causes a 143 | contiguous block of memory to be allocated for that many elements. LDPL lists, 144 | however, are different: they are dynamic. This means that you can store as many 145 | values as you want in a single list without fear of running out of place (of 146 | course, this is limited by the memory allocated by your operating system for 147 | your LDPL program). 148 | 149 | Suppose you store the values `"hi"`, `"there"`, `"I love"` and 150 | `"LDPL, it's great!"` in a `text list`, in that particular order. Then the 151 | contents of the list and the **index** associated with each **element** will be 152 | 153 | | Index | Element | 154 | | :---: | :---: | 155 | | 0 | `"hi"` | 156 | | 1 | `"there"` | 157 | | 2 | `"I love"` | 158 | | 3 | `"LDPL, it's great!"` | 159 | 160 | We have shown the pairs in order because their order is relevant: if you added 161 | a new element to the list, it would be inserted after the last element, thus 162 | being associated with index `4`. 163 | 164 | To add values to a list, you must first push them to the list. For example, 165 | if you want to add the numbers `10`, `20` and `30` to a `number list`, your 166 | code should look like this: 167 | 168 | :::coffeescript 169 | data: 170 | myList is number list 171 | procedure: 172 | push 10 to myList # 10 is stored in index 0 of myList 173 | push 20 to myList # 20 is stored in index 1 of myList 174 | push 30 to myList # 30 is stored in index 2 of myList 175 | 176 | 177 | Values in lists can be stored and accessed just like any other variable 178 | (see the **store - in** statement for further details) using the `:` operator. 179 | This operator indicates what index of the list we are writing to or reading 180 | from. Here we declare a `number list` and store the values `5` and `-10.2` in 181 | it, and then replace the number `5` by the number `890`: 182 | 183 | :::coffeescript 184 | data: 185 | myList is number list 186 | procedure: 187 | push 5 to myList 188 | # 5 is stored in index 0 of myList 189 | push -10.2 to myList 190 | # -10.2 is stored in index 1 of myList 191 | store 890 in myList:0 192 | # We store 890 in index 0 of myList, thus replacing the 5 193 | 194 | !!!note 195 | The `push` statement adds a value at the end of a list. Also, the `store 196 | - in` statement stores a value in a variable. These two statements will 197 | be explained in more detail in their respective sections of this document. 198 | 199 | Please note that as a list is **variable** that's a collection of values, a 200 | single index of a list is a variable in itself. This means that any subindex 201 | of a list that resolves to a scalar value be used in any position where you 202 | could use a variable of the same type of that value. So, if you have something 203 | like this: 204 | 205 | ```coffeescript 206 | store in 207 | ``` 208 | 209 | You could use a `number list` with a defined sub-index \(for example, in the 210 | example above, `myList:0`\) where it says number-var, just like in 211 | the `store - in` examples in the code extracts above. 212 | 213 | In the **list statements** section, you'll find a collection of statements that 214 | can be used to work with lists. 215 | 216 | ### The Map Data Type 217 | 218 | The **map** data type is a collection of number or text values. Maps 219 | superficially resemble lists but with fundamental differences. The biggest one 220 | is that any number or string in LDPL may be used as an array index, not just 221 | consecutive integers. Also, values in maps have no order. 222 | 223 | :::python 224 | data: 225 | foo is number map # This is a map 226 | 227 | procedure: 228 | store 19 in foo:"hi there!" 229 | display foo:"hi there!" lf 230 | 231 | Maps, as collections of number or text values (or collections of said types), 232 | can only have one defined type at any given time: text or number. A single map 233 | is not capable of storing both numeric and alphanumeric values. 234 | 235 | Unlike lists, maps are **associative**. This means that each map is a 236 | collection of pairs: a key and its corresponding element. For example, 237 | you could have a `number map` with the following contents: 238 | 239 | | Key | Element | 240 | | :---: | :---: | 241 | | 4 | 30 | 242 | | 2 | 10 | 243 | | "Hi there!" | -56.3 | 244 | | "99ldplrocks89" | 0 | 245 | 246 | We have shown the pairs in jumbled order because their order is irrelevant. 247 | One advantage of maps is that new pairs can be added at any time. maps can be 248 | sparse: they can have missing keys \(say for example you have keys 1 and 5, but 249 | don't have keys 2, 3 and 4\). Another consequence of maps is that the keys 250 | don't necessarily have to be positive integers. Any number, or even a string, 251 | can be a key. 252 | 253 | Values in maps can be stored and accessed just like any other variable 254 | (see the **store - in** statement for further details) using the `:` operator. 255 | This operator indicates what key of the map we are writing to or reading from. 256 | Here we declare a `number map` and store the values `5` and `-10.2` in the 257 | keys `1` and `5`, respectively. 258 | 259 | :::coffeescript 260 | data: 261 | myMap is number map 262 | procedure: 263 | store 5 in myMap:1 #Stores 5 in key 1 of myMap 264 | store -10.2 in myMap:5 #Stores -10.2 in key 5 of myMap 265 | 266 | As stated before, map keys don't always have to be constant numbers. 267 | They can also be number variables, text and text variables, or even sub-indexes 268 | of lists or elements from other maps. For example: 269 | 270 | :::coffeescript 271 | data: 272 | myMap is number map 273 | myOtherMap is number map 274 | myVar is number 275 | 276 | procedure: 277 | store 17 in myVar 278 | store 1 in myMap:"hello" 279 | #Stores 1 in key "hello" of myMap 280 | store 7 in myMap:myVar 281 | #Stores 7 in a key equal to the current value of myVar 282 | store 3 in myMap:myOtherMap:4 283 | #Stores 3 in a key of equal value to the key of myMap with value equal to 284 | #key 4 of myOtherMap 285 | 286 | In fact, when you use a number value as a subindex for a map, it is silently 287 | casted into a text value. For example, `myMap:1` will be interpreted \(an thus, 288 | the same\) as `myMap:"1"`. 289 | 290 | Please note that as a map is **variable** that's a collection of values, 291 | a single key of a map is a variable in itself. This means that any key of a 292 | map that resolves to a scalar value can be used in any position where you could 293 | use a variable of the same type of that value. So, if you have something like 294 | this: 295 | 296 | ```coffeescript 297 | store in 298 | ``` 299 | 300 | You could use a `number map` with a particular key where it says number-var, 301 | just like in the `store - in` examples in the code extracts above 302 | \(for example `myMap:"hello"`\). 303 | 304 | As you'll see in the **Default Variable Values** section, you can access 305 | undeclared keys of a map, just like if they were declared. 306 | See the following example: 307 | 308 | ```coffeescript 309 | data: 310 | myMap is number map 311 | procedure: 312 | display myMap:99 313 | ``` 314 | 315 | In the example above, `0` will be printed and no errors displayed during 316 | compilation, even though the key `99` of `myMap` hasn't been explicitly 317 | declared. This is because when you try to access an element that hasn't 318 | been declared yet, LDPL declares it for you and initializes it to its type 319 | default value. 320 | 321 | It's important to note that this very feature is a double-edged weapon. While 322 | you can use it to access uninitialized map keys, you cannot check if a value 323 | exists in a map without initializing it if it wasn't there before. Statements 324 | like **store key count of** and **store keys of** are provided as means to 325 | overcome this situation. 326 | 327 | In the **map statements** section, you'll find a collection of statements that 328 | can be used to work with maps. 329 | 330 | !!!warning 331 | In older versions of LDPL, **map**s were called **vector**s. 332 | Starting from LDPL 3.1.0 Diligent Dreadnoughtus, they have been renamed 333 | to reflect the real data structure they represent. While it might still be 334 | possible to call them vectors in code, and legacy code that declares maps 335 | as vectors is and will continue to be supported, this nomenclature is 336 | **deprecated** and shouldn't be used anymore. 337 | 338 | ## Multicontainers 339 | 340 | As stated before, _"The name of a data type is composed of either a scalar 341 | data type name (**number** or **text**) or a container type plus one or more 342 | scalar or container type names."_ This means that you can declare variables like: 343 | 344 | :::coffeescript 345 | myVariable is list of lists of text 346 | myOtherVariable is map of lists of maps of maps of lists of maps of numbers 347 | pleaseStop is list of maps of maps of lists of maps of maps of maps of lists of lists of maps of lists of lists of text 348 | 349 | These variables are called **multicontainers**. Multicontainers hold other 350 | containers, which can hold more containers or scalar values to an arbitrary 351 | depth. For example, a `map of lists of text` is a **map** that holds **lists** 352 | of **text** values. You may access a value stored in a `map of lists of text` 353 | like so: 354 | 355 | :::coffeescript 356 | display foo:"hi":0 357 | 358 | where `hi` is the key used to access a list stored in the map, and `0` the 359 | index used to access a text value stored in the list that was stored in the 360 | map. 361 | 362 | If you want to push a list to a `list of lists` or a map to a `list of maps` you 363 | must use the `push list to _` and `push map to _` statements, respectively: 364 | 365 | :::coffeescript 366 | data: 367 | listOfMaps is list of maps of texts 368 | listOfLists is list of lists of texts 369 | procedure: 370 | push map to listOfMaps 371 | store "Hello!" in listOfMaps:0:"hi!" 372 | # Pushes a map to listOfMaps and then stores 373 | # the value "Hello!" in the key "hi!" of the pushed map 374 | push list to listOfLists 375 | push "Hello!" listOfLists:0 376 | # Pushes a map to listOfLists and then pushes 377 | # the value "Hello!" at index 0 of the pushed list 378 | 379 | These will be explained in more detail in their respective sections. 380 | 381 | The LDPL compiler supports plural data-type names, as shown above. One can 382 | use `numbers` instead of `number`, `texts` instead of `text`, 383 | `lists` instead of `lists` and `maps` 384 | instead of `map`. A `list of list of number` is the same as a `list of lists of numbers`. 385 | 386 | !!!Note 387 | In LDPL 4.3, multicontainers were introduced with a right-to-left syntax. 388 | A `map of list of text` was defined as `text list map`. This syntax is 389 | still supported. 390 | 391 | ## Default Type Values 392 | 393 | In LDPL, each variable is initialized with a value by default. This means 394 | that when you declare a variable, it will, by default, hold this value 395 | until it's changed. 396 | 397 | * **Number** variables are initialized with the value `0`. Each element of 398 | a **number map** is a number variable, and thus also initialized to `0`. 399 | * **Text** variables are initialized to the empty string `""`. The same goes 400 | for **text maps**, where each element it contains is also initialized to `""`. 401 | * **Lists** are initialized empty by default and trying to access a 402 | non-existing index will result in an error. 403 | * Keys of **Maps of Lists** (`map of list`) are declared as empty lists of the 404 | scalar type of the multicontainer by default. 405 | 406 | ## Predeclared Variables 407 | 408 | Some variables in LDPL are already declared for you without you having to 409 | declare them. These variables are the **argv** text list, and the **errorcode** 410 | and **errortext** variables. 411 | 412 | ### The *argv* list variable 413 | 414 | Every LDPL program comes with the `argv` **text list** variable declared by 415 | default. 416 | 417 | If you pass command line arguments to your LDPL compiled program \(running, for 418 | example, something like `myBinary argument1 argument2)`, the values stored 419 | in the `argv` list \(_argument vector_\) will be the values of each argument 420 | passed \(in this case, `"argument1"` will be stored in `argv:0` and 421 | `"argument2"` in `argv:1`\). 422 | 423 | !!!hint 424 | Given that `argv` is a **text list**, the values passed as arguments are 425 | always stored as **text**, even numbers. 426 | 427 | Naturally, if no arguments are passed to the program, the `argv` list will be 428 | empty. 429 | 430 | ### The *errorcode* and *errortext* variables 431 | 432 | Some LDPL operations may fail when executed. Maybe you tried loading a file 433 | that wasn't there or getting the ASCII value of a multi-byte emoji. These 434 | operations make use of the `errorcode` and `errortext` variables to 435 | tell you if they ran successfully or not. 436 | 437 | The `errorcode` and `errortext` variables come declared by default. 438 | Some statements may modify their values to express their results. 439 | 440 | The `errorcode` variable is a **number** variable. It will hold the value 0 441 | if the statement ran successfully, and any other number if it did not. 442 | 443 | The `errortext` variable is a **text** variable that will be empty if the 444 | statement ran successfully. If it did not, it will store a human readable 445 | description of what went wrong. 446 | 447 | The `errorcode` and `errortext` variables can be read and written like any other 448 | LDPL variable. 449 | 450 | !!!warning 451 | When handling error checks, please bear in mind that the content of the 452 | `errortext` variable may change in future releases of LDPL. The value 453 | stored in `errorcode`, however, will not change and so that's the value 454 | that should be used to check whether an operation ran successfully or not. 455 | -------------------------------------------------------------------------------- /docs/flow.md: -------------------------------------------------------------------------------- 1 | # Control Flow Statements 2 | 3 | !!!Note 4 | While this section is up-to-date and complete, it has to be reformated 5 | to be easier on the eyes. All UPPERCASE statement names and code should 6 | be changed to lowercase. 7 | 8 | ## `STORE _ IN _` 9 | 10 | The `STORE` statement assigns a value to a variable. 11 | 12 | **Syntax:** 13 | 14 | :::coffeescript 15 | STORE IN 16 | 17 | 18 | **Type Conversion Notes:** 19 | 20 | If the value to be stored is NUMBER and it's to be stored in a TEXT variable, 21 | the value will be converted to text, so `15` will be turned into `"15"`. If the 22 | value to be stored is a TEXT value two things can happen. If it contains any 23 | non-numeric characters \(for example letters, or more than one minus sign or 24 | more than one decimal point, for example `"--1.2"` or `"15a"`\) the conversion 25 | will fail and 0 will be stored in the NUMBER variable. If the TEXT contains a 26 | proper number, though, for example `"-416.419"` or `"89"` it will be converted 27 | to its number equivalent and stored in the variable. If a string literal depicting 28 | a number is preceded by leading zeros, these will be trimmed \(turning `0005` 29 | into `5`, `-0002.3` into `-2.3` and `00.23` into `0.23`\). 30 | 31 | ## `IF _ IS _ THEN` 32 | 33 | The `IF` statement evaluates if the condition given is positive. If it is, the code in the positive branch is executed. If it is not, the code in the negative branch is executed \(if available\). Execution then continues normally. 34 | 35 | **Syntax:** 36 | 37 | ```coffeescript 38 | IF THEN 39 | #Code goes here (positive branch) 40 | ELSE 41 | #Code goes here (negative branch) 42 | END IF 43 | ``` 44 | 45 | or 46 | 47 | ```coffeescript 48 | IF THEN 49 | #Code goes here (positive branch) 50 | END IF 51 | ``` 52 | 53 | The `` may be a relational operator between two values with the same type: 54 | 55 | * ` IS ` 56 | * ` IS ` 57 | * ` IS ` 58 | * ` IS ` 59 | * ` IS ` 60 | * ` IS ` 61 | 62 | **Possible values of `REL-OP-A`:** 63 | 64 | * `EQUAL TO` 65 | * `NOT EQUAL TO` 66 | * `GREATER THAN` 67 | * `LESS THAN` 68 | * `GREATER THAN OR EQUAL TO` 69 | * `LESS THAN OR EQUAL TO` 70 | 71 | **Possible values of `REL-OP-B`:** 72 | 73 | * `EQUAL TO` 74 | * `NOT EQUAL TO` 75 | 76 | For containers, both values must have the same type. You **cannot** compare, for example, 77 | a `list of lists of numbers` with a `list of lists of lists of numbers` or a `number map` 78 | with a `text map`. 79 | 80 | The `` may also be a membership operator: 81 | 82 | * ` IN ` 83 | * ` IN ` 84 | 85 | When using a membership operator, if the second value is a list, it checks if the first value 86 | is contained within that list. If the second value is a map, however, it checks if the first 87 | value is contained within the keys of that map. 88 | 89 | The first value must always be a scalar value, you **cannot** check if, for example, a 90 | `map of numbers` is contained within a `list of maps of numbers`. 91 | 92 | You can also write **compound conditions** using `AND`, `OR` and parenthesis: 93 | 94 | * ` AND ` is positive if both conditions are positive 95 | * ` OR ` is positive if any of the conditions is positive 96 | * `( )` is positive if the condition is positive and it's used to alter the default precedence 97 | 98 | The `AND` has higher precedence than `OR`, and the conditions inside parenthesis will be evaluated first. That means that `C1 AND C2 OR C3` is the same as `( C1 AND C2 ) OR C3`, but you can make the `OR` evaluate first if you write `C1 AND ( C2 OR C3 )`. Mind the spaces surrounding the parenthesis: `()` is **not** a 99 | valid condition, while `( )` is. 100 | 101 | Both `AND` and `OR` perform short-circuit evaluation: If the first operand of an AND is negative the second will not be evaluated and the `AND` condition is determined as negative. If the first operand of an OR is positive the second will not be evaluated and the `OR` condition is determined as positive. 102 | 103 | For example: 104 | 105 | ```coffeescript 106 | DATA: 107 | names IS TEXT LIST 108 | length IS NUMBER 109 | PROCEDURE: 110 | GET LENGTH OF names IN length 111 | IF length IS GREATER THAN 0 AND ( names:0 IS EQUAL TO "Alice" OR names:0 IS EQUAL TO "Bob" ) THEN 112 | #Code 113 | END IF 114 | ``` 115 | 116 | The `names` list is empty, so the `length is greater than 0` condition is negative, and the second one is not evaluated \(and the execution continues after the `END IF`\). Thanks to short-circuit evaluation `names:0` is not evaluated and we don\`t get an index out of range runtime error! 117 | 118 | ## `ELSE IF _ IS _ THEN` 119 | 120 | The `ELSE IF` statement is equivalent to writing an `IF` statement inside the `ELSE` statement of another `IF` statement, but shorter. Must be used after an IF statement and before `END IF` or `ELSE`. 121 | 122 | **Syntax:** 123 | 124 | All the different `IF` variants of the [IF statement](./) apply, just with `ELSE` added before them. 125 | 126 | **Example:** 127 | 128 | ```coffeescript 129 | DATA: 130 | name IS TEXT 131 | PROCEDURE: 132 | STORE "Mike" IN name 133 | IF name IS equal to "John" THEN 134 | DISPLAY "Hello there, John!" CRLF 135 | ELSE IF name IS equal to "Mike" THEN 136 | DISPLAY "Hello there, Mike!" CRLF 137 | ELSE IF name IS equal to "Robert" THEN 138 | DISPLAY "Hello there, Robert!" CRLF 139 | ELSE 140 | DISPLAY "I don't know you, " name CRLF 141 | END IF 142 | ``` 143 | 144 | 145 | 146 | ## `WHILE _ IS _ DO` 147 | 148 | The `WHILE` statement evaluates if the condition given is positive. While it is, the code between the `WHILE` and `REPEAT` statements is repeatedly ran. 149 | 150 | **Syntax:** 151 | 152 | ```coffeescript 153 | WHILE DO 154 | #Code goes here 155 | REPEAT 156 | ``` 157 | 158 | The `` that you can use are the same as the [IF](if-is-then/) statement ones. 159 | 160 | ## `FOR _ FROM _ TO _ STEP _ DO` 161 | 162 | The `FOR` statement repeatedly run the code in its body a number of times, given a `counter` variable, the `start` of the range, its `end` and a `step`. 163 | 164 | When the loop starts, `start` is assigned to `counter` and starts an iteration, evaluating a condition. The condition is `counter < end` if `step >= 0` and `counter > end` if `step < 0`. If the condition passes, the code in the body of the `FOR` is executed, otherwise the loop will end. After the code is ran the `counter` is incremeted by `step` and a new iteration is started \(checking the condition and so on\). 165 | 166 | **Syntax:** 167 | 168 | ```coffeescript 169 | FOR FROM TO STEP DO 170 | #Code goes here 171 | REPEAT 172 | ``` 173 | 174 | **Example:** 175 | 176 | ```coffeescript 177 | DATA: 178 | i IS NUMBER 179 | PROCEDURE: 180 | FOR i FROM 0 TO 10 STEP 2 DO 181 | DISPLAY i " " 182 | REPEAT 183 | # Will display "0 2 4 6 8 10" 184 | ``` 185 | 186 | ## `FOR EACH _ IN _ DO` 187 | 188 | The `FOR EACH` statement repeatedly run the code in its body for every element in a given `LIST` or `MAP`. At the start of each iteration an element of the collection is assigned to a variable matching its type. This even works with multicontainers. For example, you can iterate a `NUMBER MAP LIST` using a `NUMBER MAP` as the iteration variable. 189 | 190 | If the collection is a `LIST`, its elements will be iterated increasingly from index `0`, while in the case of a `MAP` all the elements will be iterated in no particular order. 191 | 192 | !!! info 193 | Starting from LDPL 5 *Groovy Gualicho*, `FOR EACH` iterates over the may keys, instead of its elements. The iteration variable type must thus be `TEXT`. 194 | 195 | **Syntax:** 196 | 197 | ```coffeescript 198 | FOR EACH IN DO 199 | #Code goes here 200 | REPEAT 201 | ``` 202 | 203 | **Example:** 204 | 205 | ```coffeescript 206 | DATA: 207 | letter IS TEXT 208 | letters IS TEXT LIST 209 | PROCEDURE: 210 | PUSH "L" TO letters 211 | PUSH "D" TO letters 212 | PUSH "P" TO letters 213 | PUSH "L" TO letters 214 | FOR EACH letter IN letters DO 215 | DISPLAY letter 216 | REPEAT 217 | # Will display "LDPL" 218 | ``` 219 | 220 | ## `BREAK` 221 | 222 | The `BREAK` statement breaks the execution of the innermost `WHILE`, `FOR` or `FOR EACH` loop. Will throw a compiler error if used outside one. 223 | 224 | **Syntax:** 225 | 226 | ```coffeescript 227 | BREAK 228 | ``` 229 | 230 | ## `CONTINUE` 231 | 232 | The `CONTINUE` statement jumps to the next iteration of the innermost `WHILE` or `FOR` loop. Will throw a compiler error if used outside one. 233 | 234 | **Syntax:** 235 | 236 | ```coffeescript 237 | CONTINUE 238 | ``` 239 | 240 | ## `CALL SUB-PROCEDURE` 241 | 242 | The `CALL SUB-PROCEDURE` statement executes a SUB-PROCEDURE. Once the SUB-PROCEDURE returns, execution continues from the line following the `CALL SUB-PROCEDURE`. 243 | 244 | **Syntax:** 245 | 246 | ```coffeescript 247 | CALL SUB-PROCEDURE 248 | CALL SUB-PROCEDURE WITH 249 | ``` 250 | 251 | Or 252 | 253 | ```coffeescript 254 | CALL 255 | CALL WITH 256 | ``` 257 | 258 | Of course, a SUB-PROCEDURE must be declared **somewhere** in your program for you to call it. 259 | 260 | If the SUB-PROCEDURE you call doesn't have any declared parameters, you must call it without the `WITH` keyword, otherwise you must include it and pass all required parameters after it, in the same order declared in the `PARAMETERS` section of the SUB-PROCEDURE. 261 | 262 | ## `RETURN` 263 | 264 | The `RETURN` statement returns from a SUB-PROCEDURE. Will throw a compiler error if used outside one. 265 | 266 | **Syntax:** 267 | 268 | ```coffeescript 269 | RETURN 270 | ``` 271 | 272 | ## `EXIT` 273 | 274 | The `EXIT` statement ends execution of the program. 275 | 276 | **Syntax:** 277 | 278 | ```coffeescript 279 | EXIT 280 | ``` 281 | 282 | ## `WAIT _ MILLISECONDS` 283 | 284 | The `WAIT` statement pauses the execution of a program for the given number of milliseconds. 285 | 286 | **Syntax:** 287 | 288 | ```coffeescript 289 | WAIT MILLISECONDS 290 | ``` 291 | 292 | ## `GOTO and LABEL` 293 | 294 | > "If you want to go somewhere, goto is the best way to get there." 295 | > -- Ken Thompson 296 | 297 | The `GOTO` statement performs a **one-way transfer** of control to a line of code marked by a `LABEL` statement. In lame man terms, the execution jumps to the line where the wanted `LABEL` is found and continues from there. 298 | 299 | While maligned by [Edsger W. Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra) and his cohorts, `GOTO` is very useful in many situations. Its reputation is undeserved and mostly perpetuated by people that don't understand the origins of the criticism or how the statement can be used. 300 | 301 | You also can't make a COBOL-_esque_ language without `GOTO`, so \(due to popular request\) we've added it to the language. 302 | 303 | **Syntax:** 304 | 305 | ```text 306 | LABEL 307 | ``` 308 | 309 | ```text 310 | GOTO 311 | ``` 312 | 313 | !!!hint 314 | Label names follow the naming rules stated in the **Identifier Naming Schemes** section of this documentation. 315 | 316 | 317 | **Example:** 318 | 319 | ```coffeescript 320 | PROCEDURE: 321 | GOTO start 322 | 323 | LABEL start 324 | display "> starting..." crlf 325 | 326 | GOTO ending 327 | 328 | LABEL middle 329 | display "> entering the middle section..." crlf 330 | 331 | sub cool-code 332 | GOTO cool 333 | display "hmm... is this cool?" crlf 334 | LABEL cool 335 | display "wow, yeah! cool code!" crlf 336 | end sub 337 | 338 | LABEL ending 339 | CALL cool-code 340 | display "> that's the end" crlf 341 | ``` 342 | 343 | In the output of this program you can see the `middle` LABEL and the start of the `cool-code` SUB-PROCEDURE are skipped: 344 | 345 | ```text 346 | > starting... 347 | wow, yeah! cool code! 348 | > that's the end 349 | ``` 350 | 351 | In order to keep `GOTO` from turning your source into _unmaintainable spaghetti code_, both your `GOTO` statements and the `LABEL`s they jump to have to be declared together in the same sub-procedure or in the main code body of an LDPL program. You can't `goto` across sub-procedures or into them, or anything like that. 352 | 353 | ## `CREATE STATEMENT _ EXECUTING _` 354 | 355 | The `CREATE STATEMENT` statement lets you add custom statements to LDPL that execute SUB-PROCEDUREs. 356 | 357 | **Syntax:** 358 | 359 | ```coffeescript 360 | CREATE STATEMENT EXECUTING 361 | ``` 362 | 363 | The `TEXT` describes the new statement syntax and must contain tokens separated by whitespace. Each token can be a keyword, which is a word with `A-Z` characters \(preferably in English\), or `"$"`, a character that marks where parameters are passed. At least one keyword token is required and the number of `"$"` tokens must be the same as the number of parameters of the SUB-PROCEDURE you pass after `EXECUTING`. For example, a valid `TEXT` is `"DISPLAY $ $ TIMES"` if the SUB-PROCEDURE has exactly two parameters. The SUB-PROCEDURE must be declared before creating the statement. 364 | 365 | After a statement is created you can use it like any other LDPL statement in `PROCEDURE` sections, just write a line with all the tokens of the `TEXT` in the same order but placing values instead of `"$"`. The types of the values must be the same as the parameter types of the SUB-PROCEDURE the statement executes following the same order. Using the new statement will produce the same effect as `CALL`ing the SUB-PROCEDURE \(parameters are passed by reference too\). 366 | 367 | You can create two different statements with same `TEXT` and use both if at least one of the parameter types are different in each SUB-PROCEDURE, because the resulting syntaxes will differ from each other. Using this you can create two versions of the same statements dealing with different parameter types, like the first example shows. 368 | 369 | Bear in mind that a line in your program could match more than one statement: If all of them were created with `CREATE STATEMENT`, the one that was created first will be executed. If one of the them is a LDPL built-in statement, this will be executed. For example, if you create `"DISPLAY $ $ TIMES"`, declare a variable `TIMES` and use the line `DISPLAY "Hi!" 3 TIMES`, the LDPL `DISPLAY` statement will be executed, because the line matches its syntax. This is illustrated in the second example. 370 | 371 | **Example 1:** 372 | 373 | ```coffeescript 374 | PROCEDURE: 375 | SUB-PROCEDURE displayNValueTimes 376 | PARAMETERS: 377 | value is number 378 | times is number 379 | LOCAL DATA: 380 | i is number 381 | PROCEDURE: 382 | FOR i FROM 0 TO times STEP 1 DO 383 | DISPLAY value " " 384 | REPEAT 385 | END SUB-PROCEDURE 386 | CREATE STATEMENT "DISPLAY $ $ TIMES" EXECUTING displayNValueTimes 387 | # Syntax for this new statement: DISPLAY TIMES 388 | 389 | SUB-PROCEDURE displayTValueTimes 390 | PARAMETERS: 391 | value is text 392 | times is number 393 | LOCAL DATA: 394 | i is number 395 | PROCEDURE: 396 | FOR i FROM 0 to times STEP 1 DO 397 | DISPLAY value " " 398 | REPEAT 399 | END SUB-PROCEDURE 400 | CREATE STATEMENT "DISPLAY $ $ TIMES" EXECUTING displayTValueTimes 401 | # Syntax for this new statement: DISPLAY TIMES 402 | 403 | # We can imagine that we have only one new statement: 404 | # DISPLAY TIMES 405 | 406 | DISPLAY 100 2 TIMES # This executes: CALL displayNValueTimes with 100 2 407 | DISPLAY "Hi!" 3 TIMES # This executes: CALL displayTValueTimes with "Hi!" 3 408 | 409 | # This program displays "100 100 Hi! Hi! " 410 | ``` 411 | 412 | **Example 2:** 413 | 414 | ```coffeescript 415 | DATA: 416 | times is number 417 | PROCEDURE: 418 | SUB-PROCEDURE displayTValueTimes 419 | PARAMETERS: 420 | value is text 421 | times is number 422 | LOCAL DATA: 423 | i is number 424 | PROCEDURE: 425 | FOR i FROM 0 to times STEP 1 DO 426 | DISPLAY value " " 427 | REPEAT 428 | END SUB-PROCEDURE 429 | CREATE STATEMENT "DISPLAY $ $ TIMES" EXECUTING displayTValueTimes 430 | # Syntax for this new statement: DISPLAY TIMES 431 | 432 | DISPLAY "Hi!" 3 TIMES # This executes the LDPL DISPLAY statement! 433 | # This program displays "Hi!30" because times is equal to 0 434 | ``` 435 | 436 | ## `CALL EXTERNAL _` 437 | 438 | !!!hint 439 | This section talks about external sub-procedure calling for **C++ Extensions**. If 440 | you have not read the section on C++ Extensions yet, ignore this and then come 441 | back later. 442 | 443 | 444 | The `CALL EXTERNAL` statement executes a SUB-PROCEDURE defined in an extension to LDPL, typically in C++. It otherwise operates the same as `CALL SUB-PROCEDURE`, except that external SUB-PROCEDURES do not receive parameters. 445 | 446 | **Syntax:** 447 | 448 | ```text 449 | CALL EXTERNAL 450 | ``` 451 | 452 | **Example:** 453 | 454 | ```text 455 | CALL EXTERNAL http-get 456 | ``` -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # LDPL Docs 2 | 3 | ## Introduction 4 | 5 | The **LDPL Community** has compiled this document with the desire to teach 6 | and standardize the **LDPL** programming language. This document contains the 7 | specification for the **LDPL** programming language, as well as explanations, 8 | instructions and examples for every feature included in it. 9 | 10 | ![LDPL](https://raw.githubusercontent.com/Lartu/ldpl/master/images/reference-logo.png) 11 | 12 | Feedback, corrections and suggestions are welcome, both on the main 13 | [LDPL repository](https://github.com/Lartu/ldpl) 14 | or by e-mail to [martin@ldpl-lang.org](mailto:martin@ldpl-lang.org). 15 | You can join the LDPL community at [r/ldpl](https://reddit.com/r/ldpl) 16 | or via IRC on irc.freenode.net channel #ldpl. 17 | 18 | The source code for this documentation is available in the LDPL repository. 19 | 20 | ## About LDPL 21 | 22 | **LDPL** is a powerful compiled programming language designed from the 23 | ground up to be expressive, readable, fast and easy to learn. It mimics plain 24 | English, in the likeness of the good parts of older programming languages like 25 | COBOL, with the desire that it can be understood by anybody. LDPL was designed 26 | to run on Unix systems, including AMD-64 Linux, macOS, ARMv8 Linux, Android 27 | Termux and both Intel and PowerPC OS X (tested from Tiger 10.4 onwards). 28 | It even supports UTF-8 out of the box. 29 | 30 | :::coffeescript 31 | # LDPL 'Hello World' example 32 | 33 | data: 34 | name is text # Your name will go here. 35 | 36 | procedure: 37 | display "Hello World!" lf "What's your name? " 38 | accept name 39 | display "你好, " name ", welcome to LDPL!" lf 40 | 41 | LDPL also aims to suppress unreadable code and redundancy by only having one 42 | way of doing anything. What a command does should never overlap with the 43 | purpose of another command and, as such, every LDPL command does one and only 44 | one thing. Every line is a step further towards the completion of an algorithm, 45 | no more, no less. 46 | 47 | ## The LDPL Compiler 48 | 49 | To use LDPL, you should first download or compile the LDPL compiler. 50 | For more information on how to do that, read the 51 | [*How to install LDPL* section](https://github.com/Lartu/ldpl#-how-to-install-ldpl) 52 | on the LDPL Readme. 53 | 54 | To use the compiler, you must have a C++ compiler already installed on your 55 | system and have mapped it to `c++`, found on your PATH. The LDPL Compiler 56 | compiles LDPL code to C++ code and thus this is a requirement for it to work. 57 | 58 | Once the compiler is set up, go write some LDPL source code, say `source.ldpl`. 59 | Then compile the source code using `ldpl source.ldpl`. The compiled, executable 60 | binary file will be saved as `source-bin`. Done! 61 | 62 | ### Compiler Switches 63 | 64 | To use the LDPL compiler, the syntax is as follows: 65 | 66 | ldpl [-i='']... |-c 67 | [-o=''|-r] [-f='']... [-n] 68 | ldpl [-v|-h] 69 | 70 | The `-f` flag can be used to pass extra options to the `c++` compiler when. For 71 | example, `-f=-lSDL` could be used to link against SDL. The `flag` statement can 72 | also be used as well (`flag "-lSDL"`) and its use is recommended. More on this 73 | later. 74 | 75 | By using `-r` you can just compile your project and print the C++ representation 76 | for that code. 77 | 78 | You can set the filename for the compiled binary with the `-o` flag. For 79 | example, if you want to name your program "dog", you could compile it with 80 | `ldpl -o=dog main.ldpl`. 81 | 82 | On Linux platforms, LDPL builds static binaries by default. If you want to 83 | build non-static ones use the `-ns` flag. 84 | 85 | The `-c` flag tells LDPL to accept source code from the standard input. 86 | 87 | 88 | You can import extra files and extensions to your LDPL compilation by using the 89 | `-i=` flag. Extensions can be imported by passing `.o`, `.a`, or `.cpp` files 90 | to this flag; see the Extensions section for more information. The use of the 91 | `INCLUDE` statement is preferred. 92 | 93 | `-v` and `--version` print out version info and release details. 94 | 95 | `-h` and `--help` print this list of options. 96 | 97 | ## File Extensions 98 | 99 | The preferred file extension for LDPL source files is **'.ldpl'**. 100 | The extension **'.lsc'** (LDPL Source File) should also be accepted in case 101 | the preferred extension couldn't be used for any reason. 102 | 103 | !!! tip 104 | File extensions are important: they help editors to recognize what language 105 | your source code is written in and they tell the LDPL compiler how to treat 106 | your files. 107 | 108 | ## License 109 | 110 | This LDPL Compiler is distributed under the Apache 2.0 License. 111 | All LDPL Dinosaur logos were created by [Lartu](https://lartu.net) and are 112 | released under a Creative Commons Attribution 4.0 International (CC BY 4.0) 113 | license. This documentation is released under the Apache 2.0 License. 114 | 115 | 116 | -------------------------------------------------------------------------------- /docs/io.md: -------------------------------------------------------------------------------- 1 | !!!Note 2 | While this section is up-to-date and complete, it has to be reformated 3 | to be easier on the eyes. All UPPERCASE statement names and code should 4 | be changed to lowercase. 5 | 6 | ## `DISPLAY` 7 | 8 | The `DISPLAY` statement outputs the values passed to the output stream. `CRLF` means line break and is a sugar syntax for the `"\n"` escape sequence. 9 | 10 | **Syntax:** 11 | 12 | ```coffeescript 13 | DISPLAY 14 | ``` 15 | 16 | **Example:** 17 | 18 | ```coffeescript 19 | DISPLAY "Hello, " nameVariable "! This is a number -> " 89.1 " :)" CRLF 20 | ``` 21 | 22 | ## `ACCEPT _` 23 | 24 | The `ACCEPT` command is used to gather input from the user. If a TEXT variable is specified, anything the user enters before pressing the 'return' key will be accepted. If a NUMBER variable is specified, the user must enter a number \(if any non-numeric key is entered, the error message "Redo from start" will be output and the ACCEPT command rerun\). 25 | 26 | **Syntax:** 27 | 28 | ```coffeescript 29 | ACCEPT 30 | ``` 31 | 32 | ## `EXECUTE _` 33 | 34 | The `EXECUTE` statement executes the specified system command. 35 | 36 | **Syntax:** 37 | 38 | ```coffeescript 39 | EXECUTE 40 | ``` 41 | 42 | **Example 1:** 43 | 44 | ```coffeescript 45 | # Prepare the command to execute 46 | IN myTextVar JOIN "echo " myVariable " >> myFile" 47 | # Execute it 48 | EXECUTE myTextVar 49 | ``` 50 | 51 | **Example 2:** 52 | 53 | ```coffeescript 54 | # Execute "dir" to list the files in the current directory under Windows 55 | EXECUTE "dir" 56 | ``` 57 | 58 | ## `EXECUTE _ AND STORE OUTPUT IN _` 59 | 60 | The `EXECUTE - AND STORE OUTPUT IN` executes the specified command and stores any resulting text in the passed variable. 61 | 62 | **Syntax:** 63 | 64 | ```coffeescript 65 | EXECUTE AND STORE OUTPUT IN 66 | ``` 67 | 68 | ## `EXECUTE _ AND STORE EXIT CODE IN _` 69 | 70 | The `EXECUTE - AND STORE EXIT CODE IN` executes the specified command and stores the exit code in the passed variable. 71 | 72 | **Syntax:** 73 | 74 | ```coffeescript 75 | EXECUTE AND STORE EXIT CODE IN 76 | ``` 77 | 78 | ## `ACCEPT _ UNTIL EOF` 79 | 80 | The `ACCEPT UNTIL EOF` statement accepts input from standard input until an EOF state is reached and stores all data gathered in TEXT-VAR. 81 | 82 | **Syntax:** 83 | 84 | ```coffeescript 85 | ACCEPT UNTIL EOF 86 | ``` 87 | 88 | ## `LOAD FILE _ IN _` 89 | 90 | 91 | The `LOAD FILE` statement loads the contents of a file into a text variable. 92 | 93 | **Syntax:** 94 | 95 | ```coffeescript 96 | LOAD FILE IN 97 | ``` 98 | 99 | **Example:** 100 | 101 | ```coffeescript 102 | LOAD FILE "myFolder/myTextFile.txt" IN myVariable 103 | ``` 104 | 105 | **Error Codes:** 106 | 107 | If the LOAD operation should fail, the following values will be returned into the `ERRORCODE` and `ERRORTEXT` variables: 108 | 109 | * `ERRORCODE`: 1 110 | * `ERRORTEXT`: "The file '<filename>' couldn't be opened." 111 | 112 | !!!warning 113 | Always use the `ERRORCODE` variable to check if the operation was successful or not. Do **not** use `ERRORTEXT` for anything else than displaying the error found, as its contents may change in future releases of LDPL. 114 | 115 | ## `WRITE _ TO FILE _` 116 | 117 | The `WRITE x TO FILE y` statement writes the value of `x` to the file called `y`. If the file already exists, everything in it will be overwritten by `x`. 118 | 119 | **Syntax:** 120 | 121 | ```coffeescript 122 | WRITE TO FILE 123 | ``` 124 | 125 | **Example:** 126 | 127 | ```coffeescript 128 | WRITE "Hello there!" TO FILE "hello.txt" 129 | ``` 130 | 131 | **Error Codes:** 132 | 133 | If the WRITE operation should fail, the `ERRORCODE` and `ERRORTEXT` variables will be set to the following values: 134 | 135 | * `ERRORCODE`: 1 136 | * `ERRORTEXT`: "Could not open '<filename>'" 137 | * `ERRORCODE`: 2 138 | * `ERRORTEXT`: "Could not write to '<filename>'" 139 | 140 | ## `APPEND _ TO FILE _` 141 | 142 | The `APPEND x TO FILE y` statement appends the value of `x` to the file called `y`. If the file already exists, `x` will be added at the end of its contents. 143 | 144 | **Syntax:** 145 | 146 | ```coffeescript 147 | APPEND TO FILE 148 | ``` 149 | 150 | **Example:** 151 | 152 | ```coffeescript 153 | APPEND "\nHow are you?" TO FILE "hello.txt" 154 | ``` 155 | 156 | in this case, the file `hello.txt` (created in the example of the `WRITE _ TO FILE _` function and modified as stated there) will contain the text 157 | 158 | ```text 159 | Hello there! 160 | How are you? 161 | ``` 162 | 163 | **Error Codes:** 164 | 165 | If the APPEND operation should fail, the `ERRORCODE` and `ERRORTEXT` variables will be set to the following values: 166 | 167 | * `ERRORCODE`: 1 168 | * `ERRORTEXT`: "Could not open '<filename>'" 169 | * `ERRORCODE`: 2 170 | * `ERRORTEXT`: "Could not write to '<filename>'" 171 | -------------------------------------------------------------------------------- /docs/list.md: -------------------------------------------------------------------------------- 1 | !!!Note 2 | While this section is up-to-date and complete, it has to be reformated 3 | to be easier on the eyes. All UPPERCASE statement names and code should 4 | be changed to lowercase. 5 | 6 | ## `PUSH _ TO _` 7 | 8 | The `PUSH - TO` statement is used to add elements to a LIST. When you push an element to a LIST it is appended at the end of the list. 9 | 10 | **Syntax:** 11 | 12 | ```coffeescript 13 | PUSH TO 14 | PUSH TO 15 | ``` 16 | 17 | **Example:** 18 | 19 | ```coffeescript 20 | DATA: 21 | foo IS TEXT LIST 22 | PROCEDURE: 23 | PUSH "First index" TO foo 24 | PUSH "Second index" TO foo 25 | ``` 26 | 27 | In the above example, `foo` now contains the value `"First index"` at index `0` and the value `"Second index"` at index `1`. 28 | 29 | ## `CLEAR` 30 | 31 | The `CLEAR` statement empties a LIST, thus deleting all its contents. The LIST itself is not deleted though, and can still be used and filled with new elements after a `CLEAR` statement has been executed. 32 | 33 | **Syntax:** 34 | 35 | ```coffeescript 36 | CLEAR 37 | ``` 38 | 39 | ## `COPY _ TO _` 40 | 41 | The `COPY - TO` statement copies all the elements of a LIST with their respective indices to another LIST **of the same type**. The original LIST is untouched, but the destination LIST is completely overwritten by the contents of the copied LIST and any elements that existed in it prior to the copy are deleted. In other words, the destination LIST is `CLEAR`ed before the copy. 42 | 43 | **Syntax:** 44 | 45 | ```coffeescript 46 | COPY TO 47 | COPY TO 48 | ``` 49 | 50 | **Example:** 51 | 52 | ```coffeescript 53 | DATA: 54 | foo IS TEXT LIST 55 | bar IS TEXT LIST 56 | PROCEDURE: 57 | PUSH "Hello there!" TO foo 58 | PUSH "How are you?" TO foo 59 | COPY foo TO bar 60 | DISPLAY bar:0 " " bar:1 CRLF 61 | # Will display "Hello there! How are you?" 62 | ``` 63 | 64 | ## `GET LENGTH OF _ IN _` 65 | 66 | The `GET LENGTH OF - IN` statement stores the amount of elements stored in a LIST \(or, analogously, the length of the LIST\) into a numeric variable. 67 | 68 | **Syntax:** 69 | 70 | ```coffeescript 71 | GET LENGTH OF IN 72 | ``` 73 | 74 | **Example:** 75 | 76 | ```coffeescript 77 | DATA: 78 | foo IS TEXT LIST 79 | count IS NUMBER 80 | PROCEDURE: 81 | PUSH "Hello there!" TO foo 82 | PUSH "How are you?" TO foo 83 | STORE LENGTH OF foo IN count 84 | DISPLAY count CRLF 85 | # Will display 2 86 | ``` 87 | 88 | ## `DELETE LAST ELEMENT OF _` 89 | 90 | 91 | The `DELETE LAST ELEMENT OF` deletes the last element pushed to a LIST. If the LIST was empty, this statement does nothing. 92 | 93 | **Syntax:** 94 | 95 | ```coffeescript 96 | DELETE LAST ELEMENT OF 97 | ``` 98 | 99 | ## `REMOVE ELEMENT AT _ FROM _` 100 | 101 | The `REMOVE ELEMENT AT - FROM` statement deletes the element at the specified index from a LIST. If the index is out of bounds, this statement does nothing. 102 | 103 | **Syntax:** 104 | 105 | ```coffeescript 106 | REMOVE ELEMENT AT FROM 107 | ``` 108 | 109 | **Example:** 110 | 111 | ```coffeescript 112 | DATA: 113 | foo IS TEXT LIST 114 | PROCEDURE: 115 | PUSH "Hello there!" TO foo 116 | PUSH "How are you?" TO foo 117 | REMOVE ELEMENT AT 0 FROM foo 118 | DISPLAY foo:0 CRLF 119 | # Will display "How are you?" 120 | ``` 121 | -------------------------------------------------------------------------------- /docs/manList: -------------------------------------------------------------------------------- 1 | index.md 2 | structure.md 3 | data.md 4 | procedure.md 5 | naming.md 6 | flow.md 7 | arithmetic.md 8 | time.md 9 | text.md 10 | list.md 11 | map.md 12 | io.md 13 | cppext.md 14 | -------------------------------------------------------------------------------- /docs/map.md: -------------------------------------------------------------------------------- 1 | !!!Note 2 | While this section is up-to-date and complete, it has to be reformated 3 | to be easier on the eyes. All UPPERCASE statement names and code should 4 | be changed to lowercase. 5 | 6 | ## `CLEAR` 7 | 8 | The `CLEAR` statement empties a MAP, thus deleting all its contents. The MAP itself is not deleted though, and can still be used and filled with new elements after a `CLEAR` statement has been executed. 9 | 10 | **Syntax:** 11 | 12 | ```coffeescript 13 | CLEAR 14 | ``` 15 | 16 | ## `COPY _ TO _` 17 | 18 | The `COPY - TO` statement copies all the elements of a MAP with their respective keys to another MAP **of the same type**. The original MAP is untouched, but the destination MAP is completely overwritten by the contents of the copied MAP and any elements that existed in it prior to the copy are deleted. In other words, the destination MAP is `CLEAR`ed before the copy. 19 | 20 | **Syntax:** 21 | 22 | ```coffeescript 23 | COPY TO 24 | COPY TO 25 | ``` 26 | 27 | **Example:** 28 | 29 | ```coffeescript 30 | DATA: 31 | foo IS TEXT MAP 32 | bar IS TEXT MAP 33 | PROCEDURE: 34 | STORE "Hello there!" IN foo:0 35 | STORE "How are you?" IN foo:7 36 | COPY foo TO bar 37 | DISPLAY bar:0 " " bar:7 CRLF 38 | # Will display "Hello there! How are you?" 39 | ``` 40 | 41 | 42 | ## `GET KEY COUNT OF _ IN _` 43 | 44 | The `GET KEY COUNT OF - IN` statement stores the amount of elements \(or, analogously, keys\) stored in a MAP into a numeric variable. 45 | 46 | **Syntax:** 47 | 48 | ```coffeescript 49 | GET KEY COUNT OF IN 50 | ``` 51 | 52 | **Example:** 53 | 54 | ```coffeescript 55 | DATA: 56 | foo IS TEXT MAP 57 | count IS NUMBER 58 | PROCEDURE: 59 | STORE "Hello there!" IN foo:0 60 | STORE "How are you?" IN foo:7 61 | GET KEY COUNT OF foo IN count 62 | DISPLAY count CRLF 63 | # Will display 2 64 | ``` 65 | 66 | 67 | ## `GET KEYS OF _ IN _` 68 | 69 | The `GET KEYS OF - IN` statement stores all the keys of a MAP into a TEXT LIST. Say you have a MAP with keys `0`, `"cat"` and `"dog"`. The elements these keys point to are not important. Using the `GET KEYS OF` statement, you can copy the keys of this MAP to a LIST. Thus, the resulting LIST will \(for example\) have the value `0` at index 0, the value `"cat"` at index 1 and the value `"dog"` at index 2. This statement is thus used to find all the keys of a particular MAP. 70 | 71 | **Syntax:** 72 | 73 | ```coffeescript 74 | GET KEYS OF IN 75 | ``` 76 | 77 | **Example:** 78 | 79 | ```coffeescript 80 | DATA: 81 | foo IS TEXT MAP 82 | bar IS TEXT LIST 83 | PROCEDURE: 84 | STORE "Hello there!" IN foo:0 85 | STORE "How are you?" IN foo:7 86 | STORE "I like cats" IN foo:"cat" 87 | STORE "I love dogs" IN foo:"dog" 88 | STORE "LDPL is nice" IN foo:3 89 | GET KEYS OF foo IN bar 90 | ``` 91 | 92 | At the end of the execution of the previous excerpt of code, the `TEXT LIST` called `bar` will contain the values `"0"`, `"7"`, `"cat"`, `"dog"` and `"3"` at indexes that are consecutive integers starting at zero. 93 | 94 | -------------------------------------------------------------------------------- /docs/naming.md: -------------------------------------------------------------------------------- 1 | ## Identifier Naming Schemes 2 | 3 | Variables and sub-procedure names follow the same naming rules. Their names 4 | can't be empty and may consist of any character with few exceptions \(listed 5 | below\). Like statements, variable and sub-procedure names in LDPL are not case 6 | sensitive. 7 | 8 | * Variable and sub-procedure names cannot contain the character `:`, as it is 9 | used for **map** and **list** accesses. 10 | * Variable and sub-procedure names cannot contain the character `"`, as it is 11 | used to delimit strings. 12 | * Variable and sub-procedure names cannot contain spaces. 13 | * Variable and sub-procedure names cannot be valid numbers \(they may contain 14 | numbers, though\). 15 | * Variable and sub-procedure names cannot contain the character `(` nor the 16 | character `)` as these characters are used in mathematical expressions. 17 | * Variables and sub-procedures cannot be called `CRLF`, as it is internally 18 | turned into `"\r\n"`. 19 | * Variables and sub-procedures cannot be called `LF`, as it is internally 20 | turned into `"\r\n"`. 21 | * Variables and sub-procedures cannot be called `+` nor `-` nor `*` nor `/` as 22 | these characters are used in mathematical expressions. 23 | 24 | 25 | !!!warning 26 | External Identifiers follow different naming rules. Please check the 27 | **External Identifier Naming Scheme** section for more information. 28 | 29 | ## Label Naming Schemes 30 | 31 | Labels are identifiers used alongside the `label` statement. 32 | Labels in LDPL may not be empty strings and may contain any character except spaces and 33 | `"` (double quotes). Labels cannot be named `CRLF` nor `LF` for the same reasons explained in 34 | the section above. 35 | 36 | ## External Identifier Naming Schemes 37 | 38 | !!!hint 39 | This section talks about identifier naming schemes for **C++ Extensions**. If 40 | you have not read the section on C++ Extensions yet, ignore this and then come 41 | back later. 42 | 43 | All C++ variables and functions accessible to LDPL programs may contain only 44 | `A-Z`, `0-9`, and the `_` character in their names. All other characters used 45 | on the LDPL side to reference a variable or function will get converted to an 46 | underscore (`_`) or, if it's a letter, capitalized. 47 | 48 | For example: 49 | 50 | | LDPL Identifier | Result When Converted to a C++ Identifier | 51 | | :--- | :--- | 52 | | window.rows | WINDOW\_ROWS | 53 | | HTTP/get | HTTP\_GET | 54 | | SDL/Font.new | SDL\_FONT\_NEW | 55 | | sdl.font-new | SDL\_FONT\_NEW | 56 | | NAME | NAME | 57 | | version\_number | VERSION\_NUMBER | 58 | 59 | !!!warning 60 | Note that this conversion scheme may cause collisions. 61 | All of the following LDPL variables will be converted to `ONE_TWO:` 62 | 63 | * `One-Two` 64 | * `one.two` 65 | * `one/two` 66 | * `OnE-TWO` 67 | -------------------------------------------------------------------------------- /docs/procedure.md: -------------------------------------------------------------------------------- 1 | ## The Procedure Section 2 | 3 | The **procedure** section is where all the code of a LDPL program that is not a 4 | variable declaration is written. An LDPL program **must** contain a procedure 5 | section, even if it's empty. Execution should and will fail otherwise. 6 | 7 | Within the procedure section, every line can contain either a comment, 8 | a statement, a statement and a comment or be empty. No two statements can be 9 | written on the same line. 10 | 11 | An example procedure section may end up looking like this: 12 | 13 | :::coffeescript 14 | procedure: 15 | store 5 in foo 16 | store "hi there" in bar:"hi" 17 | # Note that these are the variables 18 | # declared in the data section above. 19 | 20 | Code within the procedure section is executed from top to bottom, 21 | skipping sub-procedure declarations, unless they are explicitly called. 22 | 23 | Available statements and sub-procedure declarations will be explained further 24 | in the following sections of this document. 25 | 26 | ## Sub-procedures 27 | A **sub-procedure** is a piece of code that can be called and executed from other 28 | parts of the script. Sub-procedure subsections must be declared within the 29 | **procedure** section of the code using a `sub-procedure ` statement and 30 | end with an `end sub-procedure` statement. Alternatively, the shorter versions 31 | `sub ` and `end sub` may be used. Bear in mind that you can't define a 32 | sub-procedure within a sub-procedure. 33 | 34 | Sub-procedures may be invoked at any point in your code by **calling** them with 35 | the `call` statement. When you do that, the sub-procedure is executed and once 36 | it finishes executing, the line after the `call` that called the sub-procedure 37 | is executed and execution continues normally. More on the call statement later. 38 | 39 | Also bear in mind that you can `call` a 40 | sub-procedure before it has been declared, but the compilation process will 41 | fail if the compiler doesn't find the sub-procedure once it has parsed all the 42 | files in your program. 43 | 44 | The full syntax for declaring sub-procedures is this one: 45 | 46 | :::coffeescript 47 | sub-procedure procedureName 48 | parameters: 49 | # parameters go here 50 | local data: 51 | # local variable declarations go here 52 | procedure: 53 | # code goes here 54 | end sub-procedure 55 | 56 | Or you may, as stated, use the shorter version: 57 | 58 | :::coffeescript 59 | sub procedureName 60 | parameters: 61 | # parameters go here 62 | local data: 63 | # local variable declarations go here 64 | procedure: 65 | # code goes here 66 | end sub 67 | 68 | In context, the full declaration of a sub-procedure looks like this: 69 | 70 | :::coffeescript 71 | data: 72 | # ... 73 | procedure: 74 | # ... 75 | sub mySubprocedure 76 | parameters: 77 | # ... 78 | local data: 79 | # ... 80 | procedure: 81 | # ... 82 | end sub 83 | 84 | The **parameters** and local **data** sub-sections are optional (more on these 85 | later). If you decide to not include any of those sub-sections, you can skip 86 | the **procedure** tag altogether and just go ahead writing your code like this: 87 | 88 | :::coffeescript 89 | sub someOtherSub 90 | display "Hello there!" lf 91 | end sub 92 | 93 | You cannot have more than one sub-procedure with the same name. Also, 94 | sub-procedure names must follow the guidelines stated in the **Naming Schemes** 95 | section of this document. 96 | 97 | ### The Procedure Subsection: 98 | 99 | In the **procedure** sub-section of the sub-procedure you may write the code 100 | of your sub-procedure using statements like in the main **procedure** section. 101 | In this procedure sub-section, however, you may also use the `RETURN` statement 102 | to halt execution of the sub-procedure and return to the point where it was 103 | called from. 104 | 105 | ### The Parameters and Local Data Sub-Sections 106 | 107 | Within the **parameters** and **data** sub-sections of a function you may only 108 | declare variables just like in the global **data** section. The variables 109 | defined here, however, can only be used inside the procedure sub-section of the 110 | same sub-procedure they where declared in. 111 | 112 | Variables between the parameters and local data sub-sections may not share 113 | names. 114 | 115 | If a variable declared within the parameters or data sub-sections of 116 | a sub-procedure shares its name with a global variable, when using that name 117 | within the procedure section of the sub-procedure, it will always refer to the 118 | variable declared in said sub-procedure and not the global one. 119 | 120 | At the start of the sub-procedure execution, all the variables declared in its 121 | **local data** section will be initialized with their type default values, and 122 | each invocation of the function will have its own copy of the local variables. 123 | This is important if you want to implement recursive sub-procedures: 124 | 125 | :::coffeescript 126 | data: 127 | execution is number 128 | procedure: 129 | sub myRecursiveSub 130 | local data: 131 | myLocalVar is number 132 | procedure: 133 | in executions solve executions + 1 134 | if executions is equal to 3 then 135 | return # We don't want this to run forever! 136 | end if 137 | display "[myLocalVar starts at " myLocalVar "!" 138 | store executions in myLocalVar 139 | call myRecursiveSub 140 | display "I'm execution n°" myLocalVar "!]" 141 | end sub 142 | 143 | call myRecursiveSub 144 | 145 | That weird recursive function displays the following output: 146 | 147 | :::text 148 | [myLocalVar starts at 0![myLocalVar starts at 0!I'm execution Nº2!]I'm execution Nº1!] 149 | 150 | !!!hint 151 | Most of the statements used in this example will be explained later. 152 | Don't feel bad if you don't yet understand completely what that sub-procedure 153 | does. 154 | 155 | Variables declared within the **parameters** sub-section are quite different. 156 | When calling sub-procedures using the `call` statement, you may also use the 157 | optional `with` keyword to specify values to be passed to the sub-procedure. 158 | The variables declared in the **parameters** sub-section will take these values, 159 | following the order they were declared in. 160 | 161 | For example, if you declare a sub-procedure like this: 162 | 163 | :::coffeescript 164 | sub addTwoNumbers 165 | parameters: 166 | a is number 167 | b is number 168 | c is number 169 | procedure: 170 | # ... 171 | end sub 172 | 173 | You may then call it like this: 174 | 175 | :::coffeescript 176 | call addTwoNumbers with 5 6 7 177 | 178 | And `a` will take the value 5, `b` the value 6 and `c` the value 7. 179 | 180 | While this is fine, it gets more powerful when using variables instead of 181 | fixed values. LDPL is a pass-by-reference language. This means that if you 182 | pass a variable to a sub-procedure with the `with` keyword and its value is 183 | assigned to a parameter variable, if you modify that parameter variable the 184 | original variable will be modified as well. Let's see an example: 185 | 186 | :::coffeescript 187 | data: 188 | result is number 189 | 190 | procedure: 191 | sub addTwoNumbers 192 | parameters: 193 | a is number 194 | b is number 195 | c is number 196 | procedure: 197 | in c solve a + b 198 | end sub 199 | 200 | # the variable result is initialized to 0 by default 201 | call addTwoNumbers with 4 5 result 202 | display "The result is: " result "." lf 203 | 204 | !!!hint 205 | The `in _ solve _` statement is used to solve mathematical expressions. 206 | If you execute, for example, `in foo solve 5 + 6 - 8`, the result of solving 207 | 5 + 6 - 8 will be stored in the number variable `foo`. 208 | 209 | 210 | That code displays the following text: 211 | 212 | :::text 213 | The result is 9. 214 | 215 | This is because, as we called the variable with a variable as a parameter 216 | (`call addTwoNumbers with 4 5 result`, **result** is the variable here) it was 217 | *somewhat* aliased to the local parameter variable `c` and, thus, when we 218 | solved `a + b` in `c`, we stored the result of `a + b` in `result`. 219 | 220 | Passing variables by reference lets you return results from your sub-procedure, 221 | as shown in the example above. -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material 2 | -------------------------------------------------------------------------------- /docs/structure.md: -------------------------------------------------------------------------------- 1 | # LDPL Source Code Structure 2 | 3 | ## Case Sensitivity 4 | 5 | LDPL is a **case-insensitive** language. This means that variables and statements 6 | that consist of the same characters are understood to be the same variable 7 | or statement regardless of their case. For example, in other languages the 8 | variables `foo` and `FOO` would represent two different variables, but in 9 | LDPL they are **the same one**. This same thing happens with statements. For 10 | LDPL it's the same if you write `display` or `dIsPlAy` (but please don't do so). 11 | 12 | !!! warning 13 | LDPL is case-insensitive only for latin A-z characters. Accented characters and 14 | non-ASCII letters will be matched in a case-insensitive manner. Depending 15 | on the LDPL implementation, `ááá` and `ÁÁÁ` may not represent the same 16 | identifier. It is good practice to write your code in a single case and 17 | always call your variables and functions by the exact name used when 18 | declaring them. 19 | 20 | ## Comments 21 | Comments in LDPL are denoted with a hash symbol ('#') and can be placed both 22 | on their own line or at the end of a line that already contains a statement. 23 | Everything after the hash is considered to be part of the comment and, 24 | therefore, not executed. 25 | 26 | :::coffeescript 27 | data: 28 | # This won't be executed 29 | # This won't be either 30 | procedure: 31 | # This line won't be interpreted 32 | 33 | ## Data and Procedure Sections 34 | LDPL was designed to be a rigidly structured programming 35 | language and, as such, variable declarations and the remaining code procedures 36 | are separated into two different, mutually exclusive sections within every 37 | source file: the `data:` section and the `procedure:` section. 38 | 39 | :::coffeescript 40 | data: 41 | # Variables go here 42 | procedure: 43 | # The rest of the code goes here 44 | 45 | Variable declarations should be placed within the **data** section, while the 46 | rest of the code should be placed inside the **procedure** section. Further 47 | sub-procedures should be placed also within the **procedure** section, inside their 48 | own **sub-procedure** subsections. 49 | 50 | The **data** section may be obviated if no variables are used. 51 | 52 | If your project consists of multiple LDPL source files, each file must have its 53 | own data and procedure sections. 54 | 55 | ## Including More Source Files 56 | 57 | You can import other LDPL source files to your LDPL source by using the 58 | **include** statement. For example, say you have two sources: 59 | 60 | :::coffeescript 61 | # This is 'firstSource.ldpl' 62 | procedure: 63 | call someSubprocedure 64 | 65 | and 66 | 67 | :::coffeescript 68 | # This is 'includedFile.ldpl' 69 | procedure: 70 | sub someSubprocedure 71 | display "Hi there!" 72 | end sub 73 | 74 | You can import the second source into the first one in order to create one 75 | big source file like this: 76 | 77 | :::coffeescript 78 | # This is 'firstSource.ldpl' 79 | include "includedFile.ldpl" 80 | procedure: 81 | call someSubprocedure 82 | 83 | When you run the code above, it will print 84 | 85 | :::text 86 | Hi there! 87 | 88 | using the sub-procedure `someSubprocedure` included from the second file. 89 | 90 | The location where the included files are searched for is relative to the file 91 | that includes them. You may include as many files as you like. 92 | 93 | The `include` statement can only be used **before** the **data** section. 94 | 95 | ## C++ Extensions 96 | 97 | LDPL source code can be extended using **C++ Extensions**, C++ source files 98 | that can be compiled along the C++ source code generated by the LDPL compiler. 99 | While these are explained in greater detail in their respective section of 100 | this document, two statements are relevant to this part of the documentation: 101 | the `flag` and `extension` statements. 102 | 103 | ## C++ Compiler Flags 104 | 105 | When writing C++ code, you may need to pass some flags to the C++ compiler. 106 | Say, for example, you are writing something using the SDL Library. When 107 | trying to compile your code, you will need to pass the flag `-lSDL` to your 108 | C++ compiler. This same thing can be achieved under LDPL by using the `flag` 109 | statement. 110 | 111 | Following the example above, if you want to pass the flag `-lSDL` to the C++ 112 | compiler that compiles the code generated by the LDPL compiler from your LDPL 113 | source code, you may do this: 114 | 115 | :::coffeescript 116 | flag "-lSDL" 117 | flag "-fpermissive" # you can pass as many flags as you want 118 | data: 119 | #... 120 | procedure: 121 | #... 122 | 123 | Often, different operating systems require different flags. In those cases, 124 | you can restrict the `flag` statement to a particular OS: 125 | 126 | :::coffeescript 127 | flag "-O3" 128 | flag linux "-lncursesw" 129 | flag macos "-lncurses" 130 | flag macos "-D_XOPEN_SOURCE_EXTENDED" # multiple flags per OS are permitted 131 | data: 132 | #... 133 | procedure: 134 | #... 135 | 136 | !!! tip 137 | LDPL officially supports the following operating systems: 138 | 139 | - Linux 140 | - MacOS 141 | - Android 142 | 143 | And includes experimental support for these fine systems: 144 | 145 | - BSD 146 | - Emscripten 147 | 148 | The `flag` statement can only be used **before** the **data** section. 149 | 150 | ## C++ Extension Including 151 | 152 | Extensions are C++ source files that you can compile along your LDPL source 153 | in order to extend the language, as stated above. If you want to include an 154 | extension, you may use the `extension` statement. For example: 155 | 156 | :::coffeescript 157 | extension "someDirectory/someFile.cpp" 158 | flag "-O3" 159 | data: 160 | #... 161 | procedure: 162 | #... 163 | 164 | The `extension` statement can only be used **before** the **data** section. 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /docs/text.md: -------------------------------------------------------------------------------- 1 | !!!Note 2 | While this section is up-to-date and complete, it has to be reformated 3 | to be easier on the eyes. All UPPERCASE statement names and code should 4 | be changed to lowercase. 5 | 6 | ## `IN _ JOIN _` 7 | 8 | The `IN JOIN` statement concatenates two or more values and stores them in a TEXT variable. If any of those values is a number, it is converted to a string before concatenation. 9 | 10 | **Syntax:** 11 | 12 | ```coffeescript 13 | IN JOIN 14 | ``` 15 | 16 | **Example:** 17 | 18 | ```coffeescript 19 | IN myTextVariable JOIN "Hello World!" " " "Welcome to LDPL!" crlf 20 | ``` 21 | 22 | will store 23 | 24 | ```coffeescript 25 | "Hello World! Welcome to LDPL!\n" 26 | ``` 27 | 28 | in `myTextVariable`. 29 | 30 | 31 | ## `REPLACE _ WITH _ FROM _ IN _` 32 | 33 | 34 | The `REPLACE` statement finds and replaces every occurrence of some TEXT in a TEXT variable or value some other TEXT. The result is then stored in a TEXT variable. 35 | 36 | **Syntax:** 37 | 38 | ```coffeescript 39 | REPLACE WITH FROM IN 40 | ``` 41 | 42 | **Example:** 43 | 44 | ```coffeescript 45 | REPLACE "COBOL" FROM "COBOL is great!" WITH "LDPL" IN sentiment 46 | DISPLAY sentiment crlf 47 | ``` 48 | 49 | Outputs: 50 | 51 | ```text 52 | LDPL is great! 53 | ``` 54 | 55 | ## `SPLIT _ BY _ IN _` 56 | 57 | 58 | The `SPLIT` statement breaks up a single TEXT variable into multiple parts based on another TEXT variable and puts those parts into sub-indexes of a `TEXT LIST`, starting at the NUMBER `0` and incrementing by whole numbers. This allows you to break up a text sentence into multiple parts by splitting on spaces, for example. Or to split a file into lines by splitting on `"\n"` 59 | 60 | To break TEXT into individual characters, split by the empty string of `""`. 61 | 62 | **Syntax:** 63 | 64 | ```coffeescript 65 | SPLIT BY IN 66 | ``` 67 | 68 | **Example:** 69 | 70 | ```coffeescript 71 | DATA: 72 | parts IS TEXT LIST 73 | PROCEDURE: 74 | SPLIT "Hello there!" BY " " IN parts 75 | display parts:0 crlf parts:1 crlf 76 | ``` 77 | 78 | will output: 79 | 80 | ```text 81 | Hello 82 | there! 83 | ``` 84 | 85 | **Split into characters:** 86 | 87 | ```coffeescript 88 | DATA: 89 | parts IS TEXT LIST 90 | PROCEDURE: 91 | SPLIT "onomatopoeia" BY "" IN parts 92 | DISPLAY parts:3 " is M " crlf 93 | ``` 94 | 95 | will output: 96 | 97 | ```text 98 | m is M 99 | ``` 100 | 101 | ## `GET CHARACTER AT _ FROM _ IN _` 102 | 103 | The `GET CHARACTER AT` statement gets the character at the position indicated by the NUMBER value from the TEXT value and stores it in a TEXT variable. 104 | 105 | **Syntax:** 106 | 107 | ```coffeescript 108 | GET CHARACTER AT FROM IN 109 | ``` 110 | 111 | ## `GET LENGTH OF _ IN _` 112 | 113 | The `GET LENGTH OF` statement counts the number of characters in the passed TEXT and stores that number in the NUMBER variable. 114 | 115 | **Syntax:** 116 | 117 | ```coffeescript 118 | GET LENGTH OF IN 119 | ``` 120 | 121 | ## `GET ASCII CHARACTER _ IN _` 122 | 123 | The `GET ASCII CHARACTER` statement stores the character with the ASCII code passed in NUMBER or NUMBER-VAR in TEXT-VAR. 124 | 125 | **Syntax:** 126 | 127 | ```coffeescript 128 | GET ASCII CHARACTER IN 129 | ``` 130 | 131 | ## `GET CHARACTER CODE OF _ IN _` 132 | 133 | The `GET CHARACTER CODE OF` statement stores the ASCII code of the character passed in TEXT or TEXT-VAR in NUMBER-VAR. Will fail if the length of the string passed in TEXT or TEXT-VAR is not 1. 134 | 135 | **Syntax:** 136 | 137 | ```c 138 | GET CHARACTER CODE OF IN 139 | ``` 140 | 141 | **Error Codes:** 142 | 143 | Multi-byte characters \(like emojis and non-ASCII characters\) cannot be parsed by this statement. When trying to do so, the operation will fail and the following values will be returned into the `ERRORCODE` and `ERRORTEXT` variables: 144 | 145 | * `ERRORCODE`: 1 146 | * `ERRORTEXT`: "Multibyte character received \(probably UTF-8\). Can't be parsed into a single number." 147 | 148 | !!!warning 149 | Always use the `ERRORCODE` variable to check if the operation was successful or not. Do **not** use `ERRORTEXT` for anything else than displaying the error found, as its contents may change in future releases of LDPL. 150 | 151 | 152 | ## `STORE QUOTE _ IN _` 153 | 154 | The `STORE QUOTE IN` statement allows you to store multiple lines in a single TEXT variable. Between the `STORE QUOTE IN` and `END QUOTE` statements whitespace is preserved literally, escape codes like `\t` and `\e` work the same as they do in regular text variables \(and can themselves be escaped using `\\`\), and double quotes \(`"`\) don't need to be escaped. 155 | 156 | **Syntax:** 157 | 158 | ```python 159 | STORE QUOTE IN 160 | #Text goes here 161 | END QUOTE 162 | ``` 163 | 164 | **Example:** 165 | 166 | ```coffeescript 167 | DATA: 168 | template IS TEXT 169 | 170 | PROCEDURE: 171 | STORE QUOTE IN template 172 | 173 | {{title}} 174 | {{body}} 175 | 176 | END QUOTE 177 | 178 | # ...code to use the template... 179 | ``` 180 | 181 | ## `GET INDEX OF _ FROM _ IN _` 182 | 183 | 184 | The `GET INDEX OF - FROM - IN` statement stores in a NUMBER variable the position of the first occurrence of a specified value in a string or TEXT variable. The first position of a string \(the first letter\) is considered to be the position number `0`. The value `-1` is stored if there are no occurrences. 185 | 186 | **Syntax:** 187 | 188 | ```coffeescript 189 | GET INDEX OF FROM IN 190 | ``` 191 | 192 | **Example:** 193 | 194 | ```coffeescript 195 | DATA: 196 | position IS NUMBER 197 | PROCEDURE: 198 | GET INDEX OF "is" FROM "LDPL is nice!" IN position 199 | DISPLAY position CRLF 200 | # Will display 5. 201 | ``` 202 | 203 | ## `COUNT _ FROM _ IN _` 204 | 205 | The `COUNT - FROM - IN` statement counts all the appearances of a string in another string and stores that value in a NUMBER variable. 206 | 207 | **Syntax:** 208 | 209 | ```coffeescript 210 | COUNT FROM IN 211 | ``` 212 | 213 | **Example:** 214 | 215 | ```coffeescript 216 | DATA: 217 | count IS NUMBER 218 | PROCEDURE: 219 | COUNT "the" FROM "the cat is called theodore" IN count 220 | DISPLAY count CRLF 221 | # Will display 2, as the can be found two times in that sentence. 222 | ``` 223 | 224 | ## `SUBSTRING _ FROM _ LENGTH _ IN _` 225 | 226 | The `SUBSTRING - FROM - LENGTH - IN` statement extracts parts of a string, beginning at the character at the specified position and storing in the destination TEXT variable the specified number of characters. 227 | 228 | **Syntax:** 229 | 230 | ```coffeescript 231 | SUBSTRING FROM LENGTH IN 232 | ``` 233 | 234 | **Example:** 235 | 236 | ```coffeescript 237 | DATA: 238 | foo IS TEXT 239 | PROCEDURE: 240 | SUBSTRING "Hello there!" FROM 1 LENGTH 4 IN foo 241 | # This will extract 4 characters from position 1 242 | DISPLAY foo CRLF 243 | # Will display "ello" 244 | ``` 245 | 246 | ## `TRIM _ IN` 247 | 248 | 249 | The `TRIM - IN` statement removes whitespace from both sides of a string and stores the resulting string in a TEXT variable. 250 | 251 | **Syntax:** 252 | 253 | ```coffeescript 254 | TRIM IN 255 | ``` 256 | 257 | **Example:** 258 | 259 | ```coffeescript 260 | DATA: 261 | foo IS TEXT 262 | PROCEDURE: 263 | TRIM " hello there! " IN foo 264 | DISPLAY foo CRLF 265 | # Will display "hello there!" 266 | ``` 267 | 268 | ## `CONVERT _ TO UPPERCASE IN _` 269 | 270 | The `CONVERT - TO UPPERCASE IN` statement converts all the characters in a string to uppercase and stores the resulting string in a TEXT variable. 271 | 272 | **Syntax:** 273 | 274 | ```coffeescript 275 | CONVERT TO UPPERCASE IN 276 | ``` 277 | 278 | **Example:** 279 | 280 | ```coffeescript 281 | DATA: 282 | greeting IS TEXT 283 | PROCEDURE: 284 | CONVERT "hello there!" TO UPPERCASE IN greeting 285 | DISPLAY greeting CRLF 286 | # Will display "HELLO THERE!" 287 | ``` 288 | 289 | ## `CONVERT _ TO LOWERCASE IN _` 290 | 291 | The `CONVERT - TO LOWERCASE IN` statement converts all the characters in a string to lowercase and stores the resulting string in a TEXT variable. 292 | 293 | **Syntax:** 294 | 295 | ```coffeescript 296 | CONVERT TO LOWERCASE IN 297 | ``` 298 | 299 | **Example:** 300 | 301 | ```coffeescript 302 | DATA: 303 | greeting IS TEXT 304 | PROCEDURE: 305 | CONVERT "HELLO THERE!" TO LOWERCASE IN greeting 306 | DISPLAY greeting CRLF 307 | # Will display "hello there!" 308 | ``` 309 | -------------------------------------------------------------------------------- /docs/time.md: -------------------------------------------------------------------------------- 1 | ## `get year in _` 2 | 3 | The `get year in _` stores the current year number in the passed number variable. 4 | 5 | **Syntax:** 6 | 7 | ```coffeescript 8 | get year in 9 | ``` 10 | 11 | ## `get month in _` 12 | 13 | The `get month in _` stores the current month number (1 - 12) in the passed number variable. 14 | 15 | **Syntax:** 16 | 17 | ```coffeescript 18 | get month in 19 | ``` 20 | 21 | ## `get day in _` 22 | 23 | The `get day in _` stores the current day of the month (1 - 31) in the passed number variable. 24 | 25 | **Syntax:** 26 | 27 | ```coffeescript 28 | get day in 29 | ``` 30 | 31 | ## `get hour in _` 32 | 33 | The `get hour in _` stores the current hour in the passed number variable. 34 | 35 | **Syntax:** 36 | 37 | ```coffeescript 38 | get hour in 39 | ``` 40 | 41 | ## `get minutes in _` 42 | 43 | The `get minutes in _` stores the current minutes in the passed number variable. 44 | 45 | **Syntax:** 46 | 47 | ```coffeescript 48 | get minutes in 49 | ``` 50 | 51 | ## `get seconds in _` 52 | 53 | The `get seconds in _` stores the current seconds in the passed number variable. 54 | 55 | **Syntax:** 56 | 57 | ```coffeescript 58 | get seconds in 59 | ``` 60 | 61 | ## `get epoch in _` 62 | 63 | The `get epoch in _` stores the current Unix time (the number of seconds that 64 | have elapsed since the Unix epoch, that is the time 00:00:00 UTC on 1 January 65 | 1970) in the passed number variable. 66 | 67 | **Syntax:** 68 | 69 | ```coffeescript 70 | get epoch in 71 | ``` 72 | -------------------------------------------------------------------------------- /images/big-ldpl4.0-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/big-ldpl4.0-logo.png -------------------------------------------------------------------------------- /images/ldpl-4.0-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldpl-4.0-badge.png -------------------------------------------------------------------------------- /images/ldpl-4.0-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldpl-4.0-logo.png -------------------------------------------------------------------------------- /images/ldpl-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldpl-logo-white.png -------------------------------------------------------------------------------- /images/ldpl-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldpl-logo.png -------------------------------------------------------------------------------- /images/ldpl-open-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldpl-open-graph.png -------------------------------------------------------------------------------- /images/ldplsaur-4.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldplsaur-4.0.png -------------------------------------------------------------------------------- /images/ldplsaur-briefcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldplsaur-briefcase.png -------------------------------------------------------------------------------- /images/ldplsaur-giant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldplsaur-giant.png -------------------------------------------------------------------------------- /images/ldplsaur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/ldplsaur.png -------------------------------------------------------------------------------- /images/lpm-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/lpm-logo.png -------------------------------------------------------------------------------- /images/reference-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/reference-logo.png -------------------------------------------------------------------------------- /images/release-logos/5.1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/5.1.0.png -------------------------------------------------------------------------------- /images/release-logos/active-argentinosaurus-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/active-argentinosaurus-white.png -------------------------------------------------------------------------------- /images/release-logos/active-argentinosaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/active-argentinosaurus.png -------------------------------------------------------------------------------- /images/release-logos/busy-brontosaurus-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/busy-brontosaurus-white.png -------------------------------------------------------------------------------- /images/release-logos/busy-brontosaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/busy-brontosaurus.png -------------------------------------------------------------------------------- /images/release-logos/creative-carnotaurus-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/creative-carnotaurus-white.png -------------------------------------------------------------------------------- /images/release-logos/creative-carnotaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/creative-carnotaurus.png -------------------------------------------------------------------------------- /images/release-logos/diligent-dreadnoughtus-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/diligent-dreadnoughtus-white.png -------------------------------------------------------------------------------- /images/release-logos/diligent-dreadnoughtus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/diligent-dreadnoughtus.png -------------------------------------------------------------------------------- /images/release-logos/eloquent-eoraptor-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/eloquent-eoraptor-white.png -------------------------------------------------------------------------------- /images/release-logos/eloquent-eoraptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/eloquent-eoraptor.png -------------------------------------------------------------------------------- /images/release-logos/friendly-falcarius-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/friendly-falcarius-white.png -------------------------------------------------------------------------------- /images/release-logos/friendly-falcarius.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/friendly-falcarius.png -------------------------------------------------------------------------------- /images/release-logos/groovy-gualicho.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/groovy-gualicho.png -------------------------------------------------------------------------------- /images/release-logos/source/5.1.0.acorn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/release-logos/source/5.1.0.acorn -------------------------------------------------------------------------------- /images/tutorial-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lartu/ldpl/5a11fe8e8c8acf9b6d8f7a1a13558d6e92122351/images/tutorial-logo.png -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # +=============================================+ 2 | # | | 3 | # | The __ ____ ____ __ | 4 | # | / / / __ \/ __ \/ / | 5 | # | / / / / / / /_/ / / | 6 | # | / /___/ /_/ / ____/ /___ | 7 | # | /_____/_____/_/ /_____/ | 8 | # | Programming Language | 9 | # | http://www.ldpl-lang.org/ | 10 | # | | 11 | # +=============================================+ 12 | 13 | # +----------+ 14 | # | MAKEFILE | 15 | # +----------+ 16 | # Usage: 17 | # make [x86=true] [version='""'] 18 | # make install 19 | 20 | # --- Version Information --- 21 | VERSION = '"5.2.0"' #LDPL Version 22 | VERSIONNAME = '"Groovy Gualicho"' #LDPL Version Name 23 | 24 | # --- Options --- 25 | # Alternative version name (make version="") 26 | ifdef version 27 | VERSION = '"$(version)"' 28 | endif 29 | # x86 cross compilation options (make x86=true) 30 | ifdef x86 31 | CROSS = -m32 32 | VERSION = '"$(version) (i386)"' 33 | endif 34 | #Flags for static compilation on Windows and Linux (not Android) 35 | ifneq ($(shell uname -s),Darwin) 36 | ifneq ($(shell uname -o),Android) 37 | LFLAGS = -static-libgcc -static-libstdc++ 38 | endif 39 | endif 40 | #PREFIX is environment variable, but if it is not set, then set default value 41 | ifeq ($(PREFIX),) 42 | PREFIX := /usr/local 43 | endif 44 | 45 | # --- Makefile Data --- 46 | SOURCE = ldpl.cpp 47 | OUT = ldpl 48 | LPMLOCATION = ~/ldpl/lpm/ 49 | LDPLLIBLOCATION = /lib/ldpl 50 | 51 | # --- Compilation Flags --- 52 | FLAGS = -Wall -std=gnu++11 -fpermissive -DVERSION=$(VERSION) -DVERSIONNAME=$(VERSIONNAME) -DCOMPILEDATE='"$(shell date +%Y-%m-%d)"' -DCOMPILEHOUR='"$(shell date +%H:%M:%S)"' -DLPMLOCATION='"$(DESTDIR)$(PREFIX)$(LPMLOCATION)"' -DLDPLLIBLOCATION='"$(DESTDIR)$(PREFIX)$(LDPLLIBLOCATION)"' 53 | 54 | # --- Build Rules --- 55 | # Build LDPL 56 | all: 57 | cd src && $(CXX) $(FLAGS) $(CROSS) $(SOURCE) -o $(OUT) $(LFLAGS) 58 | mkdir -p build 59 | mv src/$(OUT) build 60 | 61 | # Delete built file 62 | clean: 63 | rm -rf build 64 | 65 | install: build/ldpl 66 | install -d $(DESTDIR)$(PREFIX)/bin/ 67 | install -m 775 build/ldpl $(DESTDIR)$(PREFIX)/bin/ 68 | install -d $(DESTDIR)$(PREFIX)/lib/ldpl 69 | install src/ldpl_lib/ldpl_lib.cpp $(DESTDIR)$(PREFIX)$(LDPLLIBLOCATION) 70 | install src/ldpl_lib/BigInt.hpp $(DESTDIR)$(PREFIX)$(LDPLLIBLOCATION) 71 | 72 | uninstall: 73 | rm $(DESTDIR)$(PREFIX)/bin/ldpl 74 | rm -rf $(DESTDIR)$(PREFIX)$(LDPLLIBLOCATION) 75 | -------------------------------------------------------------------------------- /man/README: -------------------------------------------------------------------------------- 1 | +------------------------------+ 2 | | LDPL Only Man File Generator | 3 | | (LDPL OMFG) | 4 | +------------------------------+ 5 | 6 | *** Usage *** 7 | 8 | Run the command: 9 | 10 | $ ./generateMan.sh 11 | 12 | to generate an updated LDPL man page. Done. 13 | Your new man page is 'ldpl.1'. 14 | 15 | *** Alternatively, you can walk through the process step-by-step *** 16 | 17 | To recompile a man page for LDPL, clone the LDPL documentation repository 18 | (git clone https://www.github.com/lartu/ldpl-docs) in this folder. 19 | 20 | Then copy compileman.php and ldplman-intro into the cloned folder and 21 | go inside it. Then run: 22 | 23 | $ php compileman.php > ldpl.1 24 | 25 | A file called ldpl.1 will be generated. Copy it to the previous 26 | folder and delete the cloned folder. 27 | 28 | You can add it to your the new man file to your man pages or just open it by 29 | running: 30 | 31 | $ man ./ldpl.1 32 | 33 | Please note that as man pages for LDPL are generated from the LDPL 34 | documentation, they SHOULD NEVER be edited by hand. 35 | -------------------------------------------------------------------------------- /man/compileman.php: -------------------------------------------------------------------------------- 1 | 0){ 9 | $manPage = $manPage . "\n\n" . file_get_contents($line); 10 | } 11 | else if(strlen($line) > 0 && trim(substr($line, 0, 2)) == "##"){ 12 | $manPage = $manPage . "\n\n" . ".ce 1\n.SH -=-=-=-=- DOCS: " . trim(strtoupper(substr($line, 2))) . " -=-=-=-=-"; 13 | } 14 | } 15 | 16 | //Replace all originally blank lines with 17 | $manPage = explode("\n", $manPage); 18 | foreach($manPage as &$line){ 19 | if(strlen(trim($line)) == 0){ 20 | $line = ""; 21 | } 22 | } 23 | $manPage = implode("\n", $manPage); 24 | 25 | $manPage = str_replace("\\", "\\\\", $manPage); 26 | $manPage = str_replace("\(", "(", $manPage); 27 | $manPage = str_replace("\)", ")", $manPage); 28 | $manPage = str_replace("**", "", $manPage); 29 | $manPage = str_replace("_ ", " ", $manPage); 30 | $manPage = str_replace(" _", " ", $manPage); 31 | $manPage = preg_replace("/\!\[(.*?)\]\(.*?\)/m", "", $manPage); 32 | $manPage = preg_replace("/\[(.*?)\]\(.*?\)/m", "$1", $manPage); 33 | $manPage = preg_replace("/\:\:\:.*/m", "", $manPage); 34 | $manPage = preg_replace("/\!\!\!\s*warning/m", "~ Warning ~", $manPage); 35 | $manPage = preg_replace("/\!\!\!\s*hint/m", "~ Hint ~", $manPage); 36 | $manPage = preg_replace("/\!\!\!\s*note/m", "~ Note ~", $manPage); 37 | $manPage = preg_replace("/\!\!\!\s*tip/m", "~ Tip ~", $manPage); 38 | $manPage = preg_replace("/\!\!\!\s*Warning/m", "~ Warning ~", $manPage); 39 | $manPage = preg_replace("/\!\!\!\s*Hint/m", "~ Hint ~", $manPage); 40 | $manPage = preg_replace("/\!\!\!\s*Note/m", "~ Note ~", $manPage); 41 | $manPage = preg_replace("/\!\!\!\s*Tip/m", "~ Tip ~", $manPage); 42 | 43 | 44 | /*$manPage = str_replace("\(", "(", $manPage); 45 | $manPage = str_replace("\)", ")", $manPage); 46 | $manPage = str_replace('{% hint style="info" %}', "-- Note:\n.br", $manPage); 47 | $manPage = str_replace('{% hint style="warning" %}', "-- Warning:\n.br", $manPage); 48 | $manPage = str_replace('{% endhint %}', ".br\n--", $manPage); 49 | $manPage = str_replace('{% endcode-tabs-item %}', "", $manPage); 50 | $manPage = str_replace('{% endcode-tabs %}', "", $manPage); 51 | $manPage = str_replace('{% code-tabs %}', "", $manPage); 52 | $manPage = str_replace('{% code-tabs-item title="', "File: ", $manPage); 53 | $manPage = str_replace('" %}', "\n.br", $manPage); 54 | $manPage = str_replace("\\", "\\\\", $manPage); 55 | 56 | 57 | $manPage = str_replace("\[", "[", $manPage); 58 | $manPage = str_replace("\]", "]", $manPage); 59 | $manPage = str_replace("\#", "#", $manPage); 60 | $manPage = str_replace(">", ">", $manPage); 61 | $manPage = str_replace("<", "<", $manPage);*/ 62 | 63 | 64 | $manPage = explode("\n", $manPage); 65 | 66 | $incode = false; 67 | $lineNum = 1; 68 | 69 | //Convert each page to groff 70 | foreach($manPage as &$line){ 71 | 72 | $line = rtrim($line); 73 | 74 | if(strlen($line) > 0 && substr($line, 0, 3) == "```"){ 75 | $incode = !$incode; 76 | if($incode) $lineNum = 1; 77 | $line = ""; 78 | } 79 | else if($incode){ 80 | /*if($lineNum < 10) 81 | $line = $lineNum . " | " . $line . "\n.br"; 82 | else 83 | $line = $lineNum . "| " . $line . "\n.br"; 84 | $lineNum++;*/ 85 | $line = " " . $line . "\n.br"; 86 | } 87 | else if(strlen($line) > 0 && substr(trim($line), 0, 2) == "* "){ 88 | //$line = ".br\n • " . trim(substr($line, 2)); 89 | $count = 1; 90 | $line = str_replace("*", "•", $line, $count); 91 | if(substr($line, 0, 4) != " "){ 92 | $line = " " . trim($line); 93 | } 94 | } 95 | else if(strlen($line) > 0 && substr(trim($line), 0, 2) == "- "){ 96 | //$line = ".br\n • " . trim(substr($line, 2)); 97 | $count = 1; 98 | $line = str_replace("-", "•", $line, $count); 99 | if(substr($line, 0, 4) != " "){ 100 | $line = " " . trim($line); 101 | } 102 | } 103 | else if(strlen($line) > 0 && substr($line, 0, 1) == "|"){ 104 | $line = ".br\n" . trim($line); 105 | } 106 | /*else if(strlen($line) > 0 && trim(substr($line, 0, 4)) == "####"){ 107 | $line = ".B " . trim(substr($line, 4)); 108 | } 109 | if(strlen($line) > 0 && trim(substr($line, 0, 3)) == "###"){ 110 | $line = ".B " . trim(substr($line, 3)); 111 | } 112 | else if(strlen($line) > 0 && trim(substr($line, 0, 2)) == "##"){ 113 | $line = ".B " . trim(substr($line, 2)); 114 | } 115 | else if(strlen($line) > 0 && substr($line, 0, 1) == "#"){ 116 | $line = ".SH " . strtoupper(trim(substr($line, 1))); 117 | } 118 | else if($line == "!"){ 119 | $line = ""; 120 | }*/ 121 | } 122 | 123 | $manPage = implode("\n", $manPage); 124 | 125 | $manPage = str_replace('`', "'", $manPage); 126 | $manPage = str_replace("\n\n", "\n", $manPage, $count); 127 | $manPage = str_replace("\n\n", "\n\n", $manPage, $count); 128 | $manPage = str_replace("", "", $manPage, $count); 129 | echo file_get_contents("ldplman-intro") . "\n" . $manPage . "\n"; 130 | ?> 131 | -------------------------------------------------------------------------------- /man/generateMan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cp -r ../docs docs 3 | cp compileman.php docs/ 4 | cp ldplman-intro docs/ 5 | cd docs 6 | php compileman.php > ldpl.1 7 | cp ldpl.1 .. 8 | cd .. 9 | rm -rf docs 10 | -------------------------------------------------------------------------------- /man/ldplman-intro: -------------------------------------------------------------------------------- 1 | .TH LDPL 1 "5 may 2019" "LDPL Man 1.0" 2 | 3 | .SH NAME 4 | ldpl - The LDPL programming language compiler 5 | 6 | .SH SYNOPSIS 7 | .PP 8 | ldpl [-i=]... |-c 9 | [-o=|-r] [-f=]... [-n] 10 | .br 11 | ldpl [-v|-h] 12 | 13 | For more information on these options you can run "ldpl -h". 14 | 15 | .SH DESCRIPTION 16 | .PP 17 | This program compiles LDPL source code into executable binaries. 18 | LDPL is a programming language designed from the ground up to be excessively expressive, fast, readable and easy to learn. 19 | In order to be able to properly compile LDPL executables you 20 | .I 21 | must 22 | have a valid C++ compiler on your $PATH linked to the name 'c++'. 23 | 24 | Documentation and reference for the LDPL Programming Language can be found in the sections below. 25 | 26 | .SH OPTIONS 27 | .PP 28 | -v, --version 29 | Print out LDPL version info and release details. 30 | 31 | -h, --help 32 | Print this list of options. 33 | 34 | -r 35 | By using -r you can just compile the project and print the C++ representation for that code. 36 | 37 | -o= 38 | You can set the output file for the compiled binary with the -o flag. For example, if you want to name your program "dog", you could compile it with ldpl -o=dog main.ldpl. 39 | 40 | -i= 41 | Extensions can be imported by using this flag; see the Extensions section. 42 | 43 | -f= 44 | The -f flag can be used to add flags to the C++ compilation line. See the Building C++ Extensions section for more information. 45 | 46 | -n, --non-static 47 | On Linux and Windows platforms, LDPL builds static binaries by default. If you want to build non-static ones use the -ns flag. To build on Android Termux you must use this flag. 48 | 49 | -c 50 | The -c flag tells LDPL to accept source code from the standard input. 51 | 52 | .SH AUTHOR 53 | This document is based on the LDPL Reference, written by Martín del Río in collaboration with Chris West. 54 | 55 | .SH REPORTING BUGS 56 | Report any bugs to . 57 | 58 | .SH COPYRIGHT 59 | Copyright © 2018 - 2019, Martín del Río. LDPL may be copied only under the terms of the GNU General Public License 3.0, which may be found in the LDPL repository. 60 | .br 61 | This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. 62 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: LDPL Docs 2 | nav: 3 | - Introduction: index.md 4 | - Code Structure: structure.md 5 | - Data Section & Variables: data.md 6 | - Procedure Section & Subs: procedure.md 7 | - Naming Schemes: naming.md 8 | - Control Flow Statements: flow.md 9 | - Arithmetic Statements: arithmetic.md 10 | - Time Statements: time.md 11 | - Text Statements: text.md 12 | - List Statements: list.md 13 | - Map Statements: map.md 14 | - I/O Statements: io.md 15 | - C++ Extensions: cppext.md 16 | theme: 17 | name: 'material' 18 | 19 | markdown_extensions: 20 | - admonition 21 | - codehilite: 22 | linenums: true 23 | 24 | -------------------------------------------------------------------------------- /snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: ldpl-lang 2 | base: core22 3 | version: '5.1.0' 4 | summary: Compiled programming language for Unix systems, inspired by COBOL 5 | description: | 6 | The LDPL project produces the LDPL Programming Language, a free, powerful, compiled, open-source programming language designed from the ground up to be excessively expressive, readable, fast and easy to learn. LDPL was designed to run on Unix systems, including AMD-64 Linux, macOS, ARMv8 Linux, Android Termux and both Intel and PowerPC OS X (tested from Tiger 10.4 onwards). It even supports UTF-8 out of the box. 7 | 8 | grade: stable 9 | confinement: classic 10 | 11 | apps: 12 | ldpl: 13 | command: bin/ldpl 14 | 15 | parts: 16 | ldpl: 17 | plugin: autotools 18 | source: https://github.com/Lartu/ldpl.git 19 | source-tag: '5.1.0' 20 | build-packages: 21 | - g++ 22 | - build-essential 23 | - make 24 | - man-db 25 | - libc6-dev 26 | override-build: | 27 | make 28 | make install 29 | organize: 30 | /usr/local/bin/ldpl: bin/ 31 | 32 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) # CMake version check 2 | project(ldpl) # Create project "simple_example" 3 | set(CMAKE_CXX_STANDARD 11) # Enable c++11 standard 4 | 5 | # Add main.cpp file of project root directory as source file 6 | set(SOURCE_FILES ldpl.cpp) 7 | 8 | set(VERSION "5.0") 9 | set(VERSIONNAME "Groovy Gualicho") 10 | 11 | # Alternative version name (cmake -Dversion="") 12 | if (version) 13 | set(VERSION ${version}) 14 | endif () 15 | 16 | if (CMAKE_SIZEOF_VOID_P EQUAL 4) 17 | string(CONCAT VERSION ${VERSION} " (i386)") 18 | add_definitions(-m32) 19 | endif () 20 | 21 | # add VERSION 22 | set(VERSION " -DVERSION='\"${VERSION}\"' ") 23 | add_definitions(${VERSION}) 24 | 25 | # add VERSIONNAME 26 | set(VERSIONNAME " -DVERSIONNAME='\"${VERSIONNAME}\"' ") 27 | add_definitions(${VERSIONNAME}) 28 | 29 | add_definitions(-Wall -fpermissive) 30 | 31 | # Add DATE 32 | string(TIMESTAMP DATE "%Y-%m-%d") 33 | set(DATE " -DCOMPILEDATE='\"${DATE}\"' ") 34 | add_definitions(${DATE}) 35 | 36 | # Add HOUR 37 | string(TIMESTAMP HOUR "+%H:%M:%S") 38 | set(HOUR " -DCOMPILEHOUR='\"${HOUR}\"' ") 39 | add_definitions(${HOUR}) 40 | 41 | # add LPMLOCATION 42 | set(LPMLOCATION " -DLPMLOCATION='\"~/ldpl/lpm/\"' ") 43 | add_definitions(${LPMLOCATION}) 44 | 45 | # add LDPLLIBLOCATION 46 | string(CONCAT LDPLLIBLOCATION ${CMAKE_INSTALL_PREFIX} "/lib/ldpl") 47 | set(LDPLLIBLOCATION " -DLDPLLIBLOCATION='\"${LDPLLIBLOCATION}\"' ") 48 | add_definitions(${LDPLLIBLOCATION}) 49 | 50 | # Add executable target with source files listed in SOURCE_FILES variable 51 | add_executable(ldpl ${SOURCE_FILES}) 52 | 53 | if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin|Android") 54 | target_link_libraries(ldpl -static-libgcc -static-libstdc++) 55 | endif () 56 | 57 | install(TARGETS ldpl) 58 | install(FILES ldpl_lib/ldpl_lib.cpp TYPE LIB) 59 | install(FILES ../man/ldpl.1 TYPE MAN) 60 | 61 | -------------------------------------------------------------------------------- /src/aux/aux_code.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains auxiliary functions that add C++ code to the generated C++ 2 | * file */ 3 | 4 | // +---------------------------------------------+ 5 | // | TODO: comment and format this file properly | 6 | // +---------------------------------------------+ 7 | 8 | // This is called when we know all parameters of a subprocedure 9 | void open_subprocedure_code(compiler_state &state) { 10 | string name = state.current_subprocedure; 11 | vector ¶meters = state.subprocedures[name]; 12 | vector> types; 13 | string code; 14 | code = "void " + fix_identifier(name, false) + "("; 15 | for (size_t i = 0; i < parameters.size(); ++i) { 16 | string identifier = fix_identifier(parameters[i], true, state); 17 | string type = state.get_c_type(state.variables[name][parameters[i]]); 18 | code += type + " & " + identifier; 19 | if (i < parameters.size() - 1) code += ", "; 20 | types.push_back(state.variables[name][parameters[i]]); 21 | } 22 | if (!state.correct_subprocedure_types(name, types)) 23 | badcode( 24 | "SUB-PROCEDURE declaration parameter types doesn't match previous CALL", 25 | state.where); 26 | code += "){"; 27 | state.add_code(code, state.where); 28 | state.remove_expected_subprocedure(name); 29 | } 30 | 31 | void add_call_code(string &subprocedure, vector ¶meters, 32 | compiler_state &state) { 33 | string code = fix_identifier(subprocedure, false) + "("; 34 | for (size_t i = 0; i < parameters.size(); ++i) { 35 | if (is_number(parameters[i]) || is_string(parameters[i])) { 36 | // C++ doen't allow passing literals in reference parameters, we create 37 | // vars for them 38 | string literal_paramater_var = state.new_literal_parameter_var(); 39 | state.add_code((is_number(parameters[i]) ? "ldpl_number " : "graphemedText ") + 40 | literal_paramater_var + " = " + parameters[i] + ";", 41 | state.where); 42 | code += literal_paramater_var; 43 | } else { 44 | code += get_c_variable(state, parameters[i]); 45 | } 46 | if (i < parameters.size() - 1) code += ", "; 47 | } 48 | code += ");"; 49 | state.add_code(code, state.where); 50 | } -------------------------------------------------------------------------------- /src/aux/aux_container.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains auxiliary functions to split and access [multi]containers 2 | */ 3 | 4 | // +---------------------------------------------+ 5 | // | TODO: comment and format this file properly | 6 | // +---------------------------------------------+ 7 | 8 | // Given a 'full variable' (for example 'bar' or 'foo:0:"hi there":4'), a place 9 | // to store a variable name and a vector of strings to store all indexes, we get 10 | // the variable name and all its subindexes (if any) into the corresponding 11 | // variables. 12 | void split_vector(string &token, string &var_name, vector &indexes, 13 | compiler_state &state) { 14 | // First of all we want to know if we are dealing with a container variable, 15 | // that is a LIST of something of a MAP of something. Thus, we look for any 16 | // ':' in the full variable and store the possition of the (possible) first 17 | // one. 18 | size_t pos = token.find(":"); 19 | // If we didn't find any ':', then we are dealing with a scalar variable. 20 | // We just get its name (the full variable) and an empty vector of indexes. 21 | if (pos == string::npos) { 22 | var_name = token; 23 | indexes = {}; 24 | } 25 | // If we found a ':', but it's the last character in the full variable, that 26 | // means that we received something like 'foo:'. That's not right, so we rise 27 | // an error. 28 | else if (pos == token.size() - 1) 29 | error("Incomplete MAP or LIST access found (can't end on ':')."); 30 | else { 31 | // If none of the above happened, then our container is formatted correctly. 32 | // Now it's time to split it. As we can have something like foo IS NUMBER 33 | // LIST LIST bar IS NUMBER LIST and try to access foo using a value stored 34 | // in bar (for example foo:bar:0:1, meaning the foo[bar[0]][1]), we have to 35 | // know how many indexes our container expects. We tokenize our container 36 | // using the tokenize function. 37 | vector tokens; 38 | tokenize(token, tokens, state.where, true, ':'); 39 | // We take the first token as the variable name and then discard it. 40 | var_name = tokens[0]; 41 | tokens.erase(tokens.begin(), tokens.begin() + 1); 42 | // For each of the remaining, we have to check if its a variable or not. If 43 | // its a variable name, it may be another container. For each container we 44 | // find, we append as many indexes that container requires to the previous 45 | // token we found (the index name). So, if we had foo and bar as stated 46 | // above, the parsing of foo:bar:0:1 would go like this: 47 | // - foo is taken as the variable name and thus skipped. 48 | // - bar is a container that takes one index. We'll then 'skip' one index. 49 | // We then push bar to a list of tokens we've already checked. 50 | // - 1 is an index, and we said we were going to 'skip' one index. What we 51 | // do 52 | // is, instead of pushing 1 to the list of tokens we've already checked, 53 | // we append it to the last token we found as an index access. So we take 54 | // the 'bar' that's already in our checked list and we turn it into 55 | // 'bar:1'. 56 | // - 0 is an index. We are not skipping anymore indexes as we already 57 | // skipped 58 | // '1', so we push it into our checked tokens list. 59 | // Our checked tokens list will end up like {'bar:1', 0}, that are the 60 | // indexes of foo we are trying to access. 61 | vector checked_tokens; 62 | size_t tokens_to_skip = 0; 63 | for (size_t i = 0; i < tokens.size(); ++i) { 64 | if (is_number(tokens[i]) || is_string(tokens[i])) { 65 | if (tokens_to_skip > 0) { 66 | checked_tokens.back() += ":" + tokens[i]; 67 | tokens_to_skip--; 68 | } else { 69 | checked_tokens.push_back(tokens[i]); 70 | } 71 | } 72 | // If the current token is not a number nor a string, it must be a 73 | // variable name. 74 | else { 75 | // We get the types of the current variable. 76 | vector cvar_types = variable_type(tokens[i], state); 77 | // If the variable doesn't exist, we raise an error. 78 | if (cvar_types == vector{0}) { 79 | error("The variable " + tokens[i] + " doesn't exist in " + token + 80 | "."); 81 | } 82 | // We remove an element of the current variable types as the main type 83 | // (NUMBER or TEXT) never names a container type and thus cannot be 84 | // indexed. 85 | cvar_types.pop_back(); 86 | // If we had to skip this token, we append it to the last token we have. 87 | if (tokens_to_skip > 0) { 88 | checked_tokens.back() += ":" + tokens[i]; 89 | tokens_to_skip--; 90 | } else { 91 | checked_tokens.push_back(tokens[i]); 92 | } 93 | // We tell the splitter to skip this many tokens. 94 | tokens_to_skip += cvar_types.size(); 95 | } 96 | } 97 | // After we've got all indexes we needed, we store our checked tokens list 98 | // into the indexes vector, that is what we are going to return with the 99 | // variable name. 100 | indexes = checked_tokens; 101 | } 102 | } -------------------------------------------------------------------------------- /src/aux/aux_format.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains auxiliary functions that do text or number formatting */ 2 | 3 | void replace_whitespace(string &code) { 4 | // -- Replaces all whitespace within string -- 5 | for (char &c : code) { 6 | if (isspace(c)) { 7 | c = ' '; 8 | } 9 | } 10 | } 11 | 12 | string fix_identifier(string ident, bool isVar, compiler_state &state) { 13 | // -- Replaces invalid C++ characters in internal and external identifiers -- 14 | // when state is provided to fix_identifier it can fix external variables too. 15 | if (is_external(ident, state)) { 16 | return fix_external_identifier(ident, isVar); 17 | } else { 18 | return fix_identifier(ident, isVar); 19 | } 20 | } 21 | 22 | string fix_identifier(string identifier, bool isVariable) { 23 | // -- Appends a prefix based on its type to an identifier -- 24 | return string(isVariable ? "VAR_" : "SUBPR_") + fix_identifier(identifier); 25 | } 26 | 27 | string fix_identifier(string identifier) { 28 | // -- Replaces invalid C++ characters in identifiers -- 29 | const string validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890:"; 30 | ostringstream new_id; 31 | for (unsigned int i = 0; i < identifier.size(); ++i) { 32 | if (validChars.find(identifier[i]) != string::npos) { 33 | new_id << identifier[i]; 34 | } else { 35 | new_id << "c" << (unsigned int)identifier[i] << "_"; 36 | } 37 | } 38 | return new_id.str(); 39 | } 40 | 41 | string fix_external_identifier(string identifier, bool isVariable) { 42 | // -- Replaces invalid C++ characters in external identifiers -- 43 | // External identifiers are used by C++ extensions and thus have a simpler but 44 | // more restrictive name mangling algorithm: The only characters allowed are 45 | // `A-Z`, `0-9`, and `_`. All other characters are converted to `_`. 46 | const string validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890:"; 47 | string new_id; 48 | for (unsigned int i = 0; i < identifier.size(); ++i) { 49 | if (validChars.find(identifier[i]) != string::npos) { 50 | new_id += identifier[i]; 51 | } else { 52 | new_id += "_"; 53 | } 54 | } 55 | return new_id; 56 | } 57 | 58 | string &escape_c_quotes(string &str) { 59 | // -- Replaces all " in the passed string for \" -- 60 | for (unsigned int i = 0; i < str.size(); ++i) { 61 | if (str[i] == '"') { 62 | str.erase(i, 1); 63 | str.insert(i, "\\\""); 64 | ++i; 65 | } 66 | } 67 | return str; 68 | } 69 | 70 | string &escape_c_backslashes(string &str) { 71 | // -- Replaces all " in the passed string for \" -- 72 | for (unsigned int i = 0; i < str.size(); ++i) { 73 | if (str[i] == '\\') { 74 | str.erase(i, 1); 75 | str.insert(i, "\\\\"); 76 | ++i; 77 | } 78 | } 79 | return str; 80 | } 81 | 82 | string expandHomeDirectory(string &filename) { 83 | // -- Expands a home directory (normaly when the character ~ is used) -- 84 | #if defined(_WIN32) 85 | return filename; 86 | #else 87 | string homeDir = exec("echo $HOME"); 88 | trim(homeDir); 89 | string newPath = ""; 90 | for (size_t i = 0; i < filename.length(); ++i) { 91 | if (filename[i] == '~') { 92 | newPath += homeDir; 93 | } else { 94 | newPath += filename[i]; 95 | } 96 | } 97 | return newPath; 98 | #endif 99 | } -------------------------------------------------------------------------------- /src/aux/aux_info.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains auxiliary functions that display information about the 2 | * compiler and the system */ 3 | 4 | /// @brief Displays the LDPL version information message. 5 | void displayVersionInfo() 6 | { 7 | cout << endl; 8 | cout << " This is \033[32;1mLDPL version " << VERSION << "\033[0m '\033[32;1m" 9 | << VERSIONNAME << "\033[0m'." << endl; 10 | cout << endl; 11 | cout << " Copyright 2018-2024, \033[35;1mLartu\033[0m (www.lartu.net)." 12 | << endl; 13 | cout << " Built with amazing contributions from \033[35;1mChris West\033[0m, " 14 | "\033[35;1mDamián Garro\033[0m," 15 | << endl; 16 | cout << " \033[35;1mIgnacio Losiggio\033[0m and other wonderful contributors." 17 | << endl; 18 | cout << endl; 19 | cout << " The LDPL Home Page can be found at " 20 | "\033[36;1mwww.ldpl-lang.org\033[0m." 21 | << endl; 22 | cout << " The LDPL source code is available at " 23 | "\033[36;1mwww.github.com/lartu/ldpl\033[0m." 24 | << endl; 25 | cout << endl; 26 | cout << " Complete documentation for LDPL should be found on this system" 27 | << endl; 28 | cout << " using '\033[33;1mman ldpl\033[0m'. If you have access to the " 29 | "internet, the" 30 | << endl; 31 | cout << " documentation can also be found online at " 32 | "\033[36;1mdocs.ldpl-lang.org\033[0m." 33 | << endl; 34 | cout << endl; 35 | cout << " LDPL may be copied only under the terms of the Apache License" 36 | << endl; 37 | cout << " Version 2.0, which may be found in the LDPL repository." << endl; 38 | cout << endl; 39 | cout << " This binary was compiled on \033[31;1m" << COMPILEDATE 40 | << "\033[0m at \033[31;1m" << COMPILEHOUR << "\033[0m." << endl; 41 | cout << endl; 42 | } 43 | 44 | /// @brief Displays the LDPL help information message. 45 | void displayHelpInfo() 46 | { 47 | cout << endl; 48 | cout << " \033[33;1mUsage:\033[0m" << endl; 49 | cout << " ldpl [-i='']... |-c" << endl; 50 | cout << " [-o=''|-r] [-f='']... [-n]" << endl; 51 | cout << " ldpl [-v|-h]" << endl; 52 | cout << endl; 53 | cout << " \033[33;1mOptions:\033[0m" << endl; 54 | cout << " -v --version Display LDPL version information" 55 | << endl; 56 | cout << " -h --help Display this information" << endl; 57 | cout << " -r Display generated C++ code" << endl; 58 | cout << " -o= Set output file for compiled binary" 59 | << endl; 60 | cout << " -i= Include file in current compilation" 61 | << endl; 62 | cout << " -f= Pass a flag to the C++ compiler" << endl; 63 | #ifdef STATIC_BUILDS 64 | cout << " -n --non-static Disables static binary building" << endl; 65 | #endif 66 | cout << " -c Compile from standard input" << endl; 67 | cout << endl; 68 | cout << " Complete documentation for LDPL should be found on this system" 69 | << endl; 70 | cout << " using '\033[33;1mman ldpl\033[0m'. If you have access to the " 71 | "internet, the" 72 | << endl; 73 | cout << " documentation can also be found online at " 74 | "\033[36;1mdocs.ldpl-lang.org\033[0m." 75 | << endl; 76 | cout << endl; 77 | } 78 | 79 | /// @brief Prints a non-fatal warning message. 80 | void warning(const string &msg) 81 | { 82 | cerr << "\033[1;35mLDPL Warning: "; 83 | cerr << msg; 84 | cerr << "\033[0m" << endl; 85 | } 86 | 87 | /// @brief Prints a message preceded by a * 88 | void bullet_msg(const string &msg) 89 | { 90 | cerr << "\033[1;33m*\033[0m "; 91 | cerr << msg << endl; 92 | } 93 | 94 | /// @brief Shows where the error was in a code error. 95 | void badcode(const string &msg, const code_location where) 96 | { 97 | error(msg + " (\033[0m" + where.current_file + ":" + to_string(where.current_line) + "\033[1;31m)"); 98 | } 99 | 100 | /// @brief Shows a fatal error message and exits. 101 | void error(const string &msg) 102 | { 103 | cerr << "\033[1;31mLDPL Error: "; 104 | cerr << msg; 105 | cerr << "\033[0m" << endl; 106 | exit(1); 107 | } 108 | 109 | /// @brief Returns the current operating system 110 | string current_os() 111 | { 112 | // https://sourceforge.net/p/predef/wiki/OperatingSystems/ 113 | #if defined(__linux__) 114 | return "LINUX"; 115 | #elif defined(__APPLE__) 116 | return "MACOS"; 117 | #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) 118 | return "BSD"; 119 | #elif defined(__ANDROID__) 120 | return "ANDROID"; 121 | #elif defined(__EMSCRIPTEN__) 122 | return "EMSCRIPTEN"; 123 | #else 124 | return "UNKNOWN"; 125 | #endif 126 | } -------------------------------------------------------------------------------- /src/aux/aux_line_like.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains the line_like function used for pattern matching lines */ 2 | 3 | // +---------------------------------------------+ 4 | // | TODO: comment and format this file properly | 5 | // +---------------------------------------------+ 6 | 7 | #include "../ldpl.h" 8 | 9 | // Check if the tokens of a line passed are like the ones of a model line 10 | bool line_like(string model_line, vector &tokens, 11 | compiler_state &state) 12 | { 13 | // Tokenize model line 14 | vector model_tokens; 15 | tokenize(model_line, model_tokens, state.where, false, ' '); 16 | // Check that tokens match between line and model line 17 | if (tokens.size() < model_tokens.size()) 18 | return false; 19 | unsigned int i = 0; 20 | unsigned int j = 0; 21 | for (; i < model_tokens.size(); ++i) 22 | { 23 | if (model_tokens[i] == "$name") // $name is a valid identifier for a 24 | // variable or a sub-procedure 25 | { 26 | for (char letter : tokens[j]) 27 | if (letter == ':') 28 | return false; 29 | for (char letter : tokens[j]) 30 | if (letter == '\"') 31 | return false; 32 | for (char letter : tokens[j]) 33 | if (letter == '(') 34 | return false; 35 | for (char letter : tokens[j]) 36 | if (letter == ')') 37 | return false; 38 | if (is_number(tokens[j])) 39 | return false; 40 | if (tokens[j] == "+") 41 | return false; 42 | if (tokens[j] == "-") 43 | return false; 44 | if (tokens[j] == "*") 45 | return false; 46 | if (tokens[j] == "/") 47 | return false; 48 | if (tokens[j] == "%") 49 | return false; 50 | } 51 | else if (model_tokens[i] == "$num-var") // $num-var is NUMBER variable 52 | { 53 | if (!is_num_var(tokens[j], state)) 54 | return false; 55 | } 56 | else if (model_tokens[i] == "$str-var") // $str-var is TEXT variable 57 | { 58 | if (!is_txt_var(tokens[j], state)) 59 | return false; 60 | } 61 | // $var is either a NUMBER variable or a TEXT variable 62 | else if (model_tokens[i] == "$var") 63 | { 64 | if (!is_scalar_variable(tokens[j], state)) 65 | return false; 66 | } 67 | else if (model_tokens[i] == "$anyVar") // $anyVar is any variable 68 | { 69 | if (!variable_exists(tokens[j], state)) 70 | return false; 71 | } 72 | else if (model_tokens[i] == "$scalar-map") 73 | { // $scalar-map is TEXT MAP, NUMBER MAP 74 | if (!is_scalar_map(tokens[j], state)) 75 | return false; 76 | } 77 | else if (model_tokens[i] == "$num-vec") // $num-vec is NUMBER vector 78 | { 79 | if (!is_num_map(tokens[j], state)) 80 | return false; 81 | } 82 | else if (model_tokens[i] == "$str-vec") // $str-vec is TEXT vector 83 | { 84 | if (!is_txt_map(tokens[j], state)) 85 | return false; 86 | } 87 | else if (model_tokens[i] == "$list") 88 | { // $list is a LIST 89 | if (variable_type(tokens[j], state).back() != 3) 90 | return false; 91 | } 92 | else if (model_tokens[i] == "$map") 93 | { // $map is a MAP 94 | if (variable_type(tokens[j], state).back() != 4) 95 | return false; 96 | } 97 | else if (model_tokens[i] == "$scalar-list") 98 | { // $scalar-list is a LIST of scalar values 99 | if (!is_scalar_list(tokens[j], state)) 100 | return false; 101 | } 102 | else if (model_tokens[i] == "$num-list") // $num-vec is NUMBER list 103 | { 104 | if (!is_num_list(tokens[j], state)) 105 | return false; 106 | } 107 | else if (model_tokens[i] == "$str-list") // $str-vec is TEXT list 108 | { 109 | if (!is_txt_list(tokens[j], state)) 110 | return false; 111 | } 112 | else if (model_tokens[i] == "$list-list") // $str-vec is a LIST of LISTs 113 | { 114 | if (!is_list_list(tokens[j], state)) 115 | return false; 116 | } 117 | else if (model_tokens[i] == "$map-list") // $str-vec is LIST of MAPs 118 | { 119 | if (!is_map_list(tokens[j], state)) 120 | return false; 121 | } 122 | else if (model_tokens[i] == "$collection") // $collection is either a MAP or a LIST 123 | { 124 | // if(!is_scalar_map(tokens[j], state) && !is_scalar_list(tokens[j], 125 | // state) && (variable_type(tokens[j], state).size() < 2)) return false; 126 | if (variable_type(tokens[j], state).size() < 2) 127 | return false; 128 | } 129 | else if (model_tokens[i] == "$literal") // $literal is either a NUMBER or a TEXT 130 | { 131 | if (!is_string(tokens[j]) && !is_number(tokens[j])) 132 | return false; 133 | } 134 | else if (model_tokens[i] == "$string") // $string is a TEXT literal 135 | { 136 | if (!is_string(tokens[j])) 137 | return false; 138 | } 139 | else if (model_tokens[i] == "$number") // $number is a NUMBER literal 140 | { 141 | if (!is_number(tokens[j])) 142 | return false; 143 | } 144 | else if (model_tokens[i] == "$expression") // $expression is NUMBER, 145 | // TEXT, TEXT-VAR, NUMBER-VAR 146 | { 147 | if (!is_expression(tokens[j], state)) 148 | return false; 149 | } 150 | else if (model_tokens[i] == "$str-expr") // $str-expr is either a TEXT or a TEXT variable 151 | { 152 | if (!is_txt_expr(tokens[j], state)) 153 | return false; 154 | } 155 | else if (model_tokens[i] == "$num-expr") // $num-expr is either a NUMBER 156 | // or a NUMBER variable 157 | { 158 | if (!is_num_expr(tokens[j], state)) 159 | return false; 160 | } 161 | else if (model_tokens[i].find("$var-type-") == 0) // variable with a given type number 162 | { 163 | vector actual_type = variable_type(tokens[j], state); 164 | string expected_type = model_tokens[i].substr(10); 165 | if (actual_type.size() != expected_type.length()) 166 | return false; 167 | for (size_t t = 0; t < actual_type.size(); ++t) 168 | { 169 | if ((int)actual_type[t] != expected_type[t] - '0') 170 | return false; 171 | } 172 | } 173 | else if (model_tokens[i] == "$natural") // $natural is an integer greater than 0 174 | { 175 | if (!is_natural(tokens[j])) 176 | return false; 177 | } 178 | else if (model_tokens[i] == "$display") // multiple NUMBER, TEXT, TEXT-VAR, NUMBER-VAR 179 | { 180 | for (; j < tokens.size(); ++j) 181 | { 182 | if (!is_expression(tokens[j], state)) 183 | return false; 184 | } 185 | } 186 | else if (model_tokens[i] == "$subprocedure") // $subprocedure is a SUB-PROCEDURE 187 | { 188 | if (!is_subprocedure(tokens[j], state)) 189 | return false; 190 | } 191 | else if (model_tokens[i] == "$external") // $external is a C++ function defined elsewhere 192 | { 193 | return !is_subprocedure(tokens[j], state) && 194 | !is_expression(tokens[j], state); 195 | } 196 | else if (model_tokens[i] == "$label") // $label is a GOTO label 197 | { 198 | return is_label(tokens[j]); 199 | } 200 | else if (model_tokens[i] == "$math") // $math is a math expression 201 | { 202 | vector maths; // further tokenize math expressions 203 | string math_token = ""; 204 | for (; j < tokens.size(); ++j) 205 | { 206 | for (unsigned int z = 0; z < tokens[j].size(); ++z) 207 | { 208 | if (tokens[j][z] == '(' || tokens[j][z] == ')') 209 | { 210 | if (!math_token.empty()) 211 | maths.push_back(math_token); 212 | math_token = tokens[j][z]; 213 | maths.push_back(math_token); 214 | math_token = ""; 215 | } 216 | else 217 | { 218 | math_token += tokens[j][z]; 219 | } 220 | } 221 | if (!math_token.empty()) 222 | maths.push_back(math_token); 223 | math_token = ""; 224 | } 225 | // replace LDPL line tokens with new math tokens 226 | tokens.erase(tokens.begin() + i, tokens.end()); 227 | tokens.insert(tokens.end(), maths.begin(), maths.end()); 228 | 229 | // validate the new tokens 230 | for (unsigned int z = i; z < tokens.size(); ++z) 231 | { 232 | if (!is_math_symbol(tokens[z]) && !is_expression(tokens[z], state)) 233 | return false; 234 | } 235 | return true; 236 | } 237 | else if (model_tokens[i] == "$condition") // $condition is a IF/WHILE condition 238 | { 239 | // Skip to the last token (THEN/DO), 240 | // the condition is validated in get_c_condition 241 | j = tokens.size() - 1; 242 | continue; 243 | } 244 | else if (model_tokens[i] == "$anything") 245 | return true; 246 | else if (model_tokens[i] != tokens[j]) 247 | return false; 248 | ++j; 249 | } 250 | if (j < tokens.size()) 251 | return false; 252 | return true; 253 | } -------------------------------------------------------------------------------- /src/aux/aux_state.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains auxiliary functions that check the current compilation 2 | * state */ 3 | 4 | #include "../ldpl.h" 5 | 6 | bool is_num_map(string &token, compiler_state &state) 7 | { 8 | // -- Returns if the variable is a NUMBER MAP or an access to a multicontainer 9 | // that results in a NUMBER MAP -- 10 | vector type = variable_type(token, state); 11 | if (type.size() == 2 && type[0] == 1 && type[1] == 4) 12 | return true; 13 | return false; 14 | } 15 | 16 | bool is_txt_map(string &token, compiler_state &state) 17 | { 18 | // -- Returns if the variable is a TEXT MAP or an access to a multicontainer 19 | // that results in a TEXT MAP -- 20 | vector type = variable_type(token, state); 21 | if (type.size() == 2 && type[0] == 2 && type[1] == 4) 22 | return true; 23 | return false; 24 | } 25 | 26 | bool is_num_list(string &token, compiler_state &state) 27 | { 28 | // -- Returns if the variable is a NUMBER LIST or an access to a 29 | // multicontainer that results in a NUMBER LIST -- 30 | vector type = variable_type(token, state); 31 | if (type.size() == 2 && type[0] == 1 && type[1] == 3) 32 | return true; 33 | return false; 34 | } 35 | 36 | bool is_txt_list(string &token, compiler_state &state) 37 | { 38 | // -- Returns if the variable is a TEXT MAP or an access to a multicontainer 39 | // that results in a TEXT MAP -- 40 | vector type = variable_type(token, state); 41 | if (type.size() == 2 && type[0] == 2 && type[1] == 3) 42 | return true; 43 | return false; 44 | } 45 | 46 | bool is_list_list(string &token, compiler_state &state) 47 | { 48 | // -- Returns if the variable is a NUMBER/TEXT LIST LIST multicontainer or a 49 | // multicontainer access that results in a LIST of LISTs -- 50 | vector type = variable_type(token, state); 51 | if (type.size() >= 2 && type[type.size() - 2] == 3 && type.back() == 3) 52 | return true; 53 | return false; 54 | } 55 | 56 | bool is_map_list(string &token, compiler_state &state) 57 | { 58 | // -- Returns if the variable is a multicontainer NUMBER/TEXT LIST MAP or a 59 | // multicontainer access that results in a LIST of MAPs -- 60 | vector type = variable_type(token, state); 61 | if (type.size() >= 2 && type[type.size() - 2] == 4 && type.back() == 3) 62 | return true; 63 | return false; 64 | } 65 | 66 | bool is_scalar_map(string &token, compiler_state &state) 67 | { 68 | // -- Returns if the variable is a NUMBER MAP or an access to a multicontainer 69 | // that results in a NUMBER MAP -- 70 | // -- or if the variable is a TEXT MAP or an access to a multicontainer that 71 | // results in a TEXT MAP -- 72 | return is_num_map(token, state) || is_txt_map(token, state); 73 | } 74 | 75 | bool is_map_map(string &token, compiler_state &state) 76 | { 77 | // -- Returns if the variable is a NUMBER/TEXT MAP MAP multicontainer or a 78 | // multicontainer access that results in a MAP of MAPs -- 79 | vector type = variable_type(token, state); 80 | if (type.size() >= 2 && type[type.size() - 2] == 4 && type.back() == 4) 81 | return true; 82 | return false; 83 | } 84 | 85 | bool is_map(string &token, compiler_state &state) 86 | { 87 | // -- Returns true if the variable is a MAP, regardless of a map of what 88 | // (multicontainer or not) -- 89 | vector type = variable_type(token, state); 90 | return type.back() == 4; 91 | } 92 | 93 | bool is_scalar_list(string &token, compiler_state &state) 94 | { 95 | // -- Returns if the variable is a NUMBER LIST or an access to a 96 | // multicontainer that results in a NUMBER LIST -- 97 | // -- or if the variable is a TEXT LIST or an access to a multicontainer that 98 | // results in a TEXT LIST -- 99 | return is_num_list(token, state) || is_txt_list(token, state); 100 | } 101 | 102 | bool is_num_var(string &token, compiler_state &state) 103 | { 104 | // -- Checks if token is a NUMBER variable (or an access to a container that 105 | // results in a NUMBER variable) -- 106 | return (variable_type(token, state) == vector{1}); 107 | } 108 | 109 | bool is_txt_var(string &token, compiler_state &state) 110 | { 111 | // -- Checks if token is a TEXT variable (or an access to a container that 112 | // results in a TEXT variable) -- 113 | return (variable_type(token, state) == vector{2}); 114 | } 115 | 116 | bool is_scalar_variable(string &token, compiler_state &state) 117 | { 118 | // -- Returns is an identifier is a valid scalar variable or an access that 119 | // results in one -- 120 | return is_num_var(token, state) || is_txt_var(token, state); 121 | } 122 | 123 | bool is_num_expr(string &token, compiler_state &state) 124 | { 125 | // -- Returns is an identifier is a valid scalar variable or number or an 126 | // access that results in one -- 127 | return is_num_var(token, state) || is_number(token); 128 | } 129 | 130 | bool is_txt_expr(string &token, compiler_state &state) 131 | { 132 | // -- Returns is an identifier is a valid scalar variable or text or an access 133 | // that results in one -- 134 | return is_txt_var(token, state) || is_string(token); 135 | } 136 | 137 | bool is_expression(string &token, compiler_state &state) 138 | { 139 | // -- Returns is an identifier is a valid scalar variable or text or number or 140 | // an access that results in one -- 141 | return is_num_expr(token, state) || is_txt_expr(token, state); 142 | } 143 | 144 | bool is_external(string &token, compiler_state &state) 145 | { 146 | // -- Returns if an identifier maps to an external variable -- 147 | return state.externals[token]; 148 | } 149 | 150 | bool variable_exists(string &token, compiler_state &state) 151 | { 152 | // -- Returns if a variable has been declared or not -- 153 | // (Bear in mind that myList is a variable, myList:0 is not, that's an access 154 | // for all this function is concerned) 155 | return variable_type(token, state) != vector{0}; 156 | } 157 | 158 | bool is_subprocedure(string &token, compiler_state &state) 159 | { 160 | // -- Returns if an identifier maps to a valid, existing sub-procedure -- 161 | for (auto &subprocedure : state.subprocedures) 162 | if (subprocedure.first == token) 163 | return true; 164 | return false; 165 | } 166 | 167 | bool in_procedure_section(compiler_state &state) 168 | { 169 | // -- Returns if the compiler is currently compiling a procedure section or 170 | // not -- 171 | if (state.section_state == 3) 172 | { 173 | // We're inside a SUB-PROCEDURE procedure with no sections 174 | state.section_state = 2; 175 | open_subprocedure_code(state); 176 | } 177 | return state.section_state == 2; 178 | } 179 | 180 | vector variable_type(string &token, compiler_state &state) 181 | { 182 | // -- Returns the LDPL internal representation of the type of a variable -- 183 | // 184 | // Return the number of the type or {0} if the variable doesn't exist. This 185 | // function can take full variables (foo:0:"hi"). Returns all the types of the 186 | // variable. If foo is number map list {1, 4, 3} and we just pass it foo:0, it 187 | // will return {1, 4}, that is the type foo:0 has. 188 | // 189 | // Variables can have mixed types. For example, a LIST of MAPS of NUMBERS 190 | // called foo is a NUMBER when you access both containers (foo:0:"hi") a 191 | // NUMBER MAP when you access just the list (foo:0) or a NUMBER MAP LIST when 192 | // you access nothing (foo) So we first split the full variable with accesses 193 | // and everything into tokens by : 194 | vector tokens; 195 | string varName = ""; 196 | tokenize(token, tokens, state.where, true, ':'); 197 | // So, for example, foo:0:"hi" will be split into {foo, 0, "hi"} 198 | // We take the first element as the variable name 199 | varName = tokens[0]; 200 | // Then we check if the variable exists. If it does, we store its types in a 201 | // variable. 202 | vector types; 203 | if (state.variables[state.current_subprocedure].count(varName) > 0) 204 | types = state.variables[state.current_subprocedure][varName]; 205 | else if (state.variables[""].count(varName) > 0) 206 | types = state.variables[""][varName]; 207 | // If the variable wasn't found, we return {0} 208 | else 209 | return {0}; 210 | // If it was found, though, we want to get its current type. 211 | // If, in the example above, we had foo:0, then our current type would be {1, 212 | // 4} meaning, a NUMBER (1) MAP (4). But the type the variable has stored is 213 | // {1, 4, 3}, because it is a NUMBER MAP LIST (LIST is 3). We have to remove 214 | // all accessed elements from the type vector ({1, 4, 3} would turn into {1, 215 | // 4} because we accessed the list when we did :0). The number of elements to 216 | // pop from the vector is equal to the number of : found within the full 217 | // variable (foo:0). As we've already splitted the variable into tokens, the 218 | // number of elements to pop from the vector is equal to the number of tokens 219 | // we have minus one. If the container access should contain other container 220 | // accesses (for example foo:bar:0), the thing changes, and we must make sure 221 | // to discard those indexes that access the other containers and not the one 222 | // we are trying to get the types of. 223 | size_t tokensToSkip = 0; 224 | for (size_t i = 1; i < tokens.size(); ++i) 225 | { 226 | // If the current token is a scalar literal, we can skip it safely. 227 | if (is_number(tokens[i]) || is_string(tokens[i])) 228 | { 229 | if (tokensToSkip > 0) 230 | tokensToSkip--; 231 | } 232 | // If it's not, then it must be a variable name. 233 | else 234 | { 235 | // If the variable doesn't exist in the current context, we rise an error. 236 | if (state.variables[state.current_subprocedure].count(tokens[i]) == 0 && 237 | state.variables[""].count(tokens[i]) == 0) 238 | error("The variable " + tokens[i] + " used in " + token + 239 | " doesn't exist."); 240 | vector cvar_types = variable_type(tokens[i], state); 241 | if (cvar_types.size() > 1) 242 | // If the variable exists and is a container, then we skip as many 243 | // tokens as that variable takes. 244 | tokensToSkip += cvar_types.size() - 1; 245 | else 246 | // If the variable exists and is a scalar, we can skip it 247 | if (tokensToSkip > 0) 248 | tokensToSkip--; 249 | } 250 | if (tokensToSkip == 0) 251 | types.pop_back(); 252 | } 253 | // We return {0} if there is an incomplete container access 254 | // that must be complete to resolve as a scalar index 255 | if (tokensToSkip > 0) 256 | return {0}; 257 | // Now we have the types and can return them. 258 | return types; 259 | } -------------------------------------------------------------------------------- /src/aux/aux_tokenizer.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains functions used to tokenize an LDPL source file */ 2 | 3 | // +---------------------------------------------+ 4 | // | TODO: comment and format this file properly | 5 | // +---------------------------------------------+ 6 | 7 | // Tokenizes a line splitting by 'splitChar' with optional convertion of tokens 8 | // to uppercase (except in strings) 9 | void tokenize(string &line, vector &tokens, code_location &where, 10 | bool uppercase, char splitChar) { 11 | bool in_string = false; 12 | string current_token = ""; 13 | // For each letter in the line 14 | for (unsigned int i = 0; i < line.size(); ++i) { 15 | char letter = line[i]; 16 | if (letter == splitChar) { 17 | if (in_string) 18 | current_token += letter; 19 | else { 20 | if (current_token.size() > 0) tokens.push_back(current_token); 21 | current_token = ""; 22 | } 23 | } else if (letter == '"') { 24 | in_string = !in_string; 25 | current_token += letter; 26 | } else if (letter == '\\' && in_string) { 27 | if (i < line.size() - 1) { 28 | char next_letter = line[++i]; 29 | switch (next_letter) { 30 | case '\\': 31 | case '"': 32 | case '0': 33 | case 'a': 34 | case 'b': 35 | case 't': 36 | case 'n': 37 | case 'v': 38 | case 'f': 39 | case 'r': 40 | case 'e': 41 | current_token += "\\" + string(1, next_letter); 42 | break; 43 | default: 44 | badcode("unknown escape sequence", where); 45 | } 46 | } else 47 | badcode("\\ found alone on a string", where); 48 | } else if (letter == '#') // Comment character 49 | { 50 | if (in_string) 51 | current_token += letter; 52 | else { 53 | if (current_token.size() > 0) tokens.push_back(current_token); 54 | return; 55 | } 56 | } else { 57 | current_token += (uppercase && !in_string) ? toupper(letter) : letter; 58 | } 59 | if (i == line.size() - 1 && letter != splitChar) { 60 | if (in_string) badcode("Unterminated string", where); 61 | if (current_token.size() > 0) tokens.push_back(current_token); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/aux/aux_typecheck.cpp: -------------------------------------------------------------------------------- 1 | /* This file contains auxiliary functions to check data type compliance */ 2 | #include "../ldpl.h" 3 | 4 | bool is_number(string &number) 5 | { 6 | // -- Checks if a string is a valid real number -- 7 | unsigned int firstchar = 0; 8 | if (number[0] == '-') 9 | firstchar = 1; 10 | if (number[firstchar] == '.') 11 | return false; // .12 is not a valid decimal in LDPL, 0.12 is. 12 | if (number[firstchar] == '+') 13 | return false; // +5 is not a valid decimal in LDPL, 5 is. 14 | istringstream iss(number); 15 | double f; 16 | iss >> f; 17 | bool isNumber = iss.eof() && !iss.fail(); 18 | // If it is a number, it might be an octal literal (e.g. 025, 067, 003 etc) 19 | // so we proceed to fix the original number to make it decimal. 20 | if (isNumber) 21 | { 22 | string f_number = ""; 23 | unsigned int i; 24 | for (i = 1; i < number.length(); ++i) 25 | { 26 | if (number[i - 1] != '0') 27 | { 28 | // If prev char not 0 29 | if (number[i - 1] == '-') 30 | // if prev char is -, continue check 31 | f_number += '-'; 32 | else 33 | // if prev char is number, break 34 | break; 35 | } 36 | else if (number[i] == '.') 37 | // If prev number is 0 38 | f_number += '0'; 39 | } 40 | f_number += number.substr(i - 1); 41 | number = f_number; 42 | return true; 43 | } 44 | else 45 | return false; 46 | } 47 | 48 | bool is_natural(string number) 49 | { 50 | // -- Checks if a string is a valid natural number -- 51 | if (!is_number(number)) 52 | return false; 53 | if (stod(number) <= 0) 54 | return false; 55 | for (char l : number) 56 | if (l == '.') 57 | return false; 58 | return true; 59 | } 60 | 61 | bool is_label(string &token) 62 | { 63 | // -- Checks if a string is a valid label -- 64 | for (char letter : token) 65 | if (letter == '\"') 66 | return false; 67 | return true; 68 | } 69 | 70 | bool is_math_symbol(string &token) 71 | { 72 | // -- Checks if a string is a valid math symbol -- 73 | string syms = "+-*/()%"; 74 | return token.size() == 1 && syms.find(token[0]) != string::npos; 75 | } 76 | 77 | bool is_string(string &token) 78 | { 79 | // -- Checks if a string is a valid LDPL string -- 80 | if (token.size() < 2 || token[0] != '"' || token[token.size() - 1] != '"') 81 | return false; 82 | // Check for unescaped quotes 83 | for (unsigned int i = 1; i < token.size() - 1; ++i) 84 | { 85 | if (token[i] == '\"' && token[i - 1] != '\\') 86 | return false; 87 | } 88 | return true; 89 | } -------------------------------------------------------------------------------- /src/data_types/code_location.h: -------------------------------------------------------------------------------- 1 | struct code_location { 2 | string current_file; 3 | int current_line; 4 | }; -------------------------------------------------------------------------------- /src/data_types/compiler_state.h: -------------------------------------------------------------------------------- 1 | // TODO: Change vectors to maps 2 | struct compiler_state { 3 | unsigned int section_state = 0; 4 | // 0 no section, 1 data or local, 2 procedure, 3 sub-procedure start, 4 5 | // parameters Code to output (plain C code) 6 | code_location where = {"", 0}; 7 | vector variable_code; 8 | vector output_code; 9 | vector subroutine_code; // code outside main() 10 | // variables 11 | map>> 12 | variables; // map>> (variables are stored here) 14 | map externals; // variables defined in c++ extensions 15 | // 1 number, 2 text, 3 list, 4 map --> <2, 3, 4, 4> means, for example, map of 16 | // map of list of text 17 | map> 18 | subprocedures; // subprocedure -> list of parameter identifiers 19 | void add_var_code(string code) { this->variable_code.push_back(code); } 20 | void add_code(string code) { 21 | auto& output = 22 | current_subprocedure == "" ? this->output_code : this->subroutine_code; 23 | output.push_back(code); 24 | } 25 | void add_code(string code, code_location where) { 26 | auto& output = 27 | current_subprocedure == "" ? this->output_code : this->subroutine_code; 28 | // TODO add a debug flag for this 29 | // if (line_num) 30 | // output.push_back("#line " + to_string(where.current_line) + " \"" + 31 | // escape_c_quotes(where.current_file) + "\""); 32 | output.push_back(code); 33 | } 34 | bool open_quote = false; 35 | string current_subprocedure = ""; 36 | int open_loops = 0; 37 | stack 38 | block_stack; // 0 sub-procedure, 1 if or else if, 2 while/for, 3 else 39 | void open_subprocedure(string& subprocedure) { 40 | current_subprocedure = subprocedure; 41 | block_stack.push(0); 42 | } 43 | void close_subprocedure() { 44 | current_subprocedure = ""; 45 | block_stack.pop(); 46 | } 47 | bool closing_subprocedure() { 48 | return !block_stack.empty() && block_stack.top() == 0; 49 | } 50 | void open_if() { block_stack.push(1); } 51 | void close_if() { block_stack.pop(); } 52 | bool closing_if() { return !block_stack.empty() && block_stack.top() == 1; } 53 | void open_else() { block_stack.top() = 3; } 54 | bool closing_else() { return !block_stack.empty() && block_stack.top() == 3; } 55 | void open_loop() { 56 | ++open_loops; 57 | block_stack.push(2); 58 | } 59 | void close_loop() { 60 | --open_loops; 61 | block_stack.pop(); 62 | } 63 | bool closing_loop() { return !block_stack.empty() && block_stack.top() == 2; } 64 | // We keep track of declared variables used in range-based loops 65 | int range_vars = 0; 66 | string new_range_var() { return "RVAR_" + to_string(range_vars++); } 67 | // We keep track of declared variables used to pass literal parameters 68 | int literal_paramter_vars = 0; 69 | string new_literal_parameter_var() { 70 | return "LPVAR_" + to_string(literal_paramter_vars++); 71 | } 72 | // Adds a subprocedure that has been called but hasn't been declared. 73 | // If it hasn't been declared when compilation reaches the end of the source, 74 | // an error is risen. 75 | vector>>> 76 | expected_subprocedures; // subprocedure -> list of parameter types 77 | void add_expected_subprocedure(string name, string fixed_name, 78 | vector>& types) { 79 | for (auto& subprocedure : expected_subprocedures) 80 | if (subprocedure.first == name) return; 81 | expected_subprocedures.emplace_back(name, types); 82 | string code = "void " + fixed_name + "("; 83 | for (size_t i = 0; i < types.size(); ++i) { 84 | code += get_c_type(types[i]) + "&"; 85 | if (i < types.size() - 1) code += ", "; 86 | } 87 | code += ");"; 88 | add_var_code(code); 89 | } 90 | void remove_expected_subprocedure(string& name) { 91 | for (auto it = expected_subprocedures.begin(); 92 | it != expected_subprocedures.end(); ++it) { 93 | if (it->first == name) { 94 | expected_subprocedures.erase(it); 95 | return; 96 | } 97 | } 98 | } 99 | bool correct_subprocedure_types(string& name, 100 | vector>& types) { 101 | for (auto& subprocedure : expected_subprocedures) 102 | if (subprocedure.first == name) return types == subprocedure.second; 103 | for (auto& subprocedure : subprocedures) 104 | if (subprocedure.first == name) { 105 | vector> actual_types; 106 | for (auto& parameter : subprocedure.second) 107 | actual_types.push_back(variables[name][parameter]); 108 | return types == actual_types; 109 | } 110 | return true; 111 | } 112 | stack working_dir; 113 | vector> 114 | custom_statements; // (statement model line, subprocedure name) 115 | 116 | string get_c_type(vector& type) { 117 | string c_type = ""; 118 | for (auto number_type : type) { 119 | if (number_type == 1) { 120 | c_type = "ldpl_number"; 121 | } else if (number_type == 2) { 122 | c_type = "graphemedText"; 123 | } else if (number_type == 3) { 124 | c_type = "ldpl_list<" + c_type + ">"; 125 | } else if (number_type == 4) { 126 | c_type = "ldpl_vector<" + c_type + ">"; 127 | } 128 | } 129 | return c_type; 130 | } 131 | }; -------------------------------------------------------------------------------- /src/ldpl.cpp: -------------------------------------------------------------------------------- 1 | // +=============================================+ 2 | // | | 3 | // | The __ ____ ____ __ | 4 | // | / / / __ \/ __ \/ / | 5 | // | / / / / / / /_/ / / | 6 | // | / /___/ /_/ / ____/ /___ | 7 | // | /_____/_____/_/ /_____/ | 8 | // | Programming Language | 9 | // | http://www.ldpl-lang.org/ | 10 | // | | 11 | // +=============================================+ 12 | 13 | // +------------------+ 14 | // | --- Includes --- | 15 | // +------------------+ 16 | #include "ldpl.h" 17 | 18 | #include 19 | 20 | #include "aux/aux_c_format.cpp" // Contains auxiliary functions that turn LDPL identifiers / expressions into C / C++ identifiers / expressions 21 | #include "aux/aux_code.cpp" // Contains auxiliary functions that add C++ code to the generated C++ file 22 | #include "aux/aux_compile_line.cpp" // Contains the behemoth compile_line function that pattern-matches lines and compiles them 23 | #include "aux/aux_container.cpp" // Contains auxiliary functions to split and access [multi]containers 24 | #include "aux/aux_format.cpp" // Contains auxiliary functions that do text or number formatting 25 | #include "aux/aux_info.cpp" // Contains auxiliary functions that display information about the compiler and the system 26 | #include "aux/aux_line_like.cpp" // Contains the line_like function used for pattern matching lines 27 | #include "aux/aux_state.cpp" // Contains auxiliary functions that check the current compilation state 28 | #include "aux/aux_tokenizer.cpp" // Contains functions used to tokenize an LDPL source file 29 | #include "aux/aux_typecheck.cpp" // Contains auxiliary functions to check data type compliance 30 | 31 | // +----------------------------+ 32 | // | --- Main Aux Functions --- | 33 | // +----------------------------+ 34 | 35 | string exec(const char *cmd) 36 | { 37 | // -- Executes a command passed as a string -- 38 | array buffer; 39 | string result; 40 | unique_ptr pipe(popen(cmd, "r"), pclose); 41 | if (!pipe) 42 | { 43 | throw runtime_error("popen() failed!"); 44 | } 45 | while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) 46 | { 47 | result += buffer.data(); 48 | } 49 | return result; 50 | } 51 | 52 | void load_and_compile(string &filename, compiler_state &state) 53 | { 54 | // -- Loads a file and sends it to compilation -- 55 | filename = expandHomeDirectory(filename); 56 | state.where.current_file = filename; 57 | // Accept input from stdin 58 | ifstream file(filename); 59 | // Fail if the file couldn't be loaded 60 | if (!file.is_open()) 61 | error("The file '" + filename + "' couldn't be loaded."); 62 | // Get file contents 63 | vector lines; 64 | string line = ""; 65 | while (getline(file, line)) 66 | { 67 | replace_whitespace(line); 68 | lines.push_back(line); 69 | } 70 | bullet_msg("Compiling " + filename); 71 | compile(lines, state); 72 | } 73 | 74 | void accept_and_compile(compiler_state &state) 75 | { 76 | // -- Accepts LDPL source code via standard input and sends it to compilation 77 | // -- Get file contents 78 | vector lines; 79 | state.where.current_file = "standard input"; 80 | string line = ""; 81 | while (getline(cin, line)) 82 | { 83 | replace_whitespace(line); 84 | lines.push_back(line); 85 | } 86 | bullet_msg("Compiling"); 87 | compile(lines, state); 88 | } 89 | 90 | void compile(vector &lines, compiler_state &state) 91 | { 92 | // -- Takes a vector of LDPL source lines and compiles them to C++ -- 93 | // For each line in the source code 94 | for (size_t line_num = 1; line_num <= lines.size(); ++line_num) 95 | { 96 | string &line = lines[line_num - 1]; 97 | state.where.current_line = line_num; 98 | 99 | if (state.open_quote) 100 | { 101 | // Check for END QUOTE first 102 | if (line.size() >= 9 /*&& (line[0] == 'E' || line[0] == 'e')*/) 103 | { 104 | string upper = ""; 105 | for (char c : line) 106 | upper += toupper(c); 107 | trim(upper); 108 | if (upper == "END QUOTE") 109 | { 110 | state.open_quote = false; 111 | // Kill final newline. Programs can add crlf if needed. 112 | string &prev = state.current_subprocedure != "" 113 | ? state.subroutine_code.back() 114 | : state.output_code.back(); 115 | size_t pos = prev.rfind("\\n"); 116 | if (pos != string::npos) 117 | prev.erase(pos, 2); 118 | prev += ";"; 119 | continue; 120 | } 121 | } 122 | 123 | // No END QUOTE, emit the line as C++ 124 | state.add_code("\"\" \"" + escape_c_quotes(escape_c_backslashes(line)) + "\\n\"", state.where); 125 | continue; 126 | } 127 | 128 | trim(line); 129 | // Split line in various tokens 130 | vector tokens; 131 | tokenize(line, tokens, state.where, true, ' '); 132 | for (string &token : tokens) 133 | if (token == "CRLF" || token == "LF") 134 | token = "\"\\n\""; 135 | if (tokens.size() == 0) 136 | continue; 137 | compile_line(tokens, state); 138 | } 139 | if (state.open_quote) 140 | error("a QUOTE block was not terminated."); 141 | if (state.closing_subprocedure()) 142 | error("a SUB-PROCEDURE block was not terminated."); 143 | if (state.closing_if()) 144 | error("a IF block was not terminated."); 145 | if (state.closing_loop()) 146 | error("a WHILE or FOR block was not terminated."); 147 | } 148 | 149 | // +-----------------------+ 150 | // | --- Main Function --- | 151 | // +-----------------------+ 152 | 153 | int main(int argc, const char *argv[]) 154 | { 155 | // Get command line arguments as string vector 156 | vector args(argv + 1, argv + argc); 157 | 158 | // Check if the user requested version or help information 159 | if (contains_any(args, {"-v", "--version"})) 160 | { 161 | displayVersionInfo(); 162 | return 0; 163 | } 164 | else if (contains_any(args, {"-h", "--help"})) 165 | { 166 | displayHelpInfo(); 167 | return 0; 168 | } 169 | 170 | // Define local variables that will be used for compilation 171 | compiler_state 172 | state; // Compiler state (holds variables, sections, functions, etc) 173 | vector files_to_compile; 174 | #ifdef STATIC_BUILDS 175 | bool no_static = false; 176 | #endif 177 | string output_filename = ""; 178 | string final_filename = ""; 179 | 180 | // Check command line arguments 181 | if (args.size() >= 1) 182 | { 183 | for (string &arg : args) 184 | { 185 | if (arg.size() >= 1 && arg[0] != '-') 186 | { 187 | if (output_filename == "") 188 | output_filename = arg; 189 | if (files_to_compile.size() > 0) 190 | { 191 | warning( 192 | "passing multiple LDPL source files to the\ncompiler is " 193 | "deprecated and may be removed in the future.\nPlease use the " 194 | "IMPORT statement instead."); 195 | } 196 | files_to_compile.push_back(arg); 197 | } 198 | else if (arg == "-r") 199 | { 200 | show_ir = true; 201 | } 202 | #ifdef STATIC_BUILDS 203 | else if (arg == "-n" || arg == "--non-static") 204 | { 205 | no_static = true; 206 | } 207 | #endif 208 | else if (arg.substr(0, 3) == "-o=") 209 | { 210 | final_filename = arg.substr(3); 211 | } 212 | else if (arg.substr(0, 3) == "-i=") 213 | { 214 | if (((arg.length() > 5) && 215 | (0 == arg.compare(arg.length() - 5, 5, ".ldpl"))) || 216 | ((arg.length() > 4) && 217 | (0 == arg.compare(arg.length() - 4, 4, ".lsc")))) 218 | { 219 | if (files_to_compile.size() > 0) 220 | { 221 | error("To compile more than one file, use the IMPORT statement."); 222 | } 223 | files_to_compile.push_back(arg.substr(3)); 224 | } 225 | else 226 | { 227 | // pass everything but .ldpl and .lsc files to the c++ compiler 228 | extensions.push_back(arg.substr(3)); // kill -i= prefix 229 | } 230 | } 231 | else if (arg.substr(0, 3) == "-f=") 232 | { 233 | // pass flags to the c++ compiler (for example -f=-lSDL) 234 | extension_flags.push_back(arg.substr(3)); 235 | } 236 | else if (arg == "-c") 237 | { 238 | files_to_compile.push_back(arg); 239 | } 240 | else 241 | { 242 | cout << "Unknown option: " << arg << endl; 243 | cout << "Try 'ldpl -h' for more information." << endl; 244 | return 0; 245 | } 246 | } 247 | } 248 | 249 | // Add default initialization code to the generated source 250 | state.add_code("int main(int argc, char *argv[]){"); 251 | state.add_code("cout.precision(numeric_limits::digits10);"); 252 | 253 | // Add default variable declaration code to the generated code 254 | state.variables[""]["ARGV"] = {2, 3}; // List of text 255 | state.add_var_code("ldpl_list " + fix_identifier("ARGV", true) + ";"); 256 | state.variables[""]["ERRORCODE"] = {1}; // Declared in ldpl_lib.cpp 257 | state.variables[""]["ERRORTEXT"] = {2}; // Declared in ldpl_lib.cpp 258 | state.add_code("for(int i = 1; i < argc; ++i)"); 259 | state.add_code(fix_identifier("ARGV", true) + 260 | ".inner_collection.push_back(argv[i]);"); 261 | 262 | // Fail if no filename was passed 263 | if (files_to_compile.size() == 0) 264 | error("no source file specified."); 265 | 266 | // For each filename passed, compile each file into one big code 267 | for (string &filename : files_to_compile) 268 | { 269 | // Reset state section for this file 270 | state.section_state = 0; 271 | // If the filename was a filename or the user requested standard input 272 | if (filename != "-c") 273 | { 274 | bullet_msg("Loading " + filename); 275 | load_and_compile(filename, state); 276 | } 277 | else 278 | { 279 | bullet_msg("Waiting for standard input..."); 280 | accept_and_compile(state); 281 | } 282 | // Fail if no procedure section was found 283 | if (state.section_state < 2) 284 | error("PROCEDURE section not found" + 285 | (filename == "-c" ? "." : " in file '" + filename + "'.")); 286 | } 287 | 288 | // Add return code to the generated main function 289 | state.add_code("return 0; \n}"); 290 | 291 | // Add include for extensions 292 | if (!extensions.empty()) 293 | { 294 | for (string &extension : extensions) 295 | { 296 | bullet_msg("Including C++ extension " + extension); 297 | state.add_code("#include \"" + extension + "\""); 298 | } 299 | } 300 | 301 | // If an expected subprocedure was not declared, raise an error 302 | if (state.expected_subprocedures.size() > 0) 303 | error("the subprocedure " + state.expected_subprocedures[0].first + 304 | " is called but never declared."); 305 | 306 | // If only to print the generated code (IR) was required 307 | if (show_ir) 308 | { 309 | cout << "#include \"" << LDPLLIBLOCATION << "/ldpl_lib.cpp\"" 310 | << endl; // Add LDPL library 311 | for (string line : state.variable_code) 312 | cout << line << endl; 313 | for (string line : state.subroutine_code) 314 | cout << line << endl; 315 | for (string line : state.output_code) 316 | cout << line << endl; 317 | exit(0); 318 | } 319 | 320 | // Otherwise, save the generated code 321 | ofstream myfile; 322 | myfile.open("ldpl-temp.cpp"); 323 | myfile << "#include \"" << LDPLLIBLOCATION << "/ldpl_lib.cpp\"" 324 | << endl; // Add LDPL library 325 | for (string line : state.variable_code) 326 | myfile << line << endl; 327 | for (string line : state.subroutine_code) 328 | myfile << line << endl; 329 | for (string line : state.output_code) 330 | myfile << line << endl; 331 | myfile.close(); 332 | 333 | // Generate output filename if not set by -o= 334 | if (final_filename.empty()) 335 | { 336 | for (unsigned int i = 0; i < output_filename.size(); ++i) 337 | { 338 | if (output_filename[i] != '.') 339 | final_filename += output_filename[i]; 340 | else 341 | break; 342 | } 343 | if (final_filename.size() == 0) 344 | final_filename = "ldpl-output"; 345 | final_filename += "-bin"; 346 | } 347 | 348 | // Generate the C++ compilation command 349 | string compile_line = 350 | "c++ ldpl-temp.cpp -std=c++11 -w -O3 -o " + final_filename; 351 | #ifdef STATIC_BUILDS 352 | if (!no_static) 353 | compile_line += " -static-libgcc -static-libstdc++ "; 354 | #endif 355 | if (!extension_flags.empty()) 356 | { 357 | for (string &flag : extension_flags) 358 | { 359 | bullet_msg("using C++ switch " + flag); 360 | compile_line += " " + flag; 361 | } 362 | } 363 | 364 | // Compile the C++ code 365 | bullet_msg("Building " + final_filename); 366 | int compiled = system(compile_line.c_str()); 367 | #if defined(_WIN32) 368 | system("del ldpl-temp.cpp"); 369 | #else 370 | system("rm ldpl-temp.cpp"); 371 | #endif 372 | 373 | // Output if the code was compiled or not 374 | if (compiled == 0) 375 | { 376 | bullet_msg("Saved as " + final_filename); 377 | bullet_msg("\033[32;1mFile(s) compiled successfully.\033[0m"); 378 | } 379 | else 380 | { 381 | error("compilation failed."); 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /src/ldpl.h: -------------------------------------------------------------------------------- 1 | #ifndef LDPL_H 2 | #define LDPL_H 3 | /* --- STD Includes --- */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* --- Namespace Usage --- */ 17 | using namespace std; 18 | 19 | /* --- Custom Libraries --- */ 20 | #include "libraries/cpptrim.h" 21 | #include "libraries/vector_contains.h" 22 | 23 | /* --- OS Dependant Libraries --- */ 24 | #ifdef _WIN32 25 | #include 26 | #else 27 | #include 28 | #endif 29 | 30 | /* --- Static Builds? --- */ 31 | #if !defined(__APPLE__) && !defined(__ANDROID__) 32 | #define STATIC_BUILDS 1 33 | #endif 34 | 35 | /* --- Global Variables --- */ 36 | bool show_ir = false; // Show internal representation? 37 | string &escape_c_quotes(string &str); 38 | vector extensions; // C++ extensions to add to the file 39 | vector extension_flags; // Flags to pass to the C++ Compiler 40 | 41 | /* --- Custom Data Types --- */ 42 | #include "data_types/code_location.h" 43 | #include "data_types/compiler_state.h" 44 | 45 | /* --- Function Declarations --- */ 46 | void displayVersionInfo(); 47 | void displayHelpInfo(); 48 | string exec(const char *cmd); 49 | string expandHomeDirectory(string &filename); 50 | void bullet_msg(const string &msg); 51 | void warning(const string &msg); 52 | void error(const string &msg); 53 | void compile(vector &lines, compiler_state &state); 54 | void tokenize(string &line, vector &tokens, code_location &where, bool uppercase, char splitChar); 55 | void split_vector(string &token, string &var_name, vector &indexes, compiler_state &state); 56 | void compile_line(vector &tokens, compiler_state &state); 57 | bool line_like(string model_line, vector &tokens, compiler_state &state); // Important to pass tokens by copy 58 | bool is_number(string &number); 59 | bool is_natural(string number); 60 | bool is_label(string &token); 61 | bool is_math_symbol(string &symbol); 62 | bool is_string(string &token); 63 | bool is_num_var(string &token, compiler_state &state); 64 | bool is_txt_var(string &token, compiler_state &state); 65 | bool is_scalar_variable(string &token, compiler_state &state); 66 | bool is_num_expr(string &token, compiler_state &state); 67 | bool is_txt_expr(string &token, compiler_state &state); 68 | bool is_expression(string &token, compiler_state &state); 69 | bool is_num_map(string &token, compiler_state &state); 70 | bool is_txt_map(string &token, compiler_state &state); 71 | bool is_num_list(string &token, compiler_state &state); 72 | bool is_txt_list(string &token, compiler_state &state); 73 | bool is_list_list(string &token, compiler_state &state); 74 | bool is_map_map(string &token, compiler_state &state); 75 | bool is_map_list(string &token, compiler_state &state); 76 | bool is_scalar_map(string &token, compiler_state &state); 77 | bool is_scalar_list(string &token, compiler_state &state); 78 | bool is_external(string &token, compiler_state &state); 79 | bool variable_exists(string &token, compiler_state &state); 80 | bool is_subprocedure(string &token, compiler_state &state); 81 | string get_c_variable(compiler_state &state, string &variable); 82 | string get_c_expression(compiler_state &state, string &expression); 83 | string get_c_char_array(compiler_state &state, string &text); 84 | string get_c_string(compiler_state &state, string &expression); 85 | string get_c_number(compiler_state &state, string &expression); 86 | string get_c_condition(compiler_state &state, vector tokens); 87 | string get_c_condition(compiler_state &state, vector tokens, unsigned int &ct); 88 | string &escape_c_quotes(string &str); 89 | string &escape_c_backslashes(string &str); 90 | void load_and_compile(string &filename, compiler_state &state); 91 | void accept_and_compile(compiler_state &state); 92 | void replace_whitespace(string &code); 93 | string fix_external_identifier(string identifier, bool isVariable); 94 | string fix_identifier(string id, bool isv, compiler_state &s); 95 | string fix_identifier(string identifier, bool isVariable); 96 | string fix_identifier(string identifier); 97 | bool in_procedure_section(compiler_state &state); 98 | vector variable_type(string &token, compiler_state &state); 99 | void open_subprocedure_code(compiler_state &state); 100 | void add_call_code(string &subprocedure, vector ¶meters, compiler_state &state); 101 | string current_os(); 102 | bool is_map(string &token, compiler_state &state); 103 | void badcode(const string &msg, const code_location where); 104 | #endif -------------------------------------------------------------------------------- /src/libraries/cpptrim.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPTRIM 2 | #define CPPTRIM 3 | #include 4 | #include 5 | 6 | // Removes all trailing and ending whitespace from a string 7 | void trim(std::string& line) { 8 | // If the std::string is empty 9 | if (line.size() == 0) return; 10 | 11 | // If the string has only one character 12 | if (line.size() == 1 && !std::isspace(line[0])) return; 13 | 14 | // Left trim 15 | int first = 0; 16 | for (unsigned int i = 0; i < line.size(); ++i) { 17 | if (!std::isspace(line[i])) { 18 | first = i; 19 | break; 20 | } 21 | } 22 | 23 | // Right trim 24 | int last = 0; 25 | for (unsigned int i = line.size() - 1; i >= 0; --i) { 26 | if (!std::isspace(line[i])) { 27 | last = i + 1; 28 | break; 29 | } 30 | //--i will break with unsigned int when reaching 0, so we check 31 | // if i == 0 and, if it is and it's not a space, we break 32 | if (i == 0) { 33 | last = 0; 34 | break; 35 | } 36 | } 37 | 38 | // Trim the std::string 39 | line = line.substr(first, last - first); 40 | } 41 | #endif 42 | -------------------------------------------------------------------------------- /src/libraries/vector_contains.h: -------------------------------------------------------------------------------- 1 | template 2 | static bool contains(vector const& vec, T const& val) { 3 | return vec.end() != find(vec.begin(), vec.end(), val); 4 | } 5 | 6 | template 7 | static bool contains_any(vector const& vec, vector vals) { 8 | for (auto const& v : vals) { 9 | if (contains(vec, v)) return true; 10 | } 11 | return false; 12 | } --------------------------------------------------------------------------------