Initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
build
|
||||
kyn
|
||||
373
LICENSE.txt
Normal file
373
LICENSE.txt
Normal file
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
43
Makefile
Normal file
43
Makefile
Normal file
@@ -0,0 +1,43 @@
|
||||
# Compiler
|
||||
CXX = g++
|
||||
|
||||
# Compiler flags
|
||||
# -Isrc: Add src directory to include path
|
||||
# -Wall: Enable all warnings
|
||||
# -Wextra: Enable extra warnings
|
||||
# -std=c++17: Use C++17 standard
|
||||
CXXFLAGS = -O3 -Isrc -Wall -Wextra -std=c++14
|
||||
|
||||
# Build directory
|
||||
BUILD_DIR = build
|
||||
|
||||
# Find all .cpp files in src and its subdirectories
|
||||
SRCS = $(shell find src -name '*.cpp')
|
||||
|
||||
# VPATH tells make where to look for prerequisites
|
||||
VPATH = $(sort $(dir $(SRCS)))
|
||||
|
||||
# Object files in the build directory
|
||||
OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(SRCS:.cpp=.o)))
|
||||
|
||||
# Name of the executable
|
||||
TARGET = kyn
|
||||
|
||||
# The 'all' target is the default when you just run 'make'
|
||||
all: $(TARGET)
|
||||
|
||||
# Link the object files to create the executable
|
||||
$(TARGET): $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS)
|
||||
|
||||
# Compile .cpp files to .o files in the build directory
|
||||
$(BUILD_DIR)/%.o: %.cpp
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
# The 'clean' target removes generated files
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(TARGET)
|
||||
|
||||
# Phony targets are not files
|
||||
.PHONY: all clean
|
||||
83
README.md
Normal file
83
README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Kyn programming language
|
||||
|
||||
Kyn is a simple, easily extensible scripting language written in C++. The syntax is inspired by C and shell languages. It is quite fast. Kyn uses the `.kyn` file extension.
|
||||
|
||||
## Features
|
||||
|
||||
* Speedy: 20-40% faster than Python
|
||||
* Simple Syntax: A small amount of keywords makes Kyn easier to master
|
||||
* Small Codebase: 931 lines of code at the time of writing
|
||||
* Easily Extensible: The modules system allows anyone to write and contribute their own modules
|
||||
* Flexible Licensing: Kyn is licensed to you under the Mozilla Public Licence v2.0, meaning you can develop your own modules and keep them to yourself, while keeping the core interpreter open.
|
||||
|
||||
## Compiling
|
||||
|
||||
Install `make` and your favourite C++ compiler. Then, run `make`. Change the `compiler` variable if required. Ensure that your compiler supports C++14.
|
||||
|
||||
## Syntax
|
||||
|
||||
Kyn is made up of "modules", which you can call on to collect, process, and output data. Use the `println` module to print something to the console:
|
||||
|
||||
```
|
||||
println "Hello from Kyn!"
|
||||
```
|
||||
|
||||
You can use the output from one module inside another by enclosing it with `(`parenthases`)`, similar to how some shells work.
|
||||
|
||||
```
|
||||
println (math 10 * 3 + 2)
|
||||
```
|
||||
|
||||
This will print the result of 10 * 3 + 2 (which happens to be 50.)
|
||||
|
||||
You can initialise variables with the `let` module, and access variables by prefixing a `$` dollar sign.
|
||||
|
||||
```
|
||||
let myVar = "hello!"
|
||||
println $hello
|
||||
```
|
||||
|
||||
Reassigning variables is done like most programming languages.
|
||||
|
||||
```
|
||||
let myVar = "hello!"
|
||||
myVar = "goodbye!"
|
||||
```
|
||||
|
||||
Control flow can be done through `if` and `while` (which are technically also modules, but they work in a special way.) The `compare` module can be used for comparisons between things. When using `if` and `while`, there's no need to surround your condition in brackets.
|
||||
|
||||
```
|
||||
let varA = 120
|
||||
let varB = 130
|
||||
|
||||
if compare $varA > $varB {
|
||||
println "This code will run!"
|
||||
} else {
|
||||
println "This code won't run."
|
||||
}
|
||||
|
||||
let counter = 0
|
||||
|
||||
while compare $counter <= 10 {
|
||||
counter = ($counter + 1)
|
||||
println $counter
|
||||
}
|
||||
```
|
||||
|
||||
Full details of the syntax is in `docs/syntax.md`.
|
||||
|
||||
## Developing Modules
|
||||
|
||||
Modules are held in the `src/modules` folder. There are three steps to implementing a module:
|
||||
|
||||
1. Write the module code: Add all the logic and processing in a new folder, with a header file and C++ source file.
|
||||
|
||||
2. Update `src/defs/defs.cpp`, `src/executor/executor.cpp` and `src/defs/defs.h`: In these files add references to your new module, to integrate it into the Kyn interpreter.
|
||||
|
||||
3. Run `make` to build again. `make` should automatically detect your new module and compile it, as well as recompiling `defs.cpp` and `executor.cpp`, before building the executable.
|
||||
|
||||
A full tutorial on writing modules will be released later.
|
||||
|
||||
## License
|
||||
|
||||
Kyn is licensed under the Mozilla Public Licence v2.0.
|
||||
86
docs/syntax.md
Normal file
86
docs/syntax.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Kyn Syntax
|
||||
|
||||
This document details syntax of provided Kyn modules.
|
||||
|
||||
Call a module by writing it's name, then the arguments for the module.
|
||||
|
||||
Substitute the output of a module into another module's arguments by enclosing the call in `(`parenthases`)`.
|
||||
|
||||
Access a variable by prefixing it's name with a `$` dollar sign.
|
||||
|
||||
## Input/Output
|
||||
|
||||
### print
|
||||
|
||||
Prints all the arguments provided to the console.
|
||||
|
||||
Example: `print "Hello!"`
|
||||
|
||||
### println
|
||||
|
||||
Prints all the arguments provided to the console, appending a newline after.
|
||||
|
||||
Example: `println "Hello!"`
|
||||
|
||||
### input
|
||||
|
||||
Gets a line of input from the console.
|
||||
|
||||
Example: `input` (this does nothing but prompt the user)
|
||||
|
||||
Another example: `let userInput = (input)`
|
||||
|
||||
## Data
|
||||
|
||||
### let
|
||||
|
||||
Defines a variable to the value provided.
|
||||
|
||||
Example: `let myVar = "Hello!"`
|
||||
|
||||
## Computations
|
||||
|
||||
### math
|
||||
|
||||
Computes a mathematical expression. Supports addition (+), subtraction (-), multiplication (*), division (/), power to (^) and mod (%).
|
||||
|
||||
Example: `math 4 * 3 + 12 / 4 - 5 ^ 3`
|
||||
|
||||
### compare
|
||||
|
||||
Compares two values. Supports equal (==) and inequal (!=) on all values. Supports greater than (>), greater than or equal to (>=), lesser than (<), and lesser than or equal to (<=) for numbers.
|
||||
|
||||
## Control Flow
|
||||
|
||||
### exit
|
||||
|
||||
Exits the program with a return code. If no return code or an invalid return code is provided, exits with code 0. Otherwise, exits with given code.
|
||||
|
||||
### if
|
||||
|
||||
Runs a module. If the output of that module is 0 or "false", skips the code in that block. If an `else` block is provided after, that is running instead. Otherwise, the code inside the `if` block is ran.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
if compare 1 == 1 {
|
||||
println "1 is the same as 1"
|
||||
} else {
|
||||
println "1 is not the same as 1"
|
||||
}
|
||||
```
|
||||
|
||||
### while
|
||||
|
||||
Runs a module. If the output of that module is 0 or "false", skips the code in that block. Otherwise, loops the block until the module ran is 0 or "false".
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
let number = 0
|
||||
|
||||
while compare $number <= 10 {
|
||||
number = ($number + 1)
|
||||
println $number
|
||||
}
|
||||
```
|
||||
27
src/data/data.cpp
Normal file
27
src/data/data.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "data.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "../defs/defs.h"
|
||||
#include "../error/error.h"
|
||||
|
||||
namespace data {
|
||||
std::map<std::string, Value> vars;
|
||||
std::map<std::string, std::vector<Instruction>> functions;
|
||||
void modifyValue(std::string key, Value value) {
|
||||
if (vars.find(key) != vars.end()) {
|
||||
vars[key] = value;
|
||||
} else {
|
||||
error("Could not find variable " + key + "to modify (try defining first with let?)");
|
||||
}
|
||||
}
|
||||
Value getValue(std::string key) {
|
||||
if (vars.find(key) != vars.end()) {
|
||||
return vars[key];
|
||||
} else {
|
||||
error("Could not find variable " + key + " to access (try defining first with let?)");
|
||||
return Value();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/data/data.h
Normal file
13
src/data/data.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../defs/defs.h"
|
||||
|
||||
namespace data {
|
||||
extern std::map<std::string, Value> vars;
|
||||
extern std::map<std::string, std::vector<Instruction>> functions;
|
||||
void modifyValue(std::string key, Value value);
|
||||
Value getValue(std::string key);
|
||||
}
|
||||
205
src/defs/defs.cpp
Normal file
205
src/defs/defs.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "defs.h"
|
||||
#include "../error/error.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
InstructionType strToInstructionType(std::string in) {
|
||||
if (in == "println") return InstructionType::Println;
|
||||
else if (in == "print") return InstructionType::Print;
|
||||
else if (in == "math") return InstructionType::Math;
|
||||
else if (in == "let") return InstructionType::Let;
|
||||
else if (in == "exit") return InstructionType::Exit;
|
||||
else if (in == "if") return InstructionType::If;
|
||||
else if (in == "while") return InstructionType::While;
|
||||
else if (in == "compare") return InstructionType::Compare;
|
||||
else if (in == "input") return InstructionType::Input;
|
||||
else return InstructionType::Variable;
|
||||
}
|
||||
|
||||
Instruction::Instruction(std::vector<std::string> toks) {
|
||||
if (!toks.empty()) {
|
||||
// Get type of instruction from first token
|
||||
instruction = strToInstructionType(toks[0]);
|
||||
if (instruction == InstructionType::Variable) {
|
||||
for (std::string tok : toks) {
|
||||
args.push_back(Value(tok));
|
||||
}
|
||||
} else {
|
||||
// The rest are the args, convert them to values first
|
||||
for (size_t i = 1; i < toks.size(); i++) {
|
||||
args.push_back(Value(toks[i]));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error("Empty tokens!");
|
||||
}
|
||||
}
|
||||
|
||||
Instruction::Instruction(std::vector<Value> toks) {
|
||||
if (!toks.empty()) {
|
||||
// Check type of value in first token, then compute token
|
||||
if (toks[0].valtype == ValueType::Real) {
|
||||
instruction = strToInstructionType(toks[0].real);
|
||||
} else {
|
||||
error("Instruction should be a real");
|
||||
}
|
||||
if (instruction == InstructionType::Variable) {
|
||||
for (const auto& tok : toks) {
|
||||
args.push_back(tok);
|
||||
}
|
||||
} else {
|
||||
// The rest are the args
|
||||
for (size_t i = 1; i < toks.size(); i++) {
|
||||
args.push_back(toks[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error("Empty tokens!");
|
||||
}
|
||||
}
|
||||
|
||||
Value::Value(std::string stringval) : valtype(ValueType::Real), real(stringval) {}
|
||||
|
||||
Value::Value(Instruction instval) : valtype(ValueType::Processed), processed(std::make_unique<Instruction>(std::move(instval))) {}
|
||||
|
||||
Value::Value() : valtype(ValueType::Real) {}
|
||||
|
||||
Value::Value(InstructionGroup instgroup) : valtype(ValueType::InstructionGroup), instructionGroup(instgroup) {};
|
||||
|
||||
Value::Value(const Value& other) : valtype(other.valtype), real(other.real), varName(other.varName), instructionGroup(other.instructionGroup) {
|
||||
if (other.processed) {
|
||||
processed = std::make_unique<Instruction>(*other.processed);
|
||||
}
|
||||
}
|
||||
|
||||
Value::Value(Varname varname) : valtype(ValueType::Variable), varName(varname) {}
|
||||
|
||||
Varname::Varname(std::string in) : key(in.substr(1)) {}
|
||||
Varname::Varname() : key("") {}
|
||||
|
||||
Value& Value::operator=(const Value& other) {
|
||||
if (this != &other) {
|
||||
valtype = other.valtype;
|
||||
real = other.real;
|
||||
varName = other.varName;
|
||||
instructionGroup = other.instructionGroup;
|
||||
if (other.processed) {
|
||||
processed = std::make_unique<Instruction>(*other.processed);
|
||||
} else {
|
||||
processed.reset();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Value::toString() const {
|
||||
std::string out = "Value(type: ";
|
||||
switch (valtype) {
|
||||
case ValueType::Real:
|
||||
out += "Real, value: " + real + ")";
|
||||
break;
|
||||
case ValueType::Processed:
|
||||
out += "Processed, value: ";
|
||||
if (processed) {
|
||||
out += processed->toString();
|
||||
} else {
|
||||
out += "null";
|
||||
}
|
||||
out += ")";
|
||||
break;
|
||||
case ValueType::InstructionGroup:
|
||||
out += "InstructionGroup, contains " + std::to_string(instructionGroup.size()) + " instructions)";
|
||||
break;
|
||||
default:
|
||||
out += "FIXME)";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string Instruction::toString() const {
|
||||
std::string out = "Instruction(type: ";
|
||||
switch (instruction) {
|
||||
case InstructionType::None:
|
||||
out += "None";
|
||||
break;
|
||||
case InstructionType::Println:
|
||||
out += "Println";
|
||||
break;
|
||||
case InstructionType::Math:
|
||||
out += "Math";
|
||||
break;
|
||||
case InstructionType::Let:
|
||||
out += "Let";
|
||||
break;
|
||||
case InstructionType::If:
|
||||
out += "If";
|
||||
break;
|
||||
case InstructionType::Compare:
|
||||
out += "Compare";
|
||||
break;
|
||||
case InstructionType::Input:
|
||||
out += "Input";
|
||||
break;
|
||||
default:
|
||||
out += "FIXME";
|
||||
break;
|
||||
}
|
||||
out += ", args: [";
|
||||
for (const auto& val : args) {
|
||||
out += val.toString() + ", ";
|
||||
}
|
||||
out += "])";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Value> split(std::string line) {
|
||||
std::vector<Value> splitvals;
|
||||
std::string buf;
|
||||
bool instring = false;
|
||||
int brackets = 0;
|
||||
for (char chr : line) {
|
||||
if (chr == ' ' && !instring && brackets == 0 && !buf.empty()) {
|
||||
if (buf[0] == '$') {
|
||||
splitvals.push_back(Value(Varname(buf)));
|
||||
} else {
|
||||
splitvals.push_back(Value(buf));
|
||||
}
|
||||
buf = "";
|
||||
} else if (chr == '(' || chr == '[') {
|
||||
brackets += 1;
|
||||
if (brackets == 1) {
|
||||
if (!buf.empty()) {
|
||||
splitvals.push_back(Value(buf));
|
||||
buf = "";
|
||||
}
|
||||
} else {
|
||||
buf += chr;
|
||||
}
|
||||
} else if (chr == ')' || chr == ']') {
|
||||
brackets -= 1;
|
||||
if (brackets == 0) {
|
||||
if (!buf.empty()) {
|
||||
splitvals.push_back(Value(Instruction(split(buf))));
|
||||
buf = "";
|
||||
}
|
||||
}
|
||||
} else if (chr == '"') {
|
||||
instring = !instring;
|
||||
} else {
|
||||
buf += chr;
|
||||
}
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
if (buf[0] == '$') {
|
||||
splitvals.push_back(Value(Varname(buf)));
|
||||
} else {
|
||||
splitvals.push_back(Value(buf));
|
||||
}
|
||||
}
|
||||
return splitvals;
|
||||
}
|
||||
|
||||
bool inReplMode = false;
|
||||
55
src/defs/defs.h
Normal file
55
src/defs/defs.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
|
||||
enum class InstructionType {
|
||||
None, Print, Println, Math, Let, Variable, Exit, If, While, Input, Compare
|
||||
};
|
||||
|
||||
enum class ValueType {
|
||||
Real, Processed, Variable, InstructionGroup
|
||||
};
|
||||
|
||||
struct Instruction;
|
||||
|
||||
typedef std::vector<Instruction> InstructionGroup;
|
||||
|
||||
struct Varname {
|
||||
std::string key;
|
||||
Varname(std::string in);
|
||||
Varname();
|
||||
};
|
||||
|
||||
struct Value {
|
||||
ValueType valtype = ValueType::Real;
|
||||
std::unique_ptr<Instruction> processed;
|
||||
std::string real = "";
|
||||
InstructionGroup instructionGroup;
|
||||
std::string toString() const;
|
||||
Varname varName = Varname();
|
||||
Value(std::string stringval);
|
||||
Value(Instruction instval);
|
||||
Value(InstructionGroup instgroup);
|
||||
Value(Varname var);
|
||||
Value();
|
||||
Value(const Value& other);
|
||||
Value& operator=(const Value& other);
|
||||
Value(Value&& other) = default;
|
||||
Value& operator=(Value&& other) = default;
|
||||
};
|
||||
|
||||
struct Instruction {
|
||||
InstructionType instruction = InstructionType::None;
|
||||
std::vector<Value> args;
|
||||
std::string toString() const;
|
||||
Instruction() = default;
|
||||
Instruction(std::vector<std::string> toks);
|
||||
Instruction(std::vector<Value> toks);
|
||||
};
|
||||
|
||||
std::vector<Value> split(std::string in);
|
||||
InstructionType strToInstructionType(std::string in);
|
||||
extern bool inReplMode;
|
||||
11
src/error/error.cpp
Normal file
11
src/error/error.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "error.h"
|
||||
#include "../defs/defs.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
void error(std::string err) {
|
||||
std::cout << "Error: " << err << std::endl;
|
||||
if (!inReplMode) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
6
src/error/error.h
Normal file
6
src/error/error.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
void error(std::string err);
|
||||
80
src/executor/executor.cpp
Normal file
80
src/executor/executor.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "executor.h"
|
||||
#include "../defs/defs.h"
|
||||
#include "../data/data.h"
|
||||
#include "../error/error.h"
|
||||
#include "../vars/vars.h"
|
||||
#include "../modules/println/println.h"
|
||||
#include "../modules/print/print.h"
|
||||
#include "../modules/math/math.h"
|
||||
#include "../modules/let/let.h"
|
||||
#include "../modules/exit/exit.h"
|
||||
#include "../modules/if/if.h"
|
||||
#include "../modules/while/while.h"
|
||||
#include "../modules/compare/compare.h"
|
||||
#include "../modules/input/input.h"
|
||||
|
||||
Value execute(Instruction inst) {
|
||||
// Special cases that need to manage their own argument evaluation
|
||||
if (inst.instruction == InstructionType::Let) {
|
||||
return modules::let(inst.args);
|
||||
}
|
||||
if (inst.instruction == InstructionType::If) {
|
||||
return modules::ifs(inst.args);
|
||||
}
|
||||
if (inst.instruction == InstructionType::While) {
|
||||
return modules::whiles(inst.args);
|
||||
}
|
||||
|
||||
// For all other instructions, evaluate the arguments first
|
||||
size_t i = 0;
|
||||
while (i < inst.args.size()) {
|
||||
bool evaluated = false;
|
||||
if (inst.args[i].valtype == ValueType::Processed) {
|
||||
if (inst.args[i].processed) {
|
||||
inst.args[i] = execute(*inst.args[i].processed);
|
||||
evaluated = true;
|
||||
}
|
||||
} else if (inst.args[i].valtype == ValueType::Variable) {
|
||||
inst.args[i] = data::getValue(inst.args[i].varName.key);
|
||||
evaluated = true;
|
||||
}
|
||||
|
||||
if (!evaluated) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Then, execute the instruction with the evaluated arguments
|
||||
switch (inst.instruction) {
|
||||
case InstructionType::Print:
|
||||
return modules::print(inst.args);
|
||||
break;
|
||||
case InstructionType::Println:
|
||||
return modules::println(inst.args);
|
||||
break;
|
||||
case InstructionType::Math:
|
||||
return modules::math(inst.args);
|
||||
break;
|
||||
case InstructionType::Compare:
|
||||
return modules::compare(inst.args);
|
||||
break;
|
||||
case InstructionType::Input:
|
||||
return modules::input(inst.args);
|
||||
break;
|
||||
case InstructionType::Exit:
|
||||
return modules::exit(inst.args);
|
||||
break;
|
||||
case InstructionType::Variable:
|
||||
if (inst.args.size() > 2 && inst.args[1].valtype == ValueType::Real && inst.args[1].real == "=") {
|
||||
return varmod(inst.args);
|
||||
} else {
|
||||
error("Unknown instruction or variable");
|
||||
return Value("");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Note: 'If' and 'Let' are already handled.
|
||||
error("Unknown instruction");
|
||||
return Value("");
|
||||
}
|
||||
}
|
||||
5
src/executor/executor.h
Normal file
5
src/executor/executor.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../defs/defs.h"
|
||||
|
||||
Value execute(Instruction inst);
|
||||
18
src/filerun/filerun.cpp
Normal file
18
src/filerun/filerun.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "filerun.h"
|
||||
#include "../parser/parser.h"
|
||||
#include "../executor/executor.h"
|
||||
#include <fstream>
|
||||
|
||||
void execFile(std::string filename) {
|
||||
std::string program;
|
||||
std::ifstream file(filename);
|
||||
std::string buf;
|
||||
while (getline(file, buf)) {
|
||||
program += buf + "\n";
|
||||
}
|
||||
file.close();
|
||||
std::vector<Instruction> parsed = parse(program);
|
||||
for (Instruction inst : parsed) {
|
||||
execute(inst);
|
||||
}
|
||||
}
|
||||
5
src/filerun/filerun.h
Normal file
5
src/filerun/filerun.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
void execFile(std::string filename);
|
||||
22
src/main.cpp
Normal file
22
src/main.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "filerun/filerun.h"
|
||||
#include "repl/repl.h"
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
repl();
|
||||
} else {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-h") || strcmp(argv[i], "--help")) {
|
||||
std::cout << "Kyn programming language" << std::endl;
|
||||
std::cout << "Usage: " << argv[0] << " (filename).kyn" << std::endl;
|
||||
std::cout << "Kyn is licensed to you under the Mozilla Public License v2.0" << std::endl;
|
||||
std::cout << "See the code at https://chookspace.com/max/kyn" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
execFile(argv[1]);
|
||||
}
|
||||
}
|
||||
45
src/modules/compare/compare.cpp
Normal file
45
src/modules/compare/compare.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "compare.h"
|
||||
#include "../../error/error.h"
|
||||
#include "../math/math.h"
|
||||
|
||||
Value modules::compare(std::vector<Value> values) {
|
||||
if (values.size() != 3) {
|
||||
error("Compare instruction requires 3 arguments (e.g., compare 5 == 10)");
|
||||
return Value("0");
|
||||
}
|
||||
|
||||
if (values[0].valtype != ValueType::Real || values[1].valtype != ValueType::Real || values[2].valtype != ValueType::Real) {
|
||||
error("Compare arguments must be real values for now");
|
||||
return Value("0");
|
||||
}
|
||||
|
||||
std::string op = values[1].real;
|
||||
|
||||
if (mathstuff::strisdigit(values[0].real) && mathstuff::strisdigit(values[2].real)) {
|
||||
int lhs = std::stoi(values[0].real);
|
||||
int rhs = std::stoi(values[2].real);
|
||||
bool result = false;
|
||||
|
||||
if (op == "==") result = (lhs == rhs);
|
||||
else if (op == "!=") result = (lhs != rhs);
|
||||
else if (op == ">") result = (lhs > rhs);
|
||||
else if (op == "<") result = (lhs < rhs);
|
||||
else if (op == ">=") result = (lhs >= rhs);
|
||||
else if (op == "<=") result = (lhs <= rhs);
|
||||
else {
|
||||
error("Unknown comparison operator: " + op);
|
||||
}
|
||||
return Value(result ? "1" : "0");
|
||||
} else {
|
||||
std::string lhs = values[0].real;
|
||||
std::string rhs = values[2].real;
|
||||
bool result = false;
|
||||
|
||||
if (op == "==") result = (lhs == rhs);
|
||||
else if (op == "!=") result = (lhs != rhs);
|
||||
else {
|
||||
error("Unknown comparison operator: " + op);
|
||||
}
|
||||
return Value(result ? "1" : "0");
|
||||
}
|
||||
}
|
||||
8
src/modules/compare/compare.h
Normal file
8
src/modules/compare/compare.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value compare(std::vector<Value> values);
|
||||
}
|
||||
12
src/modules/exit/exit.cpp
Normal file
12
src/modules/exit/exit.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "exit.h"
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
#include "../math/math.h"
|
||||
|
||||
Value modules::exit(std::vector<Value> args) {
|
||||
if (args.size() > 0 && args[0].valtype == ValueType::Real && mathstuff::strisdigit(args[0].real)) {
|
||||
std::exit(stoi(args[0].real));
|
||||
} else {
|
||||
std::exit(0);
|
||||
}
|
||||
}
|
||||
8
src/modules/exit/exit.h
Normal file
8
src/modules/exit/exit.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value exit(std::vector<Value> args);
|
||||
}
|
||||
47
src/modules/if/if.cpp
Normal file
47
src/modules/if/if.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "if.h"
|
||||
#include "../../executor/executor.h"
|
||||
#include "../../error/error.h"
|
||||
|
||||
Value modules::ifs(std::vector<Value> args) {
|
||||
if (args.size() < 2) {
|
||||
error("Syntax error: 'if' statement requires a condition and a body");
|
||||
return Value();
|
||||
}
|
||||
if (args[0].valtype != ValueType::Processed) {
|
||||
error("Syntax error: 'if' condition must be an instruction");
|
||||
return Value();
|
||||
}
|
||||
if (args[1].valtype != ValueType::InstructionGroup) {
|
||||
error("Syntax error: 'if' body must be an instruction group");
|
||||
return Value();
|
||||
}
|
||||
|
||||
Value condition_result = execute(*args[0].processed);
|
||||
|
||||
bool is_true = false;
|
||||
if (condition_result.valtype == ValueType::Real) {
|
||||
if (condition_result.real != "0" && condition_result.real != "false" && !condition_result.real.empty()) {
|
||||
is_true = true;
|
||||
}
|
||||
}
|
||||
|
||||
Value return_val;
|
||||
|
||||
if (is_true) {
|
||||
for (const auto& inst : args[1].instructionGroup) {
|
||||
return_val = execute(inst);
|
||||
}
|
||||
} else {
|
||||
if (args.size() > 2) {
|
||||
if (args[2].valtype == ValueType::InstructionGroup) {
|
||||
for (const auto& inst : args[2].instructionGroup) {
|
||||
return_val = execute(inst);
|
||||
}
|
||||
} else {
|
||||
error("Syntax error: 'else' body must be an instruction group");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return return_val;
|
||||
}
|
||||
8
src/modules/if/if.h
Normal file
8
src/modules/if/if.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value ifs(std::vector<Value> args);
|
||||
}
|
||||
11
src/modules/input/input.cpp
Normal file
11
src/modules/input/input.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "input.h"
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
Value modules::input(std::vector<Value> args) {
|
||||
std::string userin;
|
||||
std::getline(std::cin, userin);
|
||||
return Value(userin);
|
||||
}
|
||||
8
src/modules/input/input.h
Normal file
8
src/modules/input/input.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value input(std::vector<Value> args);
|
||||
}
|
||||
33
src/modules/let/let.cpp
Normal file
33
src/modules/let/let.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "let.h"
|
||||
#include "../../data/data.h"
|
||||
#include "../../defs/defs.h"
|
||||
#include "../../error/error.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
Value modules::let(std::vector<Value> values) {
|
||||
std::string varName;
|
||||
if (values.size() > 0) {
|
||||
if (values[0].valtype == ValueType::Real) {
|
||||
varName = values[0].real;
|
||||
} else {
|
||||
error("FIXME unprocessed processed (in let module)");
|
||||
}
|
||||
if (values.size() > 1) {
|
||||
if (values[1].valtype == ValueType::Real && values[1].real == "=") {
|
||||
if (values.size() > 2) {
|
||||
data::vars[varName] = values[2];
|
||||
} else {
|
||||
error("Expecting value after token '=' (in let module)");
|
||||
}
|
||||
} else {
|
||||
error("Expecting token '=' (in let module)");
|
||||
}
|
||||
} else {
|
||||
data::vars[varName] = Value("");
|
||||
}
|
||||
} else {
|
||||
error("Expecting variable name (in let module)");
|
||||
}
|
||||
return Value("");
|
||||
}
|
||||
8
src/modules/let/let.h
Normal file
8
src/modules/let/let.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../data/data.h"
|
||||
#include "../../defs/defs.h"
|
||||
|
||||
namespace modules {
|
||||
Value let(std::vector<Value> values);
|
||||
}
|
||||
139
src/modules/math/math.cpp
Normal file
139
src/modules/math/math.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "math.h"
|
||||
#include "../../defs/defs.h"
|
||||
#include "../../error/error.h"
|
||||
#include <vector>
|
||||
|
||||
mathstuff::Part::Part(int in) {
|
||||
num = in;
|
||||
}
|
||||
|
||||
mathstuff::Part::Part(mathstuff::Operators in) {
|
||||
isOperator = true;
|
||||
op = in;
|
||||
}
|
||||
|
||||
bool mathstuff::strisdigit(std::string in) {
|
||||
for (char c : in) {
|
||||
if (!isdigit(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Value modules::math(std::vector<Value> values) {
|
||||
std::vector<mathstuff::Part> expression;
|
||||
for (Value value : values) {
|
||||
if (value.valtype == ValueType::Real) {
|
||||
if (value.real == "+") {
|
||||
expression.push_back(mathstuff::Part(mathstuff::Operators::Add));
|
||||
} else if (value.real == "-") {
|
||||
expression.push_back(mathstuff::Part(mathstuff::Operators::Subtract));
|
||||
} else if (value.real == "*") {
|
||||
expression.push_back(mathstuff::Part(mathstuff::Operators::Multiply));
|
||||
} else if (value.real == "/") {
|
||||
expression.push_back(mathstuff::Part(mathstuff::Operators::Divide));
|
||||
} else if (value.real == "^") {
|
||||
expression.push_back(mathstuff::Part(mathstuff::Operators::Power));
|
||||
} else if (value.real == "%") {
|
||||
expression.push_back(mathstuff::Part(mathstuff::Operators::Mod));
|
||||
} else {
|
||||
if (mathstuff::strisdigit(value.real)) {
|
||||
expression.push_back(mathstuff::Part(std::stoi(value.real)));
|
||||
} else {
|
||||
error("Expected an integer or operator (in math module)");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error("FIXME unprocessed process (in math module)");
|
||||
}
|
||||
}
|
||||
|
||||
// Power
|
||||
size_t i = 0;
|
||||
while (i < expression.size()) {
|
||||
if (expression[i].isOperator && expression[i].op == mathstuff::Operators::Power) {
|
||||
if (i >= expression.size() || expression[i + 1].isOperator) {
|
||||
error("Expecting number after * (in math module)");
|
||||
}
|
||||
if (i == 0 || expression[i - 1].isOperator) {
|
||||
error("Expecting number after * (in math module)");
|
||||
}
|
||||
expression[i] = mathstuff::Part(expression[i - 1].num * expression[i + 1].num);
|
||||
expression.erase(std::next(expression.begin(), i + 1));
|
||||
expression.erase(std::next(expression.begin(), i - 1));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// Modulo
|
||||
i = 0;
|
||||
while (i < expression.size()) {
|
||||
if (expression[i].isOperator && expression[i].op == mathstuff::Operators::Mod) {
|
||||
if (i >= expression.size() || expression[i + 1].isOperator) {
|
||||
error("Expecting number after % (in math module)");
|
||||
}
|
||||
if (i == 0 || expression[i - 1].isOperator) {
|
||||
error("Expecting number after % (in math module)");
|
||||
}
|
||||
expression[i] = mathstuff::Part(expression[i - 1].num % expression[i + 1].num);
|
||||
expression.erase(std::next(expression.begin(), i + 1));
|
||||
expression.erase(std::next(expression.begin(), i - 1));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// Multiplication + Division
|
||||
i = 0;
|
||||
while (i < expression.size()) {
|
||||
if (expression[i].isOperator && expression[i].op == mathstuff::Operators::Multiply) {
|
||||
if (i >= expression.size() || expression[i + 1].isOperator) {
|
||||
error("Expecting number after * (in math module)");
|
||||
}
|
||||
if (i == 0 || expression[i - 1].isOperator) {
|
||||
error("Expecting number after * (in math module)");
|
||||
}
|
||||
expression[i] = mathstuff::Part(expression[i - 1].num * expression[i + 1].num);
|
||||
expression.erase(std::next(expression.begin(), i + 1));
|
||||
expression.erase(std::next(expression.begin(), i - 1));
|
||||
} else if (expression[i].isOperator && expression[i].op == mathstuff::Operators::Divide) {
|
||||
if (i >= expression.size() || expression[i].isOperator) {
|
||||
error("Expecting number after / (in math module)");
|
||||
}
|
||||
if (i == 0 || expression[i].isOperator) {
|
||||
error("Expecting number after / (in math module)");
|
||||
}
|
||||
expression[i] = mathstuff::Part(expression[i - 1].num / expression[i + 1].num);
|
||||
expression.erase(std::next(expression.begin(), i + 1));
|
||||
expression.erase(std::next(expression.begin(), i - 1));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// Addition + Subtraction
|
||||
i = 0;
|
||||
while (i < expression.size()) {
|
||||
if (expression[i].isOperator && expression[i].op == mathstuff::Operators::Add) {
|
||||
if (i >= expression.size() || expression[i + 1].isOperator) {
|
||||
error("Expecting number after + (in math module)");
|
||||
}
|
||||
if (i == 0 || expression[i - 1].isOperator) {
|
||||
error("Expecting number after + (in math module)");
|
||||
}
|
||||
expression[i] = mathstuff::Part(expression[i - 1].num + expression[i + 1].num);
|
||||
expression.erase(std::next(expression.begin(), i + 1));
|
||||
expression.erase(std::next(expression.begin(), i - 1));
|
||||
} else if (expression[i].isOperator && expression[i].op == mathstuff::Operators::Subtract) {
|
||||
if (i >= expression.size() || expression[i].isOperator) {
|
||||
error("Expecting number after - (in math module)");
|
||||
}
|
||||
if (i == 0 || expression[i].isOperator) {
|
||||
error("Expecting number after - (in math module)");
|
||||
}
|
||||
expression[i] = mathstuff::Part(expression[i - 1].num - expression[i + 1].num);
|
||||
expression.erase(std::next(expression.begin(), i + 1));
|
||||
expression.erase(std::next(expression.begin(), i - 1));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return Value(std::to_string(expression[0].num));
|
||||
}
|
||||
24
src/modules/math/math.h
Normal file
24
src/modules/math/math.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace mathstuff {
|
||||
enum class Operators {
|
||||
Add, Subtract, Multiply, Divide, Mod, Power, None
|
||||
};
|
||||
|
||||
bool strisdigit(std::string in);
|
||||
|
||||
struct Part {
|
||||
Part(int in);
|
||||
Part(Operators in);
|
||||
bool isOperator = false;
|
||||
int num = 0;
|
||||
Operators op = Operators::None;
|
||||
};
|
||||
}
|
||||
|
||||
namespace modules {
|
||||
Value math(std::vector<Value> values);
|
||||
}
|
||||
17
src/modules/print/print.cpp
Normal file
17
src/modules/print/print.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "print.h"
|
||||
#include "../../defs/defs.h"
|
||||
#include "../../error/error.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
Value modules::print(std::vector<Value> values) {
|
||||
for (Value value : values) {
|
||||
if (value.valtype == ValueType::Real) {
|
||||
std::cout << value.real << " ";
|
||||
} else {
|
||||
error("FIXME unprocessed processed (in print module)");
|
||||
}
|
||||
}
|
||||
return Value("");
|
||||
}
|
||||
8
src/modules/print/print.h
Normal file
8
src/modules/print/print.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value print(std::vector<Value> values);
|
||||
}
|
||||
18
src/modules/println/println.cpp
Normal file
18
src/modules/println/println.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "println.h"
|
||||
#include "../../defs/defs.h"
|
||||
#include "../../error/error.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
Value modules::println(std::vector<Value> values) {
|
||||
for (Value value : values) {
|
||||
if (value.valtype == ValueType::Real) {
|
||||
std::cout << value.real << " ";
|
||||
} else {
|
||||
error("FIXME unprocessed processed (in println module)");
|
||||
}
|
||||
}
|
||||
std::cout << "\n";
|
||||
return Value("");
|
||||
}
|
||||
8
src/modules/println/println.h
Normal file
8
src/modules/println/println.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value println(std::vector<Value> values);
|
||||
}
|
||||
45
src/modules/while/while.cpp
Normal file
45
src/modules/while/while.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "while.h"
|
||||
#include "../../executor/executor.h"
|
||||
#include "../../error/error.h"
|
||||
|
||||
Value modules::whiles(std::vector<Value> args) {
|
||||
if (args.size() < 2) {
|
||||
error("Syntax error: 'while' statement requires a condition and a body");
|
||||
return Value();
|
||||
}
|
||||
if (args[0].valtype != ValueType::Processed) {
|
||||
error("Syntax error: 'while' condition must be an instruction");
|
||||
return Value();
|
||||
}
|
||||
if (args[1].valtype != ValueType::InstructionGroup) {
|
||||
error("Syntax error: 'while' body must be an instruction group");
|
||||
return Value();
|
||||
}
|
||||
|
||||
Value condition_result = execute(*args[0].processed);
|
||||
|
||||
bool is_true = false;
|
||||
if (condition_result.valtype == ValueType::Real) {
|
||||
if (condition_result.real != "0" && condition_result.real != "false" && !condition_result.real.empty()) {
|
||||
is_true = true;
|
||||
}
|
||||
}
|
||||
|
||||
Value return_val;
|
||||
|
||||
while (is_true) {
|
||||
for (const auto& inst : args[1].instructionGroup) {
|
||||
return_val = execute(inst);
|
||||
}
|
||||
condition_result = execute(*args[0].processed);
|
||||
|
||||
is_true = false;
|
||||
if (condition_result.valtype == ValueType::Real) {
|
||||
if (condition_result.real != "0" && condition_result.real != "false" && !condition_result.real.empty()) {
|
||||
is_true = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return return_val;
|
||||
}
|
||||
8
src/modules/while/while.h
Normal file
8
src/modules/while/while.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value whiles(std::vector<Value> args);
|
||||
}
|
||||
145
src/parser/parser.cpp
Normal file
145
src/parser/parser.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
#include "parser.h"
|
||||
#include "../error/error.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Helper to trim whitespace
|
||||
std::string trim(const std::string& str) {
|
||||
size_t first = str.find_first_not_of(" \t\n\r");
|
||||
if (std::string::npos == first) {
|
||||
return str;
|
||||
}
|
||||
size_t last = str.find_last_not_of(" \t\n\r");
|
||||
return str.substr(first, (last - first + 1));
|
||||
}
|
||||
|
||||
std::vector<Instruction> parse(std::string program) {
|
||||
std::vector<Instruction> instructions;
|
||||
std::vector<std::string> lines;
|
||||
std::string buf;
|
||||
int blockTracker = 0;
|
||||
|
||||
for (char chr : program) {
|
||||
if (chr == '{') {
|
||||
blockTracker++;
|
||||
} else if (chr == '}') {
|
||||
if (blockTracker > 0) {
|
||||
blockTracker--;
|
||||
} else {
|
||||
error("Expected opening '{' to match closing '}'");
|
||||
}
|
||||
}
|
||||
|
||||
if ((chr == '\n' || chr == ';') && blockTracker == 0) {
|
||||
if (!buf.empty()) {
|
||||
lines.push_back(buf);
|
||||
buf.clear();
|
||||
}
|
||||
} else {
|
||||
buf.push_back(chr);
|
||||
}
|
||||
}
|
||||
|
||||
if (blockTracker != 0) {
|
||||
error("Expected closing '}' to match opening '{'");
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
lines.push_back(buf);
|
||||
}
|
||||
|
||||
for (std::string line : lines) {
|
||||
line = trim(line);
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.rfind("if", 0) == 0) {
|
||||
Instruction if_inst;
|
||||
if_inst.instruction = InstructionType::If;
|
||||
|
||||
size_t block_start = line.find('{');
|
||||
if (block_start == std::string::npos) {
|
||||
error("Expected '{' for if statement");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1. Condition
|
||||
std::string condition_str = line.substr(2, block_start - 2);
|
||||
if_inst.args.push_back(Value(Instruction(split(trim(condition_str)))));
|
||||
|
||||
// 2. Find 'then' block
|
||||
size_t block_end = 0;
|
||||
int brace_level = 0;
|
||||
for (size_t i = block_start; i < line.length(); ++i) {
|
||||
if (line[i] == '{') brace_level++;
|
||||
else if (line[i] == '}') brace_level--;
|
||||
if (brace_level == 0) {
|
||||
block_end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (block_end == 0) {
|
||||
error("Mismatched braces in if statement");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string then_content = line.substr(block_start + 1, block_end - block_start - 1);
|
||||
if_inst.args.push_back(Value(parse(then_content))); // Recursive call
|
||||
|
||||
// 3. Else block (optional)
|
||||
std::string remaining_line = trim(line.substr(block_end + 1));
|
||||
if (remaining_line.rfind("else", 0) == 0) {
|
||||
size_t else_block_start = remaining_line.find('{');
|
||||
if (else_block_start == std::string::npos) {
|
||||
error("Expected '{' for else statement");
|
||||
continue;
|
||||
}
|
||||
size_t else_block_end = remaining_line.find_last_of('}');
|
||||
if (else_block_end == std::string::npos) {
|
||||
error("Expected '}' for else statement");
|
||||
continue;
|
||||
}
|
||||
std::string else_content = remaining_line.substr(else_block_start + 1, else_block_end - else_block_start - 1);
|
||||
if_inst.args.push_back(Value(parse(else_content)));
|
||||
}
|
||||
|
||||
instructions.push_back(if_inst);
|
||||
} else if (line.rfind("while", 0) == 0) {
|
||||
Instruction while_inst;
|
||||
while_inst.instruction = InstructionType::While;
|
||||
|
||||
size_t block_start = line.find('{');
|
||||
if (block_start == std::string::npos) {
|
||||
error("Expected '{' for while statement");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1. Condition
|
||||
std::string condition_str = line.substr(5, block_start - 5);
|
||||
while_inst.args.push_back(Value(Instruction(split(trim(condition_str)))));
|
||||
|
||||
// 2. Find 'then' block
|
||||
size_t block_end = 0;
|
||||
int brace_level = 0;
|
||||
for (size_t i = block_start; i < line.length(); ++i) {
|
||||
if (line[i] == '{') brace_level++;
|
||||
else if (line[i] == '}') brace_level--;
|
||||
if (brace_level == 0) {
|
||||
block_end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (block_end == 0) {
|
||||
error("Mismatched braces in while statement");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string then_content = line.substr(block_start + 1, block_end - block_start - 1);
|
||||
while_inst.args.push_back(Value(parse(then_content))); // Recursive call
|
||||
instructions.push_back(while_inst);
|
||||
} else {
|
||||
instructions.push_back(Instruction(split(line)));
|
||||
}
|
||||
}
|
||||
return instructions;
|
||||
}
|
||||
7
src/parser/parser.h
Normal file
7
src/parser/parser.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../defs/defs.h"
|
||||
|
||||
std::vector<Instruction> parse(std::string lines);
|
||||
21
src/repl/repl.cpp
Normal file
21
src/repl/repl.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "repl.h"
|
||||
#include "../executor/executor.h"
|
||||
#include "../parser/parser.h"
|
||||
#include "../defs/defs.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
void repl() {
|
||||
inReplMode = true;
|
||||
std::cout << "Kyn REPL v0.0.1" << std::endl;
|
||||
std::cout << "Type 'exit' to exit" << std::endl;
|
||||
std::string buf;
|
||||
while (true) {
|
||||
std::cout << "kyn> ";
|
||||
getline(std::cin, buf);
|
||||
std::vector<Instruction> parsed = parse(buf);
|
||||
for (Instruction inst : parsed) {
|
||||
execute(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/repl/repl.h
Normal file
1
src/repl/repl.h
Normal file
@@ -0,0 +1 @@
|
||||
void repl();
|
||||
22
src/vars/vars.cpp
Normal file
22
src/vars/vars.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "vars.h"
|
||||
|
||||
#include "../data/data.h"
|
||||
#include "../defs/defs.h"
|
||||
#include "../error/error.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
Value varmod(std::vector<Value> args) {
|
||||
if (args.size() < 3) {
|
||||
error("Not enough args to reassign variable");
|
||||
}
|
||||
if (args[1].real != "=") {
|
||||
error("Expecting '=' token");
|
||||
}
|
||||
if (args[0].valtype == ValueType::Real) {
|
||||
data::modifyValue(args[0].real, args[2]);
|
||||
} else {
|
||||
error("Invalid variable name for assignment");
|
||||
}
|
||||
return Value("");
|
||||
}
|
||||
6
src/vars/vars.h
Normal file
6
src/vars/vars.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
Value varmod(std::vector<Value> args);
|
||||
1
tests/hello.kyn
Normal file
1
tests/hello.kyn
Normal file
@@ -0,0 +1 @@
|
||||
println "Hello World!"
|
||||
Reference in New Issue
Block a user