forked from AbleScript/ablescript
Merge pull request #39 from AbleCorp/0.2.0
This commit is contained in:
commit
92134d68d5
125
Cargo.lock
generated
125
Cargo.lock
generated
|
@ -3,12 +3,19 @@
|
|||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "able-script"
|
||||
version = "0.1.0"
|
||||
name = "ablescript"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"logos",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ablescript_cli"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"ablescript",
|
||||
"clap",
|
||||
"rustyline",
|
||||
]
|
||||
|
||||
|
@ -34,9 +41,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6736e2428df2ca2848d846c43e88745121a6654696e349ce0054a420815a7409"
|
||||
checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
|
@ -46,9 +53,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.67"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -71,6 +78,17 @@ dependencies = [
|
|||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4ea1881992efc993e4dc50a324cdbd03216e41bdc8385720ff47efc9bd2ca8"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
"str-buf",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
|
@ -99,26 +117,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
name = "error-code"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"str-buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs2"
|
||||
version = "0.4.3"
|
||||
name = "fd-lock"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||
checksum = "0010f02effd88c702318c5dde0463206be67495d0b4d906ba7c0a8f166cc7f06"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.2"
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -127,18 +155,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.93"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
@ -175,9 +203,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
|
@ -208,9 +236,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.26"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
||||
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -236,9 +264,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
|
@ -248,9 +276,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
|
@ -258,27 +286,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.7"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
@ -295,20 +323,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.23"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "8.0.0"
|
||||
version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e1b597fcd1eeb1d6b25b493538e5aa19629eb08932184b85fef931ba87e893"
|
||||
checksum = "fbd4eaf7a7738f76c98e4f0395253ae853be3eb018f7b0bb57fe1b6c17e31874"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"dirs-next",
|
||||
"fs2",
|
||||
"fd-lock",
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
|
@ -334,6 +363,12 @@ version = "1.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "str-buf"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
@ -342,9 +377,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.69"
|
||||
version = "1.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
|
||||
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -362,9 +397,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
|
@ -374,9 +409,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
|
|
20
Cargo.toml
20
Cargo.toml
|
@ -1,18 +1,2 @@
|
|||
[package]
|
||||
name = "able-script"
|
||||
version = "0.1.0"
|
||||
authors = ["able <abl3theabove@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
description = "The best programming language"
|
||||
license-file = "LICENSE"
|
||||
documentation = "https://ablecorp.us/able-script-the-book/"
|
||||
repository = "https://github.com/AbleCorp/able-script"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
logos = "0.12"
|
||||
rand = "0.8"
|
||||
rustyline = "8.0.0"
|
||||
[workspace]
|
||||
members = ["ablescript", "ablescript_cli"]
|
26
LICENSE
26
LICENSE
|
@ -1,7 +1,21 @@
|
|||
You can do whatever you want with this software assuming you meet the following conditions:
|
||||
1) Answer me why?
|
||||
2) Never attribute me
|
||||
MIT License
|
||||
|
||||
You can do whatever you want with this license assuming you meet the following conditions:
|
||||
1) attribute me
|
||||
2) I (AbleTheAbove#3819 on discord) own you
|
||||
Copyright (c) 2021 AbleCorp
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
8
able-script-test/carts.able
Normal file
8
able-script-test/carts.able
Normal file
|
@ -0,0 +1,8 @@
|
|||
functio helloable() {
|
||||
"Hello, Able!" print;
|
||||
}
|
||||
|
||||
var cart = ["able" <= 42, helloable <= "hello"];
|
||||
|
||||
cart[42] print;
|
||||
cart["hello"]();
|
16
ablescript/Cargo.toml
Normal file
16
ablescript/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "ablescript"
|
||||
version = "0.2.0"
|
||||
authors = ["able <abl3theabove@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
description = "The best programming language"
|
||||
license = "MIT"
|
||||
documentation = "https://ablecorp.us/able-script-the-book/"
|
||||
repository = "https://github.com/AbleCorp/able-script"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
logos = "0.12"
|
||||
rand = "0.8"
|
|
@ -8,11 +8,13 @@
|
|||
//! just plain subroutines and they do not return any value,
|
||||
//! so their calls are statements.
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::variables::Value;
|
||||
|
||||
type Span = std::ops::Range<usize>;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Iden {
|
||||
pub iden: String,
|
||||
pub span: Span,
|
||||
|
@ -24,19 +26,43 @@ impl Iden {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
impl PartialEq for Iden {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.iden == other.iden
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Iden {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.iden.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
pub struct Block {
|
||||
pub block: Vec<Stmt>,
|
||||
}
|
||||
|
||||
/// A syntactic unit expressing an effect.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Stmt {
|
||||
pub kind: StmtKind,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
impl PartialEq for Stmt {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.kind == other.kind
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Stmt {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.kind.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
pub enum StmtKind {
|
||||
// Control flow
|
||||
If {
|
||||
|
@ -69,7 +95,7 @@ pub enum StmtKind {
|
|||
code: Vec<u8>,
|
||||
},
|
||||
Call {
|
||||
iden: Iden,
|
||||
expr: Expr,
|
||||
args: Vec<Expr>,
|
||||
},
|
||||
Print(Expr),
|
||||
|
@ -87,13 +113,25 @@ impl Stmt {
|
|||
|
||||
/// Expression is parse unit which do not cause any effect,
|
||||
/// like math and logical operations or values.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Expr {
|
||||
pub kind: ExprKind,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
impl PartialEq for Expr {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.kind == other.kind
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Expr {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.kind.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
pub enum ExprKind {
|
||||
BinOp {
|
||||
lhs: Box<Expr>,
|
||||
|
@ -102,6 +140,11 @@ pub enum ExprKind {
|
|||
},
|
||||
Not(Box<Expr>),
|
||||
Literal(Value),
|
||||
Cart(Vec<(Expr, Expr)>),
|
||||
Index {
|
||||
cart: Box<Expr>,
|
||||
index: Box<Expr>,
|
||||
},
|
||||
Variable(String),
|
||||
}
|
||||
|
||||
|
@ -111,7 +154,7 @@ impl Expr {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
pub enum BinOpKind {
|
||||
Add,
|
||||
Subtract,
|
|
@ -10,10 +10,8 @@ pub struct Error {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorKind {
|
||||
SyntaxError(String),
|
||||
UnexpectedEof,
|
||||
UnexpectedToken(Token),
|
||||
InvalidIdentifier,
|
||||
UnknownVariable(String),
|
||||
MeloVariable(String),
|
||||
TopLevelBreak,
|
||||
|
@ -49,10 +47,8 @@ impl std::error::Error for Error {}
|
|||
impl Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ErrorKind::SyntaxError(desc) => write!(f, "syntax error: {}", desc),
|
||||
ErrorKind::UnexpectedEof => write!(f, "unexpected end of file"),
|
||||
ErrorKind::UnexpectedToken(token) => write!(f, "unexpected token {:?}", token),
|
||||
ErrorKind::InvalidIdentifier => write!(f, "invalid identifier"),
|
||||
ErrorKind::UnknownVariable(name) => write!(f, "unknown identifier \"{}\"", name),
|
||||
ErrorKind::MeloVariable(name) => write!(f, "banned variable \"{}\"", name),
|
||||
ErrorKind::TopLevelBreak => write!(f, "can only `break` out of a loop"),
|
|
@ -138,13 +138,13 @@ impl ExecEnv {
|
|||
|
||||
Ok(match &expr.kind {
|
||||
BinOp { lhs, rhs, kind } => {
|
||||
let lhs = self.eval_expr(&lhs)?;
|
||||
let rhs = self.eval_expr(&rhs)?;
|
||||
let lhs = self.eval_expr(lhs)?;
|
||||
let rhs = self.eval_expr(rhs)?;
|
||||
match kind {
|
||||
// Arithmetic operators.
|
||||
Add | Subtract | Multiply | Divide => {
|
||||
let lhs = lhs.into_i32();
|
||||
let rhs = rhs.into_i32();
|
||||
let lhs = lhs.to_i32();
|
||||
let rhs = rhs.to_i32();
|
||||
|
||||
let res = match kind {
|
||||
Add => lhs.checked_add(rhs),
|
||||
|
@ -159,8 +159,8 @@ impl ExecEnv {
|
|||
|
||||
// Numeric comparisons.
|
||||
Less | Greater => {
|
||||
let lhs = lhs.into_i32();
|
||||
let rhs = rhs.into_i32();
|
||||
let lhs = lhs.to_i32();
|
||||
let rhs = rhs.to_i32();
|
||||
|
||||
let res = match kind {
|
||||
Less => lhs < rhs,
|
||||
|
@ -182,8 +182,8 @@ impl ExecEnv {
|
|||
|
||||
// Logical connectives.
|
||||
And | Or => {
|
||||
let lhs = lhs.into_bool();
|
||||
let rhs = rhs.into_bool();
|
||||
let lhs = lhs.to_bool();
|
||||
let rhs = rhs.to_bool();
|
||||
let res = match kind {
|
||||
And => lhs && rhs,
|
||||
Or => lhs || rhs,
|
||||
|
@ -193,8 +193,26 @@ impl ExecEnv {
|
|||
}
|
||||
}
|
||||
}
|
||||
Not(expr) => Bool(!self.eval_expr(&expr)?.into_bool()),
|
||||
Not(expr) => Bool(!self.eval_expr(expr)?.to_bool()),
|
||||
Literal(value) => value.clone(),
|
||||
ExprKind::Cart(members) => Value::Cart(
|
||||
members
|
||||
.iter()
|
||||
.map(|(value, key)| {
|
||||
self.eval_expr(value).and_then(|value| {
|
||||
self.eval_expr(key)
|
||||
.map(|key| (key, Rc::new(RefCell::new(value))))
|
||||
})
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, _>>()?,
|
||||
),
|
||||
Index { cart, index } => {
|
||||
let cart = self.eval_expr(cart)?;
|
||||
let index = self.eval_expr(index)?;
|
||||
|
||||
// TODO: this probably shouldn't be cloned
|
||||
cart.index(&index).borrow().clone()
|
||||
}
|
||||
|
||||
// TODO: not too happy with constructing an artificial
|
||||
// Iden here.
|
||||
|
@ -239,21 +257,21 @@ impl ExecEnv {
|
|||
instructions: code.to_owned(),
|
||||
tape_len: tape_len
|
||||
.as_ref()
|
||||
.map(|tape_len| self.eval_expr(tape_len).map(|v| v.into_i32() as usize))
|
||||
.map(|tape_len| self.eval_expr(tape_len).map(|v| v.to_i32() as usize))
|
||||
.unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?,
|
||||
}),
|
||||
);
|
||||
}
|
||||
StmtKind::If { cond, body } => {
|
||||
if self.eval_expr(cond)?.into_bool() {
|
||||
if self.eval_expr(cond)?.to_bool() {
|
||||
return self.eval_stmts_hs(&body.block, true);
|
||||
}
|
||||
}
|
||||
StmtKind::Call { iden, args } => {
|
||||
let func = self.get_var(&iden)?;
|
||||
StmtKind::Call { expr, args } => {
|
||||
let func = self.eval_expr(expr)?;
|
||||
|
||||
if let Value::Functio(func) = func {
|
||||
self.fn_call(func, &args, &stmt.span)?;
|
||||
self.fn_call(func, args, &stmt.span)?;
|
||||
} else {
|
||||
// Fail silently for now.
|
||||
}
|
||||
|
@ -268,7 +286,7 @@ impl ExecEnv {
|
|||
},
|
||||
StmtKind::Assign { iden, value } => {
|
||||
let value = self.eval_expr(value)?;
|
||||
self.get_var_mut(&iden)?.value.replace(value);
|
||||
self.get_var_mut(iden)?.value.replace(value);
|
||||
}
|
||||
StmtKind::Break => {
|
||||
return Ok(HaltStatus::Break(stmt.span.clone()));
|
||||
|
@ -277,7 +295,7 @@ impl ExecEnv {
|
|||
return Ok(HaltStatus::Hopback(stmt.span.clone()));
|
||||
}
|
||||
StmtKind::Melo(iden) => {
|
||||
self.get_var_mut(&iden)?.melo = true;
|
||||
self.get_var_mut(iden)?.melo = true;
|
||||
}
|
||||
StmtKind::Rlyeh => {
|
||||
// Maybe print a creepy error message or something
|
||||
|
@ -296,7 +314,7 @@ impl ExecEnv {
|
|||
value += self.get_bit()? as i32;
|
||||
}
|
||||
|
||||
self.get_var_mut(&iden)?.value.replace(Value::Int(value));
|
||||
self.get_var_mut(iden)?.value.replace(Value::Int(value));
|
||||
}
|
||||
}
|
||||
|
|
@ -48,6 +48,9 @@ pub enum Token {
|
|||
#[token("=")]
|
||||
Equal,
|
||||
|
||||
#[token("<=")]
|
||||
Arrow,
|
||||
|
||||
// Logical operators
|
||||
#[token("<")]
|
||||
LessThan,
|
12
ablescript/src/lib.rs
Normal file
12
ablescript/src/lib.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
#![forbid(unsafe_code, clippy::unwrap_used)]
|
||||
|
||||
pub mod ast;
|
||||
pub mod interpret;
|
||||
pub mod parser;
|
||||
|
||||
mod base_55;
|
||||
mod brian;
|
||||
mod consts;
|
||||
mod error;
|
||||
mod lexer;
|
||||
mod variables;
|
|
@ -98,6 +98,7 @@ impl<'source> Parser<'source> {
|
|||
| Token::Integer(_)
|
||||
| Token::Abool(_)
|
||||
| Token::Bool(_)
|
||||
| Token::LeftBracket
|
||||
| Token::LeftParen => Ok(Stmt::new(
|
||||
self.value_flow(token)?,
|
||||
start..self.lexer.span().end,
|
||||
|
@ -188,6 +189,17 @@ impl<'source> Parser<'source> {
|
|||
start..self.lexer.span().end,
|
||||
)),
|
||||
|
||||
Token::LeftBracket => match buf.take() {
|
||||
Some(buf) => Ok(Expr::new(
|
||||
ExprKind::Index {
|
||||
cart: Box::new(buf),
|
||||
index: Box::new(self.expr_flow(Token::RightBracket)?),
|
||||
},
|
||||
start..self.lexer.span().end,
|
||||
)),
|
||||
None => Ok(Expr::new(self.cart_flow()?, start..self.lexer.span().end)),
|
||||
},
|
||||
|
||||
// Operations
|
||||
Token::Plus
|
||||
| Token::Minus
|
||||
|
@ -213,11 +225,58 @@ impl<'source> Parser<'source> {
|
|||
},
|
||||
start..self.lexer.span().end,
|
||||
)),
|
||||
|
||||
Token::LeftParen => self.expr_flow(Token::RightParen),
|
||||
t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Flow for creating carts
|
||||
fn cart_flow(&mut self) -> Result<ExprKind, Error> {
|
||||
let mut cart = vec![];
|
||||
let mut buf = None;
|
||||
|
||||
match self.checked_next()? {
|
||||
Token::RightBracket => (),
|
||||
t => {
|
||||
buf = Some(self.parse_expr(t, &mut buf)?);
|
||||
'cart: loop {
|
||||
let value = loop {
|
||||
match self.checked_next()? {
|
||||
Token::Arrow => break buf.take(),
|
||||
t => buf = Some(self.parse_expr(t, &mut buf)?),
|
||||
}
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
Error::new(ErrorKind::UnexpectedToken(Token::Arrow), self.lexer.span())
|
||||
})?;
|
||||
|
||||
let key = loop {
|
||||
match self.checked_next()? {
|
||||
Token::RightBracket => {
|
||||
cart.push((
|
||||
value,
|
||||
buf.take().ok_or_else(|| {
|
||||
Error::unexpected_eof(self.lexer.span().start)
|
||||
})?,
|
||||
));
|
||||
|
||||
break 'cart;
|
||||
}
|
||||
Token::Comma => break buf.take(),
|
||||
t => buf = Some(self.parse_expr(t, &mut buf)?),
|
||||
}
|
||||
}
|
||||
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?;
|
||||
|
||||
cart.push((value, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ExprKind::Cart(cart))
|
||||
}
|
||||
|
||||
/// Flow for operators
|
||||
///
|
||||
/// Generates operation from LHS buffer and next expression as RHS
|
||||
|
@ -286,13 +345,12 @@ impl<'source> Parser<'source> {
|
|||
|
||||
// Functio call
|
||||
Token::LeftParen => {
|
||||
if let Some(Expr {
|
||||
kind: ExprKind::Variable(iden),
|
||||
span,
|
||||
}) = buf
|
||||
{
|
||||
break self.functio_call_flow(Iden::new(iden, span))?;
|
||||
}
|
||||
break self.functio_call_flow(buf.take().ok_or_else(|| {
|
||||
Error::new(
|
||||
ErrorKind::UnexpectedToken(Token::LeftParen),
|
||||
self.lexer.span(),
|
||||
)
|
||||
})?)?;
|
||||
}
|
||||
|
||||
// Variable Assignment
|
||||
|
@ -421,7 +479,7 @@ impl<'source> Parser<'source> {
|
|||
}
|
||||
|
||||
/// Parse functio call flow
|
||||
fn functio_call_flow(&mut self, iden: Iden) -> Result<StmtKind, Error> {
|
||||
fn functio_call_flow(&mut self, expr: Expr) -> Result<StmtKind, Error> {
|
||||
let mut args = vec![];
|
||||
let mut buf = None;
|
||||
loop {
|
||||
|
@ -450,7 +508,7 @@ impl<'source> Parser<'source> {
|
|||
}
|
||||
|
||||
self.require(Token::Semicolon)?;
|
||||
Ok(StmtKind::Call { iden, args })
|
||||
Ok(StmtKind::Call { expr, args })
|
||||
}
|
||||
|
||||
/// Parse variable declaration
|
||||
|
@ -617,4 +675,83 @@ mod tests {
|
|||
let ast = Parser::new(code).init().unwrap();
|
||||
assert_eq!(ast, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cart_construction() {
|
||||
let code = r#"["able" <= 1, "script" <= 3 - 1] print;"#;
|
||||
let expected = &[Stmt {
|
||||
kind: StmtKind::Print(Expr {
|
||||
kind: ExprKind::Cart(vec![
|
||||
(
|
||||
Expr {
|
||||
kind: ExprKind::Literal(Value::Str("able".to_string())),
|
||||
span: 1..7,
|
||||
},
|
||||
Expr {
|
||||
kind: ExprKind::Literal(Value::Int(1)),
|
||||
span: 11..12,
|
||||
},
|
||||
),
|
||||
(
|
||||
Expr {
|
||||
kind: ExprKind::Literal(Value::Str("script".to_string())),
|
||||
span: 14..22,
|
||||
},
|
||||
Expr {
|
||||
kind: ExprKind::BinOp {
|
||||
kind: BinOpKind::Subtract,
|
||||
lhs: Box::new(Expr {
|
||||
kind: ExprKind::Literal(Value::Int(3)),
|
||||
span: 26..27,
|
||||
}),
|
||||
rhs: Box::new(Expr {
|
||||
kind: ExprKind::Literal(Value::Int(1)),
|
||||
span: 30..31,
|
||||
}),
|
||||
},
|
||||
span: 26..31,
|
||||
},
|
||||
),
|
||||
]),
|
||||
span: 0..32,
|
||||
}),
|
||||
span: 0..39,
|
||||
}];
|
||||
|
||||
let ast = Parser::new(code).init().unwrap();
|
||||
assert_eq!(ast, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cart_index() {
|
||||
let code = r#"["able" <= "ablecorp"]["ablecorp"] print;"#;
|
||||
let expected = &[Stmt {
|
||||
kind: StmtKind::Print(Expr {
|
||||
kind: ExprKind::Index {
|
||||
cart: Box::new(Expr {
|
||||
kind: ExprKind::Cart(vec![(
|
||||
Expr {
|
||||
kind: ExprKind::Literal(Value::Str("able".to_string())),
|
||||
span: 1..7,
|
||||
},
|
||||
Expr {
|
||||
kind: ExprKind::Literal(Value::Str("ablecorp".to_string())),
|
||||
span: 11..21,
|
||||
},
|
||||
)]),
|
||||
span: 0..22,
|
||||
}),
|
||||
index: Box::new(Expr {
|
||||
kind: ExprKind::Literal(Value::Str("ablecorp".to_owned())),
|
||||
span: 23..33,
|
||||
}),
|
||||
},
|
||||
span: 0..34,
|
||||
}),
|
||||
span: 0..41,
|
||||
}];
|
||||
|
||||
let ast = Parser::new(code).init().unwrap();
|
||||
assert_eq!(ast, expected);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
use std::{cell::RefCell, fmt::Display, io::Write, rc::Rc};
|
||||
use std::{
|
||||
cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, io::Write, mem::discriminant,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{ast::Stmt, consts};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Abool {
|
||||
Never = -1,
|
||||
Sometimes = 0,
|
||||
|
@ -31,7 +34,7 @@ impl From<Abool> for bool {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
pub enum Functio {
|
||||
BfFunctio {
|
||||
instructions: Vec<u8>,
|
||||
|
@ -43,7 +46,7 @@ pub enum Functio {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
Nul,
|
||||
Str(String),
|
||||
|
@ -51,8 +54,42 @@ pub enum Value {
|
|||
Bool(bool),
|
||||
Abool(Abool),
|
||||
Functio(Functio),
|
||||
Cart(HashMap<Value, Rc<RefCell<Value>>>),
|
||||
}
|
||||
|
||||
impl Hash for Value {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
discriminant(self).hash(state);
|
||||
match self {
|
||||
Value::Nul => (),
|
||||
Value::Str(v) => v.hash(state),
|
||||
Value::Int(v) => v.hash(state),
|
||||
Value::Bool(v) => v.hash(state),
|
||||
Value::Abool(v) => v.to_string().hash(state),
|
||||
Value::Functio(statements) => statements.hash(state),
|
||||
Value::Cart(_) => self.to_string().hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Value {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Value::Nul, Value::Nul) => true,
|
||||
(Value::Str(left), Value::Str(right)) => left == right,
|
||||
(Value::Int(left), Value::Int(right)) => left == right,
|
||||
(Value::Bool(left), Value::Bool(right)) => left == right,
|
||||
(Value::Abool(left), Value::Abool(right)) => left == right,
|
||||
(Value::Functio(left), Value::Functio(right)) => left == right,
|
||||
(Value::Cart(_), Value::Cart(_)) => self.to_string() == other.to_string(),
|
||||
(_, _) => false,
|
||||
// TODO: do more coercions!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Value {}
|
||||
|
||||
impl Value {
|
||||
/// Write an AbleScript value to a Brainfuck input stream by
|
||||
/// coercing the value to an integer, then truncating that integer
|
||||
|
@ -61,15 +98,15 @@ impl Value {
|
|||
/// any IO errors will cause a panic.
|
||||
pub fn bf_write(&self, stream: &mut impl Write) {
|
||||
stream
|
||||
.write_all(&[self.clone().into_i32() as u8])
|
||||
.write_all(&[self.clone().to_i32() as u8])
|
||||
.expect("Failed to write to Brainfuck input");
|
||||
}
|
||||
|
||||
/// Coerce a value to an integer.
|
||||
pub fn into_i32(self) -> i32 {
|
||||
pub fn to_i32(&self) -> i32 {
|
||||
match self {
|
||||
Value::Abool(a) => a as _,
|
||||
Value::Bool(b) => b as _,
|
||||
Value::Abool(a) => *a as _,
|
||||
Value::Bool(b) => *b as _,
|
||||
Value::Functio(func) => match func {
|
||||
Functio::BfFunctio {
|
||||
instructions,
|
||||
|
@ -77,23 +114,47 @@ impl Value {
|
|||
} => (instructions.len() + tape_len) as _,
|
||||
Functio::AbleFunctio { params, body } => (params.len() + body.len()) as _,
|
||||
},
|
||||
Value::Int(i) => i,
|
||||
Value::Int(i) => *i,
|
||||
Value::Nul => consts::ANSWER,
|
||||
Value::Str(text) => text.parse().unwrap_or(consts::ANSWER),
|
||||
Value::Cart(c) => c.len() as _,
|
||||
}
|
||||
}
|
||||
|
||||
/// Coerce a Value to a boolean. The conversion cannot fail.
|
||||
pub fn into_bool(self) -> bool {
|
||||
pub fn to_bool(&self) -> bool {
|
||||
match self {
|
||||
Value::Abool(b) => b.into(),
|
||||
Value::Bool(b) => b,
|
||||
Value::Abool(b) => (*b).into(),
|
||||
Value::Bool(b) => *b,
|
||||
Value::Functio(_) => true,
|
||||
Value::Int(x) => x != 0,
|
||||
Value::Int(x) => *x != 0,
|
||||
Value::Nul => true,
|
||||
Value::Str(s) => !s.is_empty(),
|
||||
Value::Cart(c) => !c.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Index a value with another value, as in the "a[b]" syntax.
|
||||
pub fn index(&self, index: &Value) -> Rc<RefCell<Value>> {
|
||||
Rc::new(RefCell::new(match self {
|
||||
Value::Nul => Value::Nul,
|
||||
Value::Str(s) => Value::Int(s.as_bytes()[index.to_i32() as usize] as i32),
|
||||
Value::Int(i) => {
|
||||
Value::Int((format!("{}", i).as_bytes()[index.to_i32() as usize] - b'0') as i32)
|
||||
}
|
||||
Value::Bool(b) => Value::Int(
|
||||
format!("{}", b)
|
||||
.chars()
|
||||
.nth(index.to_i32() as usize)
|
||||
.unwrap_or('?') as i32,
|
||||
),
|
||||
Value::Abool(b) => Value::Int(*b as i32),
|
||||
Value::Functio(_) => Value::Int(42),
|
||||
Value::Cart(c) => {
|
||||
return (c.get(index).cloned()).unwrap_or_else(|| Rc::new(RefCell::new(Value::Nul)))
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
|
@ -128,6 +189,15 @@ impl Display for Value {
|
|||
)
|
||||
}
|
||||
},
|
||||
Value::Cart(c) => {
|
||||
write!(f, "[")?;
|
||||
|
||||
for (key, value) in c {
|
||||
write!(f, "{} <= {},", value.borrow(), key)?;
|
||||
}
|
||||
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
ablescript_cli/Cargo.toml
Normal file
18
ablescript_cli/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "ablescript_cli"
|
||||
version = "0.2.0"
|
||||
authors = ["able <abl3theabove@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
description = "The best programming language"
|
||||
license = "MIT"
|
||||
documentation = "https://ablecorp.us/able-script-the-book/"
|
||||
repository = "https://github.com/AbleCorp/able-script"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ablescript = { path = "../ablescript" }
|
||||
|
||||
clap = "2.33"
|
||||
rustyline = "8.0.0"
|
|
@ -1,22 +1,11 @@
|
|||
#![forbid(unsafe_code, clippy::unwrap_used)]
|
||||
|
||||
mod ast;
|
||||
mod base_55;
|
||||
mod brian;
|
||||
mod consts;
|
||||
mod error;
|
||||
mod interpret;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
mod repl;
|
||||
mod variables;
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
use ablescript::interpret::ExecEnv;
|
||||
use ablescript::parser::Parser;
|
||||
use clap::{App, Arg};
|
||||
use interpret::ExecEnv;
|
||||
use logos::Source;
|
||||
use parser::Parser;
|
||||
|
||||
fn main() {
|
||||
// variables::test(); // NOTE(Able): Add this as a test case
|
||||
|
@ -64,7 +53,7 @@ fn main() {
|
|||
"Error `{:?}` occurred at span: {:?} = `{:?}`",
|
||||
e.kind,
|
||||
e.span.clone(),
|
||||
source.slice(e.span)
|
||||
&source[e.span]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use rustyline::Editor;
|
||||
|
||||
use crate::{interpret::ExecEnv, parser::Parser};
|
||||
use ablescript::interpret::ExecEnv;
|
||||
use ablescript::parser::Parser;
|
||||
|
||||
pub fn repl(ast_print: bool) {
|
||||
let mut rl = Editor::<()>::new();
|
||||
|
@ -13,13 +14,15 @@ pub fn repl(ast_print: bool) {
|
|||
// end of the string if stdin is connected to a file
|
||||
// or unsupported terminal; this can interfere with
|
||||
// error printing.
|
||||
rl.add_history_entry(&line);
|
||||
let line = line.trim_end();
|
||||
|
||||
if line == "exit" {
|
||||
println!("bye");
|
||||
break;
|
||||
}
|
||||
let mut parser = Parser::new(&line);
|
||||
|
||||
let mut parser = Parser::new(line);
|
||||
let value = parser.init().and_then(|ast| {
|
||||
if ast_print {
|
||||
println!("{:#?}", &ast);
|
Loading…
Reference in a new issue