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