From e45afeac5efafdb58fbab825afac99b8334ca1af Mon Sep 17 00:00:00 2001 From: Erin Date: Wed, 28 Apr 2021 22:52:19 +0200 Subject: [PATCH 01/10] Added Peekable Lexer - Added wrapper for Lexer with `peek()` method - Renamed `token` module to `lexer` as it more describe it's function - Started work on operator flow --- able-script-test/parse_test.able | 4 +-- src/{tokens.rs => lexer.rs} | 57 +++++++++++++++++++++++++++++++- src/main.rs | 2 +- src/parser/item.rs | 7 ++++ src/parser/mod.rs | 14 ++++---- src/parser/ops.rs | 30 +++++++++++++++++ src/parser/utils.rs | 2 +- 7 files changed, 104 insertions(+), 12 deletions(-) rename src/{tokens.rs => lexer.rs} (68%) create mode 100644 src/parser/ops.rs diff --git a/able-script-test/parse_test.able b/able-script-test/parse_test.able index 6d7ceca..f3c8d37 100644 --- a/able-script-test/parse_test.able +++ b/able-script-test/parse_test.able @@ -1,3 +1 @@ -if (true) { - var a = 3; -} \ No newline at end of file +a() \ No newline at end of file diff --git a/src/tokens.rs b/src/lexer.rs similarity index 68% rename from src/tokens.rs rename to src/lexer.rs index b21c4d5..dbe33a8 100644 --- a/src/tokens.rs +++ b/src/lexer.rs @@ -1,7 +1,62 @@ -use logos::{Lexer, Logos}; +use logos::{Lexer, Logos, Span}; use crate::variables::Abool; +pub struct PeekableLexer<'source> { + lexer: Lexer<'source, Token>, + peeked: Option>, +} + +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 { + 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.lexer.next() + } +} + #[derive(Logos, Debug, PartialEq, Clone)] pub enum Token { #[token("functio")] diff --git a/src/main.rs b/src/main.rs index 32d3d3c..bd6b72a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,8 @@ mod base_55; mod error; +mod lexer; mod parser; -mod tokens; mod variables; use clap::{App, Arg}; diff --git a/src/parser/item.rs b/src/parser/item.rs index 46616dc..63dafa0 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::variables::Value; #[derive(Debug, Clone)] @@ -11,6 +13,7 @@ pub enum Expr { }, FunctionDeclaration { iden: String, + args: Vec, body: Vec, }, BfFDeclaration { @@ -22,6 +25,10 @@ pub enum Expr { body: Vec, }, + FunctionCall { + iden: Iden, + args: HashMap, + }, Literal(Value), Melo(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 36a44c0..2461252 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,19 +1,19 @@ mod item; +mod ops; mod utils; use item::Expr; use crate::{ error::{Error, ErrorKind}, + lexer::PeekableLexer, variables::Value, }; -use crate::{parser::item::Iden, tokens::Token}; - -use logos::Logos; +use crate::{lexer::Token, parser::item::Iden}; /// Parser structure / state machine pub struct Parser<'a> { - lexer: logos::Lexer<'a, Token>, + lexer: PeekableLexer<'a>, ast: Vec, } @@ -21,7 +21,7 @@ impl<'a> Parser<'a> { /// Create a new parser object pub fn new(source: &'a str) -> Self { Self { - lexer: Token::lexer(source), + lexer: PeekableLexer::lexer(source), ast: Vec::new(), } } @@ -50,6 +50,7 @@ impl<'a> Parser<'a> { let start = self.lexer.span().start; match token { + Token::Identifier(_) => self.parse_ops(token), // Control flow Token::If => self.if_cond(), @@ -110,13 +111,14 @@ impl<'a> Parser<'a> { fn function_declaration(&mut self) -> Result { let iden = self.require_iden()?; self.require(Token::LeftParenthesis)?; + let args = vec![]; self.require(Token::RightParenthesis)?; self.require(Token::LeftBrace)?; // Parse function body let body = self.parse_body()?; - Ok(Expr::FunctionDeclaration { iden, body }) + Ok(Expr::FunctionDeclaration { iden, args, body }) } /// Declare BF FFI Function diff --git a/src/parser/ops.rs b/src/parser/ops.rs new file mode 100644 index 0000000..2b76e68 --- /dev/null +++ b/src/parser/ops.rs @@ -0,0 +1,30 @@ +use std::collections::HashMap; + +use super::*; + +impl<'a> Parser<'a> { + pub(super) fn parse_ops(&mut self, token: Token) -> Result { + let iden = if let Token::Identifier(i) = token { + Iden(i) + } else { + unimplemented!() + }; + + let mut buf = Vec::new(); + + buf.push(match self.lexer.peek() { + Some(Token::LeftParenthesis) => self.fn_call(iden)?, + _ => unimplemented!(), + }); + + Ok(buf[0].clone()) + } + + fn fn_call(&mut self, iden: Iden) -> Result { + self.require(Token::RightParenthesis)?; + Ok(Expr::FunctionCall { + iden, + args: HashMap::new(), + }) + } +} diff --git a/src/parser/utils.rs b/src/parser/utils.rs index 42c2b63..49c8b34 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -1,5 +1,5 @@ use crate::error::{Error, ErrorKind}; -use crate::tokens::Token; +use crate::lexer::Token; use crate::variables::Abool; use super::{item::Expr, Parser}; From 17a32a8df79586f7e48d05f7329bfe424060a59f Mon Sep 17 00:00:00 2001 From: Erin Date: Thu, 29 Apr 2021 09:47:29 +0200 Subject: [PATCH 02/10] Bugfix: Peeking - Fixed `PeekableLexer` next to not-be passtrough to iterator - Made error handling depend on state of Option --- src/lexer.rs | 5 ++++- src/parser/ops.rs | 1 + src/parser/utils.rs | 12 ++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lexer.rs b/src/lexer.rs index dbe33a8..f3038e4 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -53,7 +53,10 @@ impl<'source> Iterator for PeekableLexer<'source> { /// again may or may not eventually start returning [`Some(Item)`] again at some point. #[inline] fn next(&mut self) -> Option { - self.lexer.next() + match self.peeked.take() { + Some(v) => v, + None => self.lexer.next(), + } } } diff --git a/src/parser/ops.rs b/src/parser/ops.rs index 2b76e68..fb22c30 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -21,6 +21,7 @@ impl<'a> Parser<'a> { } fn fn_call(&mut self, iden: Iden) -> Result { + self.lexer.next(); self.require(Token::RightParenthesis)?; Ok(Expr::FunctionCall { iden, diff --git a/src/parser/utils.rs b/src/parser/utils.rs index 49c8b34..eddfbea 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -42,12 +42,16 @@ impl<'a> Parser<'a> { } pub(super) fn unexpected_token(&mut self, expected: Option) -> Error { - Error { - kind: ErrorKind::SyntaxError(format!( + let error_msg = match expected { + Some(s) => format!( "Unexpected token: `{}` (required: `{:?}`)", self.lexer.slice(), - expected - )), + s + ), + None => format!("Unexpected token: `{}`)", self.lexer.slice(),), + }; + Error { + kind: ErrorKind::SyntaxError(error_msg), position: self.lexer.span(), } } From ca60f818eb79df626a063e9ef581221604780e03 Mon Sep 17 00:00:00 2001 From: Erin Date: Thu, 29 Apr 2021 18:50:51 +0200 Subject: [PATCH 03/10] Implemented function calls --- able-script-test/parse_test.able | 2 +- src/parser/item.rs | 11 +++++++++-- src/parser/mod.rs | 3 --- src/parser/ops.rs | 17 ++++++++++++++++- src/variables.rs | 1 + 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/able-script-test/parse_test.able b/able-script-test/parse_test.able index f3c8d37..4848030 100644 --- a/able-script-test/parse_test.able +++ b/able-script-test/parse_test.able @@ -1 +1 @@ -a() \ No newline at end of file +a(3,) \ No newline at end of file diff --git a/src/parser/item.rs b/src/parser/item.rs index 63dafa0..eabea92 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -2,9 +2,15 @@ use std::collections::HashMap; use crate::variables::Value; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Iden(pub String); +impl From for Expr { + fn from(iden: Iden) -> Self { + Self::Identifier(iden) + } +} + #[derive(Debug, Clone)] pub enum Expr { VariableDeclaration { @@ -27,8 +33,9 @@ pub enum Expr { FunctionCall { iden: Iden, - args: HashMap, + args: Vec, }, Literal(Value), + Identifier(Iden), Melo(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 2461252..b5a3c4f 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -142,9 +142,6 @@ impl<'a> Parser<'a> { } }; - if token == Token::RightBrace { - break; - } body.push_str(match token { Token::OpGt | Token::OpLt diff --git a/src/parser/ops.rs b/src/parser/ops.rs index fb22c30..0e8bb5d 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -22,10 +22,25 @@ impl<'a> Parser<'a> { fn fn_call(&mut self, iden: Iden) -> Result { self.lexer.next(); + let mut args: Vec = Vec::new(); + while let Some(token) = self.lexer.peek() { + match token { + Token::Identifier(id) => { + args.push(Iden(id.clone()).into()); + self.lexer.next(); + } + Token::RightParenthesis => break, + _ => { + let next = self.lexer.next(); + args.push(self.parse_expr(next)?) + } + } + self.require(Token::Comma)?; + } self.require(Token::RightParenthesis)?; Ok(Expr::FunctionCall { iden, - args: HashMap::new(), + args, }) } } diff --git a/src/variables.rs b/src/variables.rs index cb476b9..fd4a57c 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -23,6 +23,7 @@ pub enum Value { Int(i32), Bool(bool), Abool(Abool), + Nul, } #[derive(Debug)] From eef7ec16fa99d2703376b76d8bd55379a2e1ff10 Mon Sep 17 00:00:00 2001 From: Erin Date: Thu, 29 Apr 2021 19:19:35 +0200 Subject: [PATCH 04/10] Tidy up --- able-script-test/parse_test.able | 2 +- src/parser/item.rs | 6 ++++++ src/parser/ops.rs | 10 ++++------ src/parser/utils.rs | 2 ++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/able-script-test/parse_test.able b/able-script-test/parse_test.able index 4848030..4ac0e3e 100644 --- a/able-script-test/parse_test.able +++ b/able-script-test/parse_test.able @@ -1 +1 @@ -a(3,) \ No newline at end of file +1 + 4 \ No newline at end of file diff --git a/src/parser/item.rs b/src/parser/item.rs index eabea92..34d0b48 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -35,6 +35,12 @@ pub enum Expr { iden: Iden, args: Vec, }, + + Addition { + left: Box, + right: Box, + }, + Literal(Value), Identifier(Iden), Melo(Iden), diff --git a/src/parser/ops.rs b/src/parser/ops.rs index 0e8bb5d..04ebde3 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -1,8 +1,7 @@ -use std::collections::HashMap; - use super::*; impl<'a> Parser<'a> { + /// Parse operations (got identifier/value) pub(super) fn parse_ops(&mut self, token: Token) -> Result { let iden = if let Token::Identifier(i) = token { Iden(i) @@ -20,9 +19,11 @@ impl<'a> Parser<'a> { Ok(buf[0].clone()) } + /// Parse function call fn fn_call(&mut self, iden: Iden) -> Result { self.lexer.next(); let mut args: Vec = Vec::new(); + while let Some(token) = self.lexer.peek() { match token { Token::Identifier(id) => { @@ -38,9 +39,6 @@ impl<'a> Parser<'a> { self.require(Token::Comma)?; } self.require(Token::RightParenthesis)?; - Ok(Expr::FunctionCall { - iden, - args, - }) + Ok(Expr::FunctionCall { iden, args }) } } diff --git a/src/parser/utils.rs b/src/parser/utils.rs index eddfbea..2b83f1a 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -30,6 +30,7 @@ impl<'a> Parser<'a> { } } + /// Require an identifier on next and return it pub(super) fn require_iden(&mut self) -> Result { if let Some(Token::Identifier(id)) = self.lexer.next() { Ok(id) @@ -41,6 +42,7 @@ impl<'a> Parser<'a> { } } + /// Throw unexpected token error (optionally what was expected) pub(super) fn unexpected_token(&mut self, expected: Option) -> Error { let error_msg = match expected { Some(s) => format!( From 3794fd3c8fbc0278eb6bcdf73bba078dc3204d57 Mon Sep 17 00:00:00 2001 From: Erin Date: Sat, 1 May 2021 13:44:58 +0200 Subject: [PATCH 05/10] Divided token types - Item = Expr | Stmt - Added REPL (AST dump) - Removed operator parsing because it was horrible and I need to redo it --- Cargo.lock | 156 +++++++++++++++++++++++++++++++ Cargo.toml | 3 +- able-script-test/parse_test.able | 1 - src/main.rs | 7 +- src/parser/item.rs | 62 +++++++----- src/parser/mod.rs | 48 +++++----- src/parser/ops.rs | 24 ++--- src/parser/utils.rs | 12 +-- src/repl.rs | 30 ++++++ 9 files changed, 268 insertions(+), 75 deletions(-) delete mode 100644 able-script-test/parse_test.able create mode 100644 src/repl.rs diff --git a/Cargo.lock b/Cargo.lock index 05e63be..3594179 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,7 @@ dependencies = [ "clap", "logos", "rand", + "rustyline", ] [[package]] @@ -43,6 +44,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + [[package]] name = "cfg-if" version = "1.0.0" @@ -64,12 +71,49 @@ dependencies = [ "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]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "getrandom" version = "0.2.2" @@ -96,6 +140,15 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "logos" version = "0.12.0" @@ -120,6 +173,33 @@ dependencies = [ "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]] name = "ppv-lite86" version = "0.2.10" @@ -144,6 +224,16 @@ dependencies = [ "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]] name = "rand" version = "0.8.3" @@ -184,12 +274,66 @@ dependencies = [ "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]] name = "regex-syntax" version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "strsim" version = "0.8.0" @@ -216,6 +360,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + [[package]] name = "unicode-width" version = "0.1.8" @@ -234,6 +384,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index 9f8f743..15fa072 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ edition = "2018" [dependencies] clap="*" logos = "0.12" -rand = "*" \ No newline at end of file +rand = "*" +rustyline = "8.0.0" \ No newline at end of file diff --git a/able-script-test/parse_test.able b/able-script-test/parse_test.able deleted file mode 100644 index 4ac0e3e..0000000 --- a/able-script-test/parse_test.able +++ /dev/null @@ -1 +0,0 @@ -1 + 4 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index bd6b72a..eb43c6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod base_55; mod error; mod lexer; mod parser; +mod repl; mod variables; use clap::{App, Arg}; @@ -14,7 +15,7 @@ fn main() { let matches = App::new("AbleScript") .version(env!("CARGO_PKG_VERSION")) .author("Able ") - .about("Does awesome things") + .about("AbleScript interpreter") .arg( Arg::with_name("file") .short("f") @@ -36,8 +37,8 @@ fn main() { println!("{:#?}", ast); } None => { - println!("hi"); - //start the prompt + println!("Hi [AbleScript {}] - AST Printer", env!("CARGO_PKG_VERSION")); + repl::repl(); } } } diff --git a/src/parser/item.rs b/src/parser/item.rs index 34d0b48..ea556a5 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -1,47 +1,61 @@ -use std::collections::HashMap; - use crate::variables::Value; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Iden(pub String); -impl From for Expr { - fn from(iden: Iden) -> Self { - Self::Identifier(iden) +#[derive(Debug, Clone)] +pub enum Item { + Expr(Expr), + Stmt(Stmt), +} + +impl From for Item { + fn from(e: Expr) -> Self { + Item::Expr(e) + } +} + +impl From for Item { + fn from(s: Stmt) -> Self { + Item::Stmt(s) } } #[derive(Debug, Clone)] pub enum Expr { + Add { left: Box, right: Box }, + Subtract { left: Box, right: Box }, + Multiply { left: Box, right: Box }, + Divide { left: Box, right: Box }, + FunctionCall { iden: Iden, args: Vec }, + Literal(Value), + Identifier(Iden), +} +impl From for Expr { + fn from(i: Iden) -> Self { + Self::Identifier(i) + } +} + +#[derive(Debug, Clone)] +pub enum Stmt { VariableDeclaration { - iden: String, - init: Option>, + iden: Iden, + init: Option>, }, FunctionDeclaration { - iden: String, + iden: Iden, args: Vec, - body: Vec, + body: Vec, }, BfFDeclaration { - iden: String, + iden: Iden, body: String, }, If { - cond: Box, - body: Vec, + cond: Box, + body: Vec, }, - FunctionCall { - iden: Iden, - args: Vec, - }, - - Addition { - left: Box, - right: Box, - }, - - Literal(Value), - Identifier(Iden), Melo(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b5a3c4f..4c41691 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2,19 +2,22 @@ mod item; mod ops; mod utils; -use item::Expr; +use item::Item; use crate::{ error::{Error, ErrorKind}, lexer::PeekableLexer, + parser::item::{Expr, Stmt}, variables::Value, }; use crate::{lexer::Token, parser::item::Iden}; +pub type ParseResult = Result; + /// Parser structure / state machine pub struct Parser<'a> { lexer: PeekableLexer<'a>, - ast: Vec, + ast: Vec, } impl<'a> Parser<'a> { @@ -26,19 +29,19 @@ impl<'a> Parser<'a> { } } - pub fn init(&mut self) -> Result, Error> { + pub fn init(&mut self) -> Result, Error> { loop { let token = self.lexer.next(); if token.is_none() { return Ok(self.ast.clone()); }; - let expr = self.parse_expr(token)?; + let expr = self.parse_item(token)?; self.ast.push(expr); } } - fn parse_expr(&mut self, token: Option) -> Result { + fn parse_item(&mut self, token: Option) -> ParseResult { if matches!(token, None) { return Err(Error { kind: ErrorKind::EndOfTokenStream, @@ -50,7 +53,7 @@ impl<'a> Parser<'a> { let start = self.lexer.span().start; match token { - Token::Identifier(_) => self.parse_ops(token), + Token::Identifier(_) => self.parse_ops(token).map(|x| x.into()), // Control flow Token::If => self.if_cond(), @@ -60,17 +63,17 @@ impl<'a> Parser<'a> { 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))), + Token::String(x) => Ok(Expr::Literal(Value::Str(x)).into()), + Token::Integer(x) => Ok(Expr::Literal(Value::Int(x)).into()), + Token::Boolean(x) => Ok(Expr::Literal(Value::Bool(x)).into()), + Token::Aboolean(x) => Ok(Expr::Literal(Value::Abool(x)).into()), // Prefix keywords // Melo - ban variable from next usage (runtime error) Token::Melo => { let e = self.require_iden()?; self.require(Token::Semicolon)?; - Ok(Expr::Melo(Iden(e))) + Ok(Stmt::Melo(e).into()) } _ => Err(Error { @@ -83,14 +86,14 @@ impl<'a> Parser<'a> { /// Parse variable declaration /// /// `var [iden] = [literal];` - fn variable_declaration(&mut self) -> Result { + fn variable_declaration(&mut self) -> ParseResult { let iden = self.require_iden()?; let init = match self.lexer.next() { Some(Token::Semicolon) => None, Some(Token::Assignment) => { let value = self.lexer.next(); - let value = self.parse_expr(value)?; + let value = self.parse_item(value)?; self.require(Token::Semicolon)?; Some(Box::new(value)) } @@ -102,13 +105,13 @@ impl<'a> Parser<'a> { } }; - Ok(Expr::VariableDeclaration { iden, init }) + Ok(Stmt::VariableDeclaration { iden, init }.into()) } /// Declare function /// /// `functio [iden] ([expr], [expr]) { ... } - fn function_declaration(&mut self) -> Result { + fn function_declaration(&mut self) -> ParseResult { let iden = self.require_iden()?; self.require(Token::LeftParenthesis)?; let args = vec![]; @@ -118,13 +121,13 @@ impl<'a> Parser<'a> { // Parse function body let body = self.parse_body()?; - Ok(Expr::FunctionDeclaration { iden, args, body }) + Ok(Stmt::FunctionDeclaration { iden, args, body }.into()) } /// Declare BF FFI Function /// /// `bff [iden] { ... }` - fn bff_declaration(&mut self) -> Result { + fn bff_declaration(&mut self) -> ParseResult { let iden = self.require_iden()?; self.require(Token::LeftBrace)?; @@ -155,23 +158,24 @@ impl<'a> Parser<'a> { _ => return Err(self.unexpected_token(None)), }); } - Ok(Expr::BfFDeclaration { iden, body }) + Ok(Stmt::BfFDeclaration { iden, body }.into()) } /// Parse If-expression - pub fn if_cond(&mut self) -> Result { + pub fn if_cond(&mut self) -> ParseResult { self.require(Token::LeftParenthesis)?; let cond = self.lexer.next(); - let cond = self.parse_expr(cond)?; + let cond = self.parse_item(cond)?; self.require(Token::RightParenthesis)?; self.require(Token::LeftBrace)?; let body = self.parse_body()?; - Ok(Expr::If { + Ok(Stmt::If { cond: Box::new(cond), body, - }) + } + .into()) } } diff --git a/src/parser/ops.rs b/src/parser/ops.rs index 04ebde3..c959153 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -1,39 +1,27 @@ use super::*; +type ExprResult = Result; + impl<'a> Parser<'a> { /// Parse operations (got identifier/value) - pub(super) fn parse_ops(&mut self, token: Token) -> Result { - let iden = if let Token::Identifier(i) = token { - Iden(i) - } else { - unimplemented!() - }; - - let mut buf = Vec::new(); - - buf.push(match self.lexer.peek() { - Some(Token::LeftParenthesis) => self.fn_call(iden)?, - _ => unimplemented!(), - }); - - Ok(buf[0].clone()) + pub(super) fn parse_ops(&mut self, token: Token) -> ExprResult { + todo!() } /// Parse function call - fn fn_call(&mut self, iden: Iden) -> Result { + fn fn_call(&mut self, iden: Iden) -> ExprResult { self.lexer.next(); let mut args: Vec = Vec::new(); while let Some(token) = self.lexer.peek() { match token { Token::Identifier(id) => { - args.push(Iden(id.clone()).into()); + args.push(Expr::Identifier(Iden(id.clone()))); self.lexer.next(); } Token::RightParenthesis => break, _ => { let next = self.lexer.next(); - args.push(self.parse_expr(next)?) } } self.require(Token::Comma)?; diff --git a/src/parser/utils.rs b/src/parser/utils.rs index 2b83f1a..8ccfce8 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -2,7 +2,7 @@ use crate::error::{Error, ErrorKind}; use crate::lexer::Token; use crate::variables::Abool; -use super::{item::Expr, Parser}; +use super::{Parser, item::{Iden, Item}}; pub fn abool2num(abool: Abool) -> i32 { match abool { @@ -31,13 +31,13 @@ impl<'a> Parser<'a> { } /// Require an identifier on next and return it - pub(super) fn require_iden(&mut self) -> Result { + pub(super) fn require_iden(&mut self) -> Result { if let Some(Token::Identifier(id)) = self.lexer.next() { - Ok(id) + Ok(Iden(id)) } else { Err(Error { kind: ErrorKind::InvalidIdentifier, - position: self.lexer.span(), + position: self.lexer.span(), }) } } @@ -58,7 +58,7 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_body(&mut self) -> Result, Error> { + pub(super) fn parse_body(&mut self) -> Result, Error> { let mut body = Vec::new(); loop { let token = { @@ -76,7 +76,7 @@ impl<'a> Parser<'a> { if token == Token::RightBrace { break; } - body.push(self.parse_expr(Some(token))?); + body.push(self.parse_item(Some(token))?); } Ok(body) } diff --git a/src/repl.rs b/src/repl.rs new file mode 100644 index 0000000..64e2e8d --- /dev/null +++ b/src/repl.rs @@ -0,0 +1,30 @@ +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(); + println!("{:#?}", ast); + } + Err(rustyline::error::ReadlineError::Eof) => { + println!("bye"); + break; + } + Err(rustyline::error::ReadlineError::Interrupted) => (), + Err(e) => { + println!("Error: {:?}", e); + break; + } + } + } +} From 18a1343e117c206f1648761fc46b7c50303a1e1e Mon Sep 17 00:00:00 2001 From: Erin Date: Sun, 2 May 2021 00:39:08 +0200 Subject: [PATCH 06/10] Added basic math operations - cargo fmt --- src/main.rs | 5 ++++- src/parser/mod.rs | 12 +++++------ src/parser/ops.rs | 51 ++++++++++++++++++++++++++++++++++++++++++--- src/parser/utils.rs | 9 +++++--- 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index eb43c6b..fb7e7b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,7 +37,10 @@ fn main() { println!("{:#?}", ast); } None => { - println!("Hi [AbleScript {}] - AST Printer", env!("CARGO_PKG_VERSION")); + println!( + "Hi [AbleScript {}] - AST Printer", + env!("CARGO_PKG_VERSION") + ); repl::repl(); } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 4c41691..e90a8d5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -53,7 +53,11 @@ impl<'a> Parser<'a> { let start = self.lexer.span().start; match token { - Token::Identifier(_) => self.parse_ops(token).map(|x| x.into()), + Token::Identifier(_) + | Token::Aboolean(_) + | Token::Boolean(_) + | Token::Integer(_) + | Token::String(_) => self.parse_ops(token), // Control flow Token::If => self.if_cond(), @@ -62,12 +66,6 @@ impl<'a> Parser<'a> { Token::Function => self.function_declaration(), Token::BfFunction => self.bff_declaration(), - // Literals - Token::String(x) => Ok(Expr::Literal(Value::Str(x)).into()), - Token::Integer(x) => Ok(Expr::Literal(Value::Int(x)).into()), - Token::Boolean(x) => Ok(Expr::Literal(Value::Bool(x)).into()), - Token::Aboolean(x) => Ok(Expr::Literal(Value::Abool(x)).into()), - // Prefix keywords // Melo - ban variable from next usage (runtime error) Token::Melo => { diff --git a/src/parser/ops.rs b/src/parser/ops.rs index c959153..024b137 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -2,14 +2,59 @@ use super::*; type ExprResult = Result; +#[macro_export] +macro_rules! gen_infix { + ($($fn_name: ident => $type: tt);*$(;)?) => {$( + fn $fn_name(&mut self, left: Expr) -> ExprResult { + let next = self.lexer.next(); + let right = self.ensure_expr(next)?; + Ok(Expr::$type { left: Box::new(left), right: Box::new(right) }) + })* + }; +} + impl<'a> Parser<'a> { - /// Parse operations (got identifier/value) - pub(super) fn parse_ops(&mut self, token: Token) -> ExprResult { - todo!() + pub(super) fn parse_ops(&mut self, token: Token) -> ParseResult { + let mut buf: Expr = self.ensure_expr(Some(token))?; + + loop { + buf = match self.lexer.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)?, + _ => return Ok(buf.into()), + } + } + } + + // Generate infix + gen_infix! { + addition => Add; + subtract => Subtract; + multiply => Multiply; + divide => Divide; + } + + /// Ensure that input token is an expression + fn ensure_expr(&mut self, token: Option) -> 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(s))), + Token::Aboolean(a) => Ok(Expr::Literal(Value::Abool(a))), + Token::Identifier(i) => Ok(Expr::Identifier(Iden(i))), + t => Err(self.unexpected_token(Some(t))), + } } /// Parse function call fn fn_call(&mut self, iden: Iden) -> ExprResult { + return todo!(); self.lexer.next(); let mut args: Vec = Vec::new(); diff --git a/src/parser/utils.rs b/src/parser/utils.rs index 8ccfce8..1706fbe 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -2,7 +2,10 @@ use crate::error::{Error, ErrorKind}; use crate::lexer::Token; use crate::variables::Abool; -use super::{Parser, item::{Iden, Item}}; +use super::{ + item::{Iden, Item}, + Parser, +}; pub fn abool2num(abool: Abool) -> i32 { match abool { @@ -37,7 +40,7 @@ impl<'a> Parser<'a> { } else { Err(Error { kind: ErrorKind::InvalidIdentifier, - position: self.lexer.span(), + position: self.lexer.span(), }) } } @@ -50,7 +53,7 @@ impl<'a> Parser<'a> { self.lexer.slice(), s ), - None => format!("Unexpected token: `{}`)", self.lexer.slice(),), + None => format!("Unexpected token: `{}`", self.lexer.slice(),), }; Error { kind: ErrorKind::SyntaxError(error_msg), From c90d242b0f2824ebf46b9f1ef767e0732f6eb448 Mon Sep 17 00:00:00 2001 From: Erin Date: Sun, 2 May 2021 15:43:25 +0200 Subject: [PATCH 07/10] Parser implements examples - Function call is now stmt (muhehe) --- able-script-test/melo-hello.able | 2 +- src/main.rs | 13 ++++- src/parser/item.rs | 7 ++- src/parser/mod.rs | 20 +++++-- src/parser/ops.rs | 99 +++++++++++++++++++++++++------- src/repl.rs | 13 ++++- 6 files changed, 122 insertions(+), 32 deletions(-) diff --git a/able-script-test/melo-hello.able b/able-script-test/melo-hello.able index 4970c9e..ac4d9f5 100644 --- a/able-script-test/melo-hello.able +++ b/able-script-test/melo-hello.able @@ -1,3 +1,3 @@ var hi = "wonk"; melo hi; -hi print; #Should error out +hi print; # Should error out diff --git a/src/main.rs b/src/main.rs index fb7e7b5..7add24f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod repl; mod variables; use clap::{App, Arg}; +use logos::Source; use parser::Parser; fn main() { // variables::test(); @@ -34,7 +35,17 @@ fn main() { // Parse let mut parser = Parser::new(&source); 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 => { println!( diff --git a/src/parser/item.rs b/src/parser/item.rs index ea556a5..1d805fb 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -27,7 +27,6 @@ pub enum Expr { Subtract { left: Box, right: Box }, Multiply { left: Box, right: Box }, Divide { left: Box, right: Box }, - FunctionCall { iden: Iden, args: Vec }, Literal(Value), Identifier(Iden), } @@ -56,6 +55,10 @@ pub enum Stmt { cond: Box, body: Vec, }, - + FunctionCall { + iden: Iden, + args: Vec, + }, + Print(Expr), Melo(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e90a8d5..9084b01 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -35,9 +35,11 @@ impl<'a> Parser<'a> { if token.is_none() { return Ok(self.ast.clone()); }; - - let expr = self.parse_item(token)?; - self.ast.push(expr); + if matches!(token, Some(Token::Comment)) { + continue; + } + let item = self.parse_item(token)?; + self.ast.push(item); } } @@ -112,9 +114,15 @@ impl<'a> Parser<'a> { fn function_declaration(&mut self) -> ParseResult { let iden = self.require_iden()?; self.require(Token::LeftParenthesis)?; - let args = vec![]; - 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)?; // Parse function body let body = self.parse_body()?; diff --git a/src/parser/ops.rs b/src/parser/ops.rs index 024b137..69d82c1 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -2,12 +2,17 @@ use super::*; type ExprResult = Result; +/// Generate infix expression by pattern left 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.ensure_expr(next)?; + let right = self.parse_expr(next)?; Ok(Expr::$type { left: Box::new(left), right: Box::new(right) }) })* }; @@ -15,14 +20,23 @@ macro_rules! gen_infix { impl<'a> Parser<'a> { pub(super) fn parse_ops(&mut self, token: Token) -> ParseResult { - let mut buf: Expr = self.ensure_expr(Some(token))?; + 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.next() { + 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::Print) => { + self.lexer.next(); + self.require(Token::Semicolon)?; + return Ok(Stmt::Print(buf).into()); + } _ => return Ok(buf.into()), } } @@ -37,41 +51,84 @@ impl<'a> Parser<'a> { } /// Ensure that input token is an expression - fn ensure_expr(&mut self, token: Option) -> ExprResult { + fn parse_expr(&mut self, token: Option) -> 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(s))), Token::Aboolean(a) => Ok(Expr::Literal(Value::Abool(a))), Token::Identifier(i) => Ok(Expr::Identifier(Iden(i))), + Token::LeftParenthesis => self.parse_paren(), t => Err(self.unexpected_token(Some(t))), } } - /// Parse function call - fn fn_call(&mut self, iden: Iden) -> ExprResult { - return todo!(); - self.lexer.next(); - let mut args: Vec = Vec::new(); + /// 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(), + })?; - while let Some(token) = self.lexer.peek() { - match token { - Token::Identifier(id) => { - args.push(Expr::Identifier(Iden(id.clone()))); + 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); } - Token::RightParenthesis => break, - _ => { - let next = self.lexer.next(); - } - } - self.require(Token::Comma)?; + _ => return Ok(buf.into()), + }; } - self.require(Token::RightParenthesis)?; - Ok(Expr::FunctionCall { iden, args }) + } + + /// 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()) } } diff --git a/src/repl.rs b/src/repl.rs index 64e2e8d..4aa3038 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,3 +1,4 @@ +use logos::Source; use rustyline::Editor; use crate::parser::Parser; @@ -14,7 +15,17 @@ pub fn repl() { } let mut parser = Parser::new(&line); let ast = parser.init(); - println!("{:#?}", ast); + 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"); From 5a8dd5051fafe5a6c84f4cf275c0068c1fed8c5c Mon Sep 17 00:00:00 2001 From: Erin Date: Sun, 2 May 2021 16:48:33 +0200 Subject: [PATCH 08/10] Parser production ready - TODO: T-Dark block - cargo fmt - Obeyed our clippy overlord --- README.md | 2 +- src/lexer.rs | 9 +++++++++ src/parser/item.rs | 7 +++++++ src/parser/mod.rs | 4 +++- src/parser/ops.rs | 20 ++++++++++++++++++-- 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e6b1e41..e833a80 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Ablescript +# AbleScript ![Lines of code](https://img.shields.io/tokei/lines/github/abletheabove/able-script) Bravely going where some languages have gone before. diff --git a/src/lexer.rs b/src/lexer.rs index f3038e4..2fcb4bb 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -174,6 +174,15 @@ pub enum Token { #[token("!=")] OpNeq, + #[token("&")] + LogAnd, + + #[token("|")] + LogOr, + + #[token("!")] + LogNot, + /// Base52 based character ('a') #[token("'.*'")] Char, diff --git a/src/parser/item.rs b/src/parser/item.rs index 1d805fb..e9e8a84 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -27,6 +27,13 @@ pub enum Expr { Subtract { left: Box, right: Box }, Multiply { left: Box, right: Box }, Divide { left: Box, right: Box }, + Lt { left: Box, right: Box }, + Gt { left: Box, right: Box }, + Eq { left: Box, right: Box }, + Neq { left: Box, right: Box }, + And { left: Box, right: Box }, + Or { left: Box, right: Box }, + Not(Box), Literal(Value), Identifier(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9084b01..9d31559 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -59,7 +59,9 @@ impl<'a> Parser<'a> { | Token::Aboolean(_) | Token::Boolean(_) | Token::Integer(_) - | Token::String(_) => self.parse_ops(token), + | Token::String(_) + | Token::LogNot => self.parse_ops(token), + // Control flow Token::If => self.if_cond(), diff --git a/src/parser/ops.rs b/src/parser/ops.rs index 69d82c1..1829b86 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -32,6 +32,12 @@ impl<'a> Parser<'a> { 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)?; @@ -48,10 +54,16 @@ impl<'a> Parser<'a> { 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 - fn parse_expr(&mut self, token: Option) -> ExprResult { + pub(super) fn parse_expr(&mut self, token: Option) -> ExprResult { let token = token.ok_or(Error { kind: ErrorKind::EndOfTokenStream, position: self.lexer.span(), @@ -63,6 +75,10 @@ impl<'a> Parser<'a> { Token::String(s) => Ok(Expr::Literal(Value::Str(s))), Token::Aboolean(a) => Ok(Expr::Literal(Value::Abool(a))), Token::Identifier(i) => Ok(Expr::Identifier(Iden(i))), + 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))), } @@ -95,7 +111,7 @@ impl<'a> Parser<'a> { self.lexer.next(); return Ok(buf); } - _ => return Ok(buf.into()), + _ => return Ok(buf), }; } } From ecce0803789805827a7de2a91eb37faf1088ba02 Mon Sep 17 00:00:00 2001 From: Erin Date: Sun, 2 May 2021 17:38:12 +0200 Subject: [PATCH 09/10] Added T-Dark block - obeyed clippy --- src/parser/mod.rs | 50 +++++++++++++++++++++++++++++++++++++-------- src/parser/ops.rs | 12 +++++++++-- src/parser/utils.rs | 6 +++++- src/variables.rs | 6 +++--- 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9d31559..d58ed00 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -18,6 +18,7 @@ pub type ParseResult = Result; pub struct Parser<'a> { lexer: PeekableLexer<'a>, ast: Vec, + tdark: bool, } impl<'a> Parser<'a> { @@ -26,20 +27,26 @@ impl<'a> Parser<'a> { Self { lexer: PeekableLexer::lexer(source), ast: Vec::new(), + tdark: false, } } pub fn init(&mut self) -> Result, Error> { loop { let token = self.lexer.next(); - if token.is_none() { - return Ok(self.ast.clone()); - }; - if matches!(token, Some(Token::Comment)) { - continue; + + match token { + 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); + } } - let item = self.parse_item(token)?; - self.ast.push(item); } } @@ -169,7 +176,7 @@ impl<'a> Parser<'a> { Ok(Stmt::BfFDeclaration { iden, body }.into()) } - /// Parse If-expression + /// Parse If-stmt pub fn if_cond(&mut self) -> ParseResult { self.require(Token::LeftParenthesis)?; let cond = self.lexer.next(); @@ -186,4 +193,31 @@ impl<'a> Parser<'a> { } .into()) } + + /// T-Dark block parsing + pub fn tdark_block(&mut self) -> Result, 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) + } } diff --git a/src/parser/ops.rs b/src/parser/ops.rs index 1829b86..c400925 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -72,9 +72,17 @@ impl<'a> Parser<'a> { 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(s))), + 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(i))), + Token::Identifier(i) => Ok(Expr::Identifier(Iden(if self.tdark { + i.replace("lang", "script") + } else { + i + }))), Token::LogNot => { let next = self.lexer.next(); Ok(Expr::Not(Box::new(self.parse_expr(next)?))) diff --git a/src/parser/utils.rs b/src/parser/utils.rs index 1706fbe..451bc18 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -36,7 +36,11 @@ impl<'a> Parser<'a> { /// Require an identifier on next and return it pub(super) fn require_iden(&mut self) -> Result { if let Some(Token::Identifier(id)) = self.lexer.next() { - Ok(Iden(id)) + if self.tdark { + Ok(Iden(id.replace("lang", "script"))) + } else { + Ok(Iden(id)) + } } else { Err(Error { kind: ErrorKind::InvalidIdentifier, diff --git a/src/variables.rs b/src/variables.rs index fd4a57c..10927c1 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -7,9 +7,9 @@ pub enum Abool { Always = 1, } -impl Into for Abool { - fn into(self) -> bool { - match self { +impl From for bool { + fn from(val: Abool) -> Self { + match val { Abool::Never => false, Abool::Always => true, Abool::Sometimes => rand::thread_rng().gen(), From 051f6e781fb8454f094931614aa6680d7f68e954 Mon Sep 17 00:00:00 2001 From: Erin Date: Sun, 2 May 2021 18:12:51 +0200 Subject: [PATCH 10/10] Added tests and loop flow --- src/lexer.rs | 9 ++++++ src/parser/item.rs | 17 ++++++++-- src/parser/mod.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++++ src/parser/ops.rs | 1 + src/variables.rs | 2 +- 5 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/lexer.rs b/src/lexer.rs index 2fcb4bb..8474e8c 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -94,6 +94,12 @@ pub enum Token { #[token("loop")] Loop, + #[token("break")] + Break, + + #[token("hopback")] + HopBack, + // Literals /// True, False #[regex("true|false", get_bool)] @@ -115,6 +121,9 @@ pub enum Token { #[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)] Identifier(String), + #[regex("nul")] + Nul, + #[token("(")] LeftParenthesis, diff --git a/src/parser/item.rs b/src/parser/item.rs index e9e8a84..2e22968 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -3,7 +3,7 @@ use crate::variables::Value; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Iden(pub String); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Item { Expr(Expr), Stmt(Stmt), @@ -15,13 +15,19 @@ impl From for Item { } } +impl From for Item { + fn from(i: Iden) -> Self { + Item::Expr(Expr::Identifier(i)) + } +} + impl From for Item { fn from(s: Stmt) -> Self { Item::Stmt(s) } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Expr { Add { left: Box, right: Box }, Subtract { left: Box, right: Box }, @@ -43,7 +49,7 @@ impl From for Expr { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Stmt { VariableDeclaration { iden: Iden, @@ -66,6 +72,11 @@ pub enum Stmt { iden: Iden, args: Vec, }, + Loop { + body: Vec, + }, + Break, + HopBack, Print(Expr), Melo(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d58ed00..a3393df 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -67,10 +67,21 @@ impl<'a> Parser<'a> { | Token::Boolean(_) | Token::Integer(_) | Token::String(_) + | Token::Nul | Token::LogNot => self.parse_ops(token), // Control flow 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 Token::Variable => self.variable_declaration(), @@ -194,6 +205,14 @@ impl<'a> Parser<'a> { .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, Error> { self.require(Token::LeftBrace)?; @@ -221,3 +240,62 @@ impl<'a> Parser<'a> { 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) + } +} diff --git a/src/parser/ops.rs b/src/parser/ops.rs index c400925..a8104ca 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -83,6 +83,7 @@ impl<'a> Parser<'a> { } 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)?))) diff --git a/src/variables.rs b/src/variables.rs index 10927c1..8c94fba 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -17,7 +17,7 @@ impl From for bool { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Value { Str(String), Int(i32),