Merge pull request #12 from erindesu/master
This commit is contained in:
commit
a0b3f7c34a
156
Cargo.lock
generated
156
Cargo.lock
generated
|
@ -9,6 +9,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"logos",
|
"logos",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rustyline",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -43,6 +44,12 @@ version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -64,12 +71,49 @@ dependencies = [
|
||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-next"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"dirs-sys-next",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys-next"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "endian-type"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs2"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -96,6 +140,15 @@ version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "logos"
|
name = "logos"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
@ -120,6 +173,33 @@ dependencies = [
|
||||||
"utf8-ranges",
|
"utf8-ranges",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nibble_vec"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
|
@ -144,6 +224,16 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radix_trie"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||||
|
dependencies = [
|
||||||
|
"endian-type",
|
||||||
|
"nibble_vec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
@ -184,12 +274,66 @@ dependencies = [
|
||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"redox_syscall",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.23"
|
version = "0.6.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustyline"
|
||||||
|
version = "8.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e1b597fcd1eeb1d6b25b493538e5aa19629eb08932184b85fef931ba87e893"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"dirs-next",
|
||||||
|
"fs2",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"nix",
|
||||||
|
"radix_trie",
|
||||||
|
"scopeguard",
|
||||||
|
"smallvec",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width",
|
||||||
|
"utf8parse",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -216,6 +360,12 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
@ -234,6 +384,12 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
|
checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|
|
@ -10,3 +10,4 @@ edition = "2018"
|
||||||
clap="*"
|
clap="*"
|
||||||
logos = "0.12"
|
logos = "0.12"
|
||||||
rand = "*"
|
rand = "*"
|
||||||
|
rustyline = "8.0.0"
|
|
@ -1,4 +1,4 @@
|
||||||
# Ablescript
|
# AbleScript
|
||||||
![Lines of code](https://img.shields.io/tokei/lines/github/abletheabove/able-script)
|
![Lines of code](https://img.shields.io/tokei/lines/github/abletheabove/able-script)
|
||||||
|
|
||||||
Bravely going where some languages have gone before.
|
Bravely going where some languages have gone before.
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
var hi = "wonk";
|
var hi = "wonk";
|
||||||
melo hi;
|
melo hi;
|
||||||
hi print; #Should error out
|
hi print; # Should error out
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
if (true) {
|
|
||||||
var a = 3;
|
|
||||||
}
|
|
|
@ -1,7 +1,65 @@
|
||||||
use logos::{Lexer, Logos};
|
use logos::{Lexer, Logos, Span};
|
||||||
|
|
||||||
use crate::variables::Abool;
|
use crate::variables::Abool;
|
||||||
|
|
||||||
|
pub struct PeekableLexer<'source> {
|
||||||
|
lexer: Lexer<'source, Token>,
|
||||||
|
peeked: Option<Option<Token>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'source> PeekableLexer<'source> {
|
||||||
|
pub fn lexer(source: &'source str) -> Self {
|
||||||
|
Self {
|
||||||
|
lexer: Token::lexer(source),
|
||||||
|
peeked: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the next() value without advancing the iterator.
|
||||||
|
#[inline]
|
||||||
|
pub fn peek(&mut self) -> &Option<Token> {
|
||||||
|
if self.peeked.is_none() {
|
||||||
|
self.peeked = Some(self.lexer.next());
|
||||||
|
}
|
||||||
|
self.peeked.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the range for the current token in `Source`.
|
||||||
|
#[inline]
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
self.lexer.span()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a string slice of the current token.
|
||||||
|
#[inline]
|
||||||
|
pub fn slice(&self) -> &'source str {
|
||||||
|
self.lexer.slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a slice of remaining source, starting at the end of current token.
|
||||||
|
#[inline]
|
||||||
|
pub fn remainder(&self) -> &'source str {
|
||||||
|
self.lexer.remainder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'source> Iterator for PeekableLexer<'source> {
|
||||||
|
type Item = Token;
|
||||||
|
|
||||||
|
/// Advances the iterator and returns the next value.
|
||||||
|
///
|
||||||
|
/// Returns [`None`] when iteration is finished.
|
||||||
|
/// Individual iterator implementations may choose to resume iteration, and so calling `next()`
|
||||||
|
/// again may or may not eventually start returning [`Some(Item)`] again at some point.
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.peeked.take() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => self.lexer.next(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Logos, Debug, PartialEq, Clone)]
|
#[derive(Logos, Debug, PartialEq, Clone)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
#[token("functio")]
|
#[token("functio")]
|
||||||
|
@ -36,6 +94,12 @@ pub enum Token {
|
||||||
#[token("loop")]
|
#[token("loop")]
|
||||||
Loop,
|
Loop,
|
||||||
|
|
||||||
|
#[token("break")]
|
||||||
|
Break,
|
||||||
|
|
||||||
|
#[token("hopback")]
|
||||||
|
HopBack,
|
||||||
|
|
||||||
// Literals
|
// Literals
|
||||||
/// True, False
|
/// True, False
|
||||||
#[regex("true|false", get_bool)]
|
#[regex("true|false", get_bool)]
|
||||||
|
@ -57,6 +121,9 @@ pub enum Token {
|
||||||
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)]
|
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)]
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
|
|
||||||
|
#[regex("nul")]
|
||||||
|
Nul,
|
||||||
|
|
||||||
#[token("(")]
|
#[token("(")]
|
||||||
LeftParenthesis,
|
LeftParenthesis,
|
||||||
|
|
||||||
|
@ -116,6 +183,15 @@ pub enum Token {
|
||||||
#[token("!=")]
|
#[token("!=")]
|
||||||
OpNeq,
|
OpNeq,
|
||||||
|
|
||||||
|
#[token("&")]
|
||||||
|
LogAnd,
|
||||||
|
|
||||||
|
#[token("|")]
|
||||||
|
LogOr,
|
||||||
|
|
||||||
|
#[token("!")]
|
||||||
|
LogNot,
|
||||||
|
|
||||||
/// Base52 based character ('a')
|
/// Base52 based character ('a')
|
||||||
#[token("'.*'")]
|
#[token("'.*'")]
|
||||||
Char,
|
Char,
|
25
src/main.rs
25
src/main.rs
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
mod base_55;
|
mod base_55;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod lexer;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod tokens;
|
mod repl;
|
||||||
mod variables;
|
mod variables;
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
use logos::Source;
|
||||||
use parser::Parser;
|
use parser::Parser;
|
||||||
fn main() {
|
fn main() {
|
||||||
// variables::test();
|
// variables::test();
|
||||||
|
@ -14,7 +16,7 @@ fn main() {
|
||||||
let matches = App::new("AbleScript")
|
let matches = App::new("AbleScript")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.author("Able <abl3theabove@gmail.com>")
|
.author("Able <abl3theabove@gmail.com>")
|
||||||
.about("Does awesome things")
|
.about("AbleScript interpreter")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("file")
|
Arg::with_name("file")
|
||||||
.short("f")
|
.short("f")
|
||||||
|
@ -33,11 +35,24 @@ fn main() {
|
||||||
// Parse
|
// Parse
|
||||||
let mut parser = Parser::new(&source);
|
let mut parser = Parser::new(&source);
|
||||||
let ast = parser.init();
|
let ast = parser.init();
|
||||||
println!("{:#?}", ast);
|
match ast {
|
||||||
|
Ok(ast) => println!("{:#?}", ast),
|
||||||
|
Err(e) => {
|
||||||
|
println!(
|
||||||
|
"Error `{:?}` occured at span: {:?} = `{:?}`",
|
||||||
|
e.kind,
|
||||||
|
e.position.clone(),
|
||||||
|
source.slice(e.position)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("hi");
|
println!(
|
||||||
//start the prompt
|
"Hi [AbleScript {}] - AST Printer",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
);
|
||||||
|
repl::repl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,82 @@
|
||||||
use crate::variables::Value;
|
use crate::variables::Value;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct Iden(pub String);
|
pub struct Iden(pub String);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Item {
|
||||||
|
Expr(Expr),
|
||||||
|
Stmt(Stmt),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Expr> for Item {
|
||||||
|
fn from(e: Expr) -> Self {
|
||||||
|
Item::Expr(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Iden> for Item {
|
||||||
|
fn from(i: Iden) -> Self {
|
||||||
|
Item::Expr(Expr::Identifier(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Stmt> for Item {
|
||||||
|
fn from(s: Stmt) -> Self {
|
||||||
|
Item::Stmt(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
|
Add { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Subtract { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Multiply { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Divide { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Lt { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Gt { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Eq { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Neq { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
And { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Or { left: Box<Expr>, right: Box<Expr> },
|
||||||
|
Not(Box<Expr>),
|
||||||
|
Literal(Value),
|
||||||
|
Identifier(Iden),
|
||||||
|
}
|
||||||
|
impl From<Iden> for Expr {
|
||||||
|
fn from(i: Iden) -> Self {
|
||||||
|
Self::Identifier(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Stmt {
|
||||||
VariableDeclaration {
|
VariableDeclaration {
|
||||||
iden: String,
|
iden: Iden,
|
||||||
init: Option<Box<Expr>>,
|
init: Option<Box<Item>>,
|
||||||
},
|
},
|
||||||
FunctionDeclaration {
|
FunctionDeclaration {
|
||||||
iden: String,
|
iden: Iden,
|
||||||
body: Vec<Expr>,
|
args: Vec<Iden>,
|
||||||
|
body: Vec<Item>,
|
||||||
},
|
},
|
||||||
BfFDeclaration {
|
BfFDeclaration {
|
||||||
iden: String,
|
iden: Iden,
|
||||||
body: String,
|
body: String,
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
cond: Box<Expr>,
|
cond: Box<Item>,
|
||||||
body: Vec<Expr>,
|
body: Vec<Item>,
|
||||||
},
|
},
|
||||||
|
FunctionCall {
|
||||||
Literal(Value),
|
iden: Iden,
|
||||||
|
args: Vec<Expr>,
|
||||||
|
},
|
||||||
|
Loop {
|
||||||
|
body: Vec<Item>,
|
||||||
|
},
|
||||||
|
Break,
|
||||||
|
HopBack,
|
||||||
|
Print(Expr),
|
||||||
Melo(Iden),
|
Melo(Iden),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,56 @@
|
||||||
mod item;
|
mod item;
|
||||||
|
mod ops;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use item::Expr;
|
use item::Item;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
|
lexer::PeekableLexer,
|
||||||
|
parser::item::{Expr, Stmt},
|
||||||
variables::Value,
|
variables::Value,
|
||||||
};
|
};
|
||||||
use crate::{parser::item::Iden, tokens::Token};
|
use crate::{lexer::Token, parser::item::Iden};
|
||||||
|
|
||||||
use logos::Logos;
|
pub type ParseResult = Result<Item, Error>;
|
||||||
|
|
||||||
/// Parser structure / state machine
|
/// Parser structure / state machine
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
lexer: logos::Lexer<'a, Token>,
|
lexer: PeekableLexer<'a>,
|
||||||
ast: Vec<Expr>,
|
ast: Vec<Item>,
|
||||||
|
tdark: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Create a new parser object
|
/// Create a new parser object
|
||||||
pub fn new(source: &'a str) -> Self {
|
pub fn new(source: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
lexer: Token::lexer(source),
|
lexer: PeekableLexer::lexer(source),
|
||||||
ast: Vec::new(),
|
ast: Vec::new(),
|
||||||
|
tdark: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<Vec<Expr>, Error> {
|
pub fn init(&mut self) -> Result<Vec<Item>, Error> {
|
||||||
loop {
|
loop {
|
||||||
let token = self.lexer.next();
|
let token = self.lexer.next();
|
||||||
if token.is_none() {
|
|
||||||
return Ok(self.ast.clone());
|
|
||||||
};
|
|
||||||
|
|
||||||
let expr = self.parse_expr(token)?;
|
match token {
|
||||||
self.ast.push(expr);
|
Some(Token::Comment) => continue,
|
||||||
|
Some(Token::TDark) => {
|
||||||
|
let mut block = self.tdark_block()?;
|
||||||
|
self.ast.append(&mut block);
|
||||||
|
}
|
||||||
|
None => return Ok(self.ast.clone()),
|
||||||
|
_ => {
|
||||||
|
let item = self.parse_item(token)?;
|
||||||
|
self.ast.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(&mut self, token: Option<Token>) -> Result<Expr, Error> {
|
fn parse_item(&mut self, token: Option<Token>) -> ParseResult {
|
||||||
if matches!(token, None) {
|
if matches!(token, None) {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
kind: ErrorKind::EndOfTokenStream,
|
kind: ErrorKind::EndOfTokenStream,
|
||||||
|
@ -50,26 +62,38 @@ impl<'a> Parser<'a> {
|
||||||
let start = self.lexer.span().start;
|
let start = self.lexer.span().start;
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
|
Token::Identifier(_)
|
||||||
|
| Token::Aboolean(_)
|
||||||
|
| Token::Boolean(_)
|
||||||
|
| Token::Integer(_)
|
||||||
|
| Token::String(_)
|
||||||
|
| Token::Nul
|
||||||
|
| Token::LogNot => self.parse_ops(token),
|
||||||
|
|
||||||
// Control flow
|
// Control flow
|
||||||
Token::If => self.if_cond(),
|
Token::If => self.if_cond(),
|
||||||
|
Token::Loop => self.loop_block(),
|
||||||
|
|
||||||
|
Token::HopBack => {
|
||||||
|
self.require(Token::Semicolon)?;
|
||||||
|
Ok(Stmt::HopBack.into())
|
||||||
|
}
|
||||||
|
Token::Break => {
|
||||||
|
self.require(Token::Semicolon)?;
|
||||||
|
Ok(Stmt::Break.into())
|
||||||
|
}
|
||||||
|
|
||||||
// Declarations
|
// Declarations
|
||||||
Token::Variable => self.variable_declaration(),
|
Token::Variable => self.variable_declaration(),
|
||||||
Token::Function => self.function_declaration(),
|
Token::Function => self.function_declaration(),
|
||||||
Token::BfFunction => self.bff_declaration(),
|
Token::BfFunction => self.bff_declaration(),
|
||||||
|
|
||||||
// Literals
|
|
||||||
Token::String(x) => Ok(Expr::Literal(Value::Str(x))),
|
|
||||||
Token::Integer(x) => Ok(Expr::Literal(Value::Int(x))),
|
|
||||||
Token::Boolean(x) => Ok(Expr::Literal(Value::Bool(x))),
|
|
||||||
Token::Aboolean(x) => Ok(Expr::Literal(Value::Abool(x))),
|
|
||||||
|
|
||||||
// Prefix keywords
|
// Prefix keywords
|
||||||
// Melo - ban variable from next usage (runtime error)
|
// Melo - ban variable from next usage (runtime error)
|
||||||
Token::Melo => {
|
Token::Melo => {
|
||||||
let e = self.require_iden()?;
|
let e = self.require_iden()?;
|
||||||
self.require(Token::Semicolon)?;
|
self.require(Token::Semicolon)?;
|
||||||
Ok(Expr::Melo(Iden(e)))
|
Ok(Stmt::Melo(e).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(Error {
|
_ => Err(Error {
|
||||||
|
@ -82,14 +106,14 @@ impl<'a> Parser<'a> {
|
||||||
/// Parse variable declaration
|
/// Parse variable declaration
|
||||||
///
|
///
|
||||||
/// `var [iden] = [literal];`
|
/// `var [iden] = [literal];`
|
||||||
fn variable_declaration(&mut self) -> Result<Expr, Error> {
|
fn variable_declaration(&mut self) -> ParseResult {
|
||||||
let iden = self.require_iden()?;
|
let iden = self.require_iden()?;
|
||||||
|
|
||||||
let init = match self.lexer.next() {
|
let init = match self.lexer.next() {
|
||||||
Some(Token::Semicolon) => None,
|
Some(Token::Semicolon) => None,
|
||||||
Some(Token::Assignment) => {
|
Some(Token::Assignment) => {
|
||||||
let value = self.lexer.next();
|
let value = self.lexer.next();
|
||||||
let value = self.parse_expr(value)?;
|
let value = self.parse_item(value)?;
|
||||||
self.require(Token::Semicolon)?;
|
self.require(Token::Semicolon)?;
|
||||||
Some(Box::new(value))
|
Some(Box::new(value))
|
||||||
}
|
}
|
||||||
|
@ -101,28 +125,35 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Expr::VariableDeclaration { iden, init })
|
Ok(Stmt::VariableDeclaration { iden, init }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Declare function
|
/// Declare function
|
||||||
///
|
///
|
||||||
/// `functio [iden] ([expr], [expr]) { ... }
|
/// `functio [iden] ([expr], [expr]) { ... }
|
||||||
fn function_declaration(&mut self) -> Result<Expr, Error> {
|
fn function_declaration(&mut self) -> ParseResult {
|
||||||
let iden = self.require_iden()?;
|
let iden = self.require_iden()?;
|
||||||
self.require(Token::LeftParenthesis)?;
|
self.require(Token::LeftParenthesis)?;
|
||||||
self.require(Token::RightParenthesis)?;
|
let mut args = vec![];
|
||||||
|
loop {
|
||||||
|
let next = self.lexer.next();
|
||||||
|
match next {
|
||||||
|
Some(Token::RightParenthesis) => break,
|
||||||
|
Some(Token::Identifier(i)) => args.push(Iden(i)),
|
||||||
|
_ => return Err(self.unexpected_token(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
self.require(Token::LeftBrace)?;
|
self.require(Token::LeftBrace)?;
|
||||||
// Parse function body
|
// Parse function body
|
||||||
let body = self.parse_body()?;
|
let body = self.parse_body()?;
|
||||||
|
|
||||||
Ok(Expr::FunctionDeclaration { iden, body })
|
Ok(Stmt::FunctionDeclaration { iden, args, body }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Declare BF FFI Function
|
/// Declare BF FFI Function
|
||||||
///
|
///
|
||||||
/// `bff [iden] { ... }`
|
/// `bff [iden] { ... }`
|
||||||
fn bff_declaration(&mut self) -> Result<Expr, Error> {
|
fn bff_declaration(&mut self) -> ParseResult {
|
||||||
let iden = self.require_iden()?;
|
let iden = self.require_iden()?;
|
||||||
self.require(Token::LeftBrace)?;
|
self.require(Token::LeftBrace)?;
|
||||||
|
|
||||||
|
@ -140,9 +171,6 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if token == Token::RightBrace {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
body.push_str(match token {
|
body.push_str(match token {
|
||||||
Token::OpGt
|
Token::OpGt
|
||||||
| Token::OpLt
|
| Token::OpLt
|
||||||
|
@ -156,23 +184,118 @@ impl<'a> Parser<'a> {
|
||||||
_ => return Err(self.unexpected_token(None)),
|
_ => return Err(self.unexpected_token(None)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(Expr::BfFDeclaration { iden, body })
|
Ok(Stmt::BfFDeclaration { iden, body }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse If-expression
|
/// Parse If-stmt
|
||||||
pub fn if_cond(&mut self) -> Result<Expr, Error> {
|
pub fn if_cond(&mut self) -> ParseResult {
|
||||||
self.require(Token::LeftParenthesis)?;
|
self.require(Token::LeftParenthesis)?;
|
||||||
let cond = self.lexer.next();
|
let cond = self.lexer.next();
|
||||||
let cond = self.parse_expr(cond)?;
|
let cond = self.parse_item(cond)?;
|
||||||
self.require(Token::RightParenthesis)?;
|
self.require(Token::RightParenthesis)?;
|
||||||
|
|
||||||
self.require(Token::LeftBrace)?;
|
self.require(Token::LeftBrace)?;
|
||||||
|
|
||||||
let body = self.parse_body()?;
|
let body = self.parse_body()?;
|
||||||
|
|
||||||
Ok(Expr::If {
|
Ok(Stmt::If {
|
||||||
cond: Box::new(cond),
|
cond: Box::new(cond),
|
||||||
body,
|
body,
|
||||||
})
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse loop
|
||||||
|
pub fn loop_block(&mut self) -> ParseResult {
|
||||||
|
self.require(Token::LeftBrace)?;
|
||||||
|
let body = self.parse_body()?;
|
||||||
|
|
||||||
|
Ok(Stmt::Loop { body }.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// T-Dark block parsing
|
||||||
|
pub fn tdark_block(&mut self) -> Result<Vec<Item>, Error> {
|
||||||
|
self.require(Token::LeftBrace)?;
|
||||||
|
self.tdark = true;
|
||||||
|
let mut body = Vec::new();
|
||||||
|
loop {
|
||||||
|
let token = {
|
||||||
|
match self.lexer.next() {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::EndOfTokenStream,
|
||||||
|
position: self.lexer.span(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if token == Token::RightBrace {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
body.push(self.parse_item(Some(token))?);
|
||||||
|
}
|
||||||
|
self.tdark = false;
|
||||||
|
Ok(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use Expr::*;
|
||||||
|
use Stmt::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn control_flow() {
|
||||||
|
let code = r#"loop { var a = 3 + 2; if (a == 5) { break; } }"#;
|
||||||
|
|
||||||
|
let expected: &[Item] = &[Item::Stmt(Loop {
|
||||||
|
body: vec![
|
||||||
|
VariableDeclaration {
|
||||||
|
iden: Iden("a".to_owned()),
|
||||||
|
init: Some(Box::new(
|
||||||
|
Add {
|
||||||
|
left: Box::new(Literal(Value::Int(3))),
|
||||||
|
right: Box::new(Literal(Value::Int(2))),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
If {
|
||||||
|
cond: Box::new(
|
||||||
|
Eq {
|
||||||
|
left: Box::new(Iden("a".to_owned()).into()),
|
||||||
|
right: Box::new(Literal(Value::Int(5)).into()),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
body: vec![Break.into()],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
],
|
||||||
|
})];
|
||||||
|
let ast = Parser::new(code).init().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ast, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tdark() {
|
||||||
|
let code = r#"T-Dark { var lang = nul; lang print; }"#;
|
||||||
|
let expected: &[Item] = &[
|
||||||
|
VariableDeclaration {
|
||||||
|
iden: Iden("script".to_owned()),
|
||||||
|
init: Some(Box::new(Literal(Value::Nul).into())),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
Print(Iden("script".to_owned()).into()).into(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let ast = Parser::new(code).init().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ast, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
159
src/parser/ops.rs
Normal file
159
src/parser/ops.rs
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
type ExprResult = Result<Expr, Error>;
|
||||||
|
|
||||||
|
/// Generate infix expression by pattern left <op> right
|
||||||
|
///
|
||||||
|
/// Credits: `@! ! Reiter#4543`
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! gen_infix {
|
||||||
|
($($fn_name: ident => $type: tt);*$(;)?) => {$(
|
||||||
|
/// Generated function for infix operator
|
||||||
|
fn $fn_name(&mut self, left: Expr) -> ExprResult {
|
||||||
|
self.lexer.next();
|
||||||
|
let next = self.lexer.next();
|
||||||
|
let right = self.parse_expr(next)?;
|
||||||
|
Ok(Expr::$type { left: Box::new(left), right: Box::new(right) })
|
||||||
|
})*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
pub(super) fn parse_ops(&mut self, token: Token) -> ParseResult {
|
||||||
|
if matches!(self.lexer.peek(), Some(Token::LeftParenthesis)) {
|
||||||
|
return self.fn_call(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf: Expr = self.parse_expr(Some(token))?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
buf = match self.lexer.peek() {
|
||||||
|
Some(Token::Addition) => self.addition(buf)?,
|
||||||
|
Some(Token::Subtract) => self.subtract(buf)?,
|
||||||
|
Some(Token::Multiply) => self.multiply(buf)?,
|
||||||
|
Some(Token::Divide) => self.divide(buf)?,
|
||||||
|
Some(Token::OpLt) => self.cmplt(buf)?,
|
||||||
|
Some(Token::OpGt) => self.cmpgt(buf)?,
|
||||||
|
Some(Token::OpEq) => self.cmpeq(buf)?,
|
||||||
|
Some(Token::OpNeq) => self.cmpneq(buf)?,
|
||||||
|
Some(Token::LogAnd) => self.logand(buf)?,
|
||||||
|
Some(Token::LogOr) => self.logor(buf)?,
|
||||||
|
Some(Token::Print) => {
|
||||||
|
self.lexer.next();
|
||||||
|
self.require(Token::Semicolon)?;
|
||||||
|
return Ok(Stmt::Print(buf).into());
|
||||||
|
}
|
||||||
|
_ => return Ok(buf.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate infix
|
||||||
|
gen_infix! {
|
||||||
|
addition => Add;
|
||||||
|
subtract => Subtract;
|
||||||
|
multiply => Multiply;
|
||||||
|
divide => Divide;
|
||||||
|
cmplt => Lt;
|
||||||
|
cmpgt => Gt;
|
||||||
|
cmpeq => Eq;
|
||||||
|
cmpneq => Neq;
|
||||||
|
logand => And;
|
||||||
|
logor => Or;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensure that input token is an expression
|
||||||
|
pub(super) fn parse_expr(&mut self, token: Option<Token>) -> ExprResult {
|
||||||
|
let token = token.ok_or(Error {
|
||||||
|
kind: ErrorKind::EndOfTokenStream,
|
||||||
|
position: self.lexer.span(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match token {
|
||||||
|
Token::Boolean(b) => Ok(Expr::Literal(Value::Bool(b))),
|
||||||
|
Token::Integer(i) => Ok(Expr::Literal(Value::Int(i))),
|
||||||
|
Token::String(s) => Ok(Expr::Literal(Value::Str(if self.tdark {
|
||||||
|
s.replace("lang", "script")
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}))),
|
||||||
|
Token::Aboolean(a) => Ok(Expr::Literal(Value::Abool(a))),
|
||||||
|
Token::Identifier(i) => Ok(Expr::Identifier(Iden(if self.tdark {
|
||||||
|
i.replace("lang", "script")
|
||||||
|
} else {
|
||||||
|
i
|
||||||
|
}))),
|
||||||
|
Token::Nul => Ok(Expr::Literal(Value::Nul)),
|
||||||
|
Token::LogNot => {
|
||||||
|
let next = self.lexer.next();
|
||||||
|
Ok(Expr::Not(Box::new(self.parse_expr(next)?)))
|
||||||
|
}
|
||||||
|
Token::LeftParenthesis => self.parse_paren(),
|
||||||
|
t => Err(self.unexpected_token(Some(t))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse parenthesieted expression
|
||||||
|
fn parse_paren(&mut self) -> ExprResult {
|
||||||
|
let next = self.lexer.next();
|
||||||
|
let mut buf = self.parse_expr(next)?;
|
||||||
|
loop {
|
||||||
|
let next = self.lexer.peek().clone().ok_or(Error {
|
||||||
|
kind: ErrorKind::EndOfTokenStream,
|
||||||
|
position: self.lexer.span(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
buf = match Some(next) {
|
||||||
|
Some(Token::Addition) => self.addition(buf)?,
|
||||||
|
Some(Token::Subtract) => self.subtract(buf)?,
|
||||||
|
Some(Token::Multiply) => self.multiply(buf)?,
|
||||||
|
Some(Token::Divide) => self.divide(buf)?,
|
||||||
|
Some(Token::LeftParenthesis) => {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::SyntaxError(
|
||||||
|
"Function call isn't an expression!".to_owned(),
|
||||||
|
),
|
||||||
|
position: self.lexer.span(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some(Token::RightParenthesis) => {
|
||||||
|
self.lexer.next();
|
||||||
|
return Ok(buf);
|
||||||
|
}
|
||||||
|
_ => return Ok(buf),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse function call
|
||||||
|
fn fn_call(&mut self, token: Token) -> ParseResult {
|
||||||
|
let iden = if let Token::Identifier(i) = token {
|
||||||
|
Iden(i)
|
||||||
|
} else {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::InvalidIdentifier,
|
||||||
|
position: self.lexer.span(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.lexer.next();
|
||||||
|
let mut args = Vec::new();
|
||||||
|
loop {
|
||||||
|
let next = self.lexer.next();
|
||||||
|
|
||||||
|
// No argument function
|
||||||
|
if matches!(next, Some(Token::RightParenthesis)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push(self.parse_expr(next)?);
|
||||||
|
match self.lexer.next() {
|
||||||
|
Some(Token::RightParenthesis) => break,
|
||||||
|
Some(Token::Comma) => continue,
|
||||||
|
_ => return Err(self.unexpected_token(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.require(Token::Semicolon)?;
|
||||||
|
Ok(Stmt::FunctionCall { iden, args }.into())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
use crate::error::{Error, ErrorKind};
|
use crate::error::{Error, ErrorKind};
|
||||||
use crate::tokens::Token;
|
use crate::lexer::Token;
|
||||||
use crate::variables::Abool;
|
use crate::variables::Abool;
|
||||||
|
|
||||||
use super::{item::Expr, Parser};
|
use super::{
|
||||||
|
item::{Iden, Item},
|
||||||
|
Parser,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn abool2num(abool: Abool) -> i32 {
|
pub fn abool2num(abool: Abool) -> i32 {
|
||||||
match abool {
|
match abool {
|
||||||
|
@ -30,9 +33,14 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn require_iden(&mut self) -> Result<String, Error> {
|
/// Require an identifier on next and return it
|
||||||
|
pub(super) fn require_iden(&mut self) -> Result<Iden, Error> {
|
||||||
if let Some(Token::Identifier(id)) = self.lexer.next() {
|
if let Some(Token::Identifier(id)) = self.lexer.next() {
|
||||||
Ok(id)
|
if self.tdark {
|
||||||
|
Ok(Iden(id.replace("lang", "script")))
|
||||||
|
} else {
|
||||||
|
Ok(Iden(id))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::InvalidIdentifier,
|
kind: ErrorKind::InvalidIdentifier,
|
||||||
|
@ -41,18 +49,23 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Throw unexpected token error (optionally what was expected)
|
||||||
pub(super) fn unexpected_token(&mut self, expected: Option<Token>) -> Error {
|
pub(super) fn unexpected_token(&mut self, expected: Option<Token>) -> Error {
|
||||||
Error {
|
let error_msg = match expected {
|
||||||
kind: ErrorKind::SyntaxError(format!(
|
Some(s) => format!(
|
||||||
"Unexpected token: `{}` (required: `{:?}`)",
|
"Unexpected token: `{}` (required: `{:?}`)",
|
||||||
self.lexer.slice(),
|
self.lexer.slice(),
|
||||||
expected
|
s
|
||||||
)),
|
),
|
||||||
|
None => format!("Unexpected token: `{}`", self.lexer.slice(),),
|
||||||
|
};
|
||||||
|
Error {
|
||||||
|
kind: ErrorKind::SyntaxError(error_msg),
|
||||||
position: self.lexer.span(),
|
position: self.lexer.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_body(&mut self) -> Result<Vec<Expr>, Error> {
|
pub(super) fn parse_body(&mut self) -> Result<Vec<Item>, Error> {
|
||||||
let mut body = Vec::new();
|
let mut body = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let token = {
|
let token = {
|
||||||
|
@ -70,7 +83,7 @@ impl<'a> Parser<'a> {
|
||||||
if token == Token::RightBrace {
|
if token == Token::RightBrace {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
body.push(self.parse_expr(Some(token))?);
|
body.push(self.parse_item(Some(token))?);
|
||||||
}
|
}
|
||||||
Ok(body)
|
Ok(body)
|
||||||
}
|
}
|
||||||
|
|
41
src/repl.rs
Normal file
41
src/repl.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use logos::Source;
|
||||||
|
use rustyline::Editor;
|
||||||
|
|
||||||
|
use crate::parser::Parser;
|
||||||
|
|
||||||
|
pub fn repl() {
|
||||||
|
let mut rl = Editor::<()>::new();
|
||||||
|
loop {
|
||||||
|
let readline = rl.readline(":: ");
|
||||||
|
match readline {
|
||||||
|
Ok(line) => {
|
||||||
|
if &line == "exit" {
|
||||||
|
println!("bye");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut parser = Parser::new(&line);
|
||||||
|
let ast = parser.init();
|
||||||
|
match ast {
|
||||||
|
Ok(ast) => println!("{:?}", ast),
|
||||||
|
Err(e) => {
|
||||||
|
println!(
|
||||||
|
"Error `{:?}` occured at span: {:?} = `{:?}`",
|
||||||
|
e.kind,
|
||||||
|
e.position.clone(),
|
||||||
|
line.slice(e.position)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(rustyline::error::ReadlineError::Eof) => {
|
||||||
|
println!("bye");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(rustyline::error::ReadlineError::Interrupted) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,9 @@ pub enum Abool {
|
||||||
Always = 1,
|
Always = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<bool> for Abool {
|
impl From<Abool> for bool {
|
||||||
fn into(self) -> bool {
|
fn from(val: Abool) -> Self {
|
||||||
match self {
|
match val {
|
||||||
Abool::Never => false,
|
Abool::Never => false,
|
||||||
Abool::Always => true,
|
Abool::Always => true,
|
||||||
Abool::Sometimes => rand::thread_rng().gen(),
|
Abool::Sometimes => rand::thread_rng().gen(),
|
||||||
|
@ -17,12 +17,13 @@ impl Into<bool> for Abool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Str(String),
|
Str(String),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Abool(Abool),
|
Abool(Abool),
|
||||||
|
Nul,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
Loading…
Reference in a new issue