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/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/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/able-script-test/parse_test.able b/able-script-test/parse_test.able deleted file mode 100644 index 6d7ceca..0000000 --- a/able-script-test/parse_test.able +++ /dev/null @@ -1,3 +0,0 @@ -if (true) { - var a = 3; -} \ No newline at end of file diff --git a/src/tokens.rs b/src/lexer.rs similarity index 64% rename from src/tokens.rs rename to src/lexer.rs index b21c4d5..8474e8c 100644 --- a/src/tokens.rs +++ b/src/lexer.rs @@ -1,7 +1,65 @@ -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 { + match self.peeked.take() { + Some(v) => v, + None => self.lexer.next(), + } + } +} + #[derive(Logos, Debug, PartialEq, Clone)] pub enum Token { #[token("functio")] @@ -36,6 +94,12 @@ pub enum Token { #[token("loop")] Loop, + #[token("break")] + Break, + + #[token("hopback")] + HopBack, + // Literals /// True, False #[regex("true|false", get_bool)] @@ -57,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, @@ -116,6 +183,15 @@ pub enum Token { #[token("!=")] OpNeq, + #[token("&")] + LogAnd, + + #[token("|")] + LogOr, + + #[token("!")] + LogNot, + /// Base52 based character ('a') #[token("'.*'")] Char, diff --git a/src/main.rs b/src/main.rs index 32d3d3c..7add24f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,13 @@ mod base_55; mod error; +mod lexer; mod parser; -mod tokens; +mod repl; mod variables; use clap::{App, Arg}; +use logos::Source; use parser::Parser; fn main() { // variables::test(); @@ -14,7 +16,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") @@ -33,11 +35,24 @@ 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!("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 46616dc..2e22968 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -1,27 +1,82 @@ use crate::variables::Value; -#[derive(Debug, Clone)] +#[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), +} + +impl From for Item { + fn from(e: Expr) -> Self { + Item::Expr(e) + } +} + +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, PartialEq)] pub enum Expr { + Add { left: Box, right: Box }, + 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), +} +impl From for Expr { + fn from(i: Iden) -> Self { + Self::Identifier(i) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Stmt { VariableDeclaration { - iden: String, - init: Option>, + iden: Iden, + init: Option>, }, FunctionDeclaration { - iden: String, - body: Vec, + iden: Iden, + args: Vec, + body: Vec, }, BfFDeclaration { - iden: String, + iden: Iden, body: String, }, If { - cond: Box, - body: Vec, + cond: Box, + body: Vec, }, - - Literal(Value), + FunctionCall { + 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 36a44c0..a3393df 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,44 +1,56 @@ 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::{parser::item::Iden, tokens::Token}; +use crate::{lexer::Token, parser::item::Iden}; -use logos::Logos; +pub type ParseResult = Result; /// Parser structure / state machine pub struct Parser<'a> { - lexer: logos::Lexer<'a, Token>, - ast: Vec, + lexer: PeekableLexer<'a>, + ast: Vec, + tdark: bool, } 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(), + tdark: false, } } - 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)?; - self.ast.push(expr); + 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); + } + } } } - 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,26 +62,38 @@ impl<'a> Parser<'a> { let start = self.lexer.span().start; match token { + Token::Identifier(_) + | Token::Aboolean(_) + | 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(), Token::Function => self.function_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 // 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 { @@ -82,14 +106,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)) } @@ -101,28 +125,35 @@ 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)?; - 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()?; - Ok(Expr::FunctionDeclaration { iden, 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)?; @@ -140,9 +171,6 @@ impl<'a> Parser<'a> { } }; - if token == Token::RightBrace { - break; - } body.push_str(match token { Token::OpGt | Token::OpLt @@ -156,23 +184,118 @@ 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 { + /// Parse If-stmt + 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()) + } + + /// 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)?; + 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) } } diff --git a/src/parser/ops.rs b/src/parser/ops.rs new file mode 100644 index 0000000..a8104ca --- /dev/null +++ b/src/parser/ops.rs @@ -0,0 +1,159 @@ +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.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) -> 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()) + } +} diff --git a/src/parser/utils.rs b/src/parser/utils.rs index 42c2b63..451bc18 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -1,8 +1,11 @@ use crate::error::{Error, ErrorKind}; -use crate::tokens::Token; +use crate::lexer::Token; use crate::variables::Abool; -use super::{item::Expr, Parser}; +use super::{ + item::{Iden, Item}, + Parser, +}; pub fn abool2num(abool: Abool) -> i32 { match abool { @@ -30,9 +33,14 @@ impl<'a> Parser<'a> { } } - pub(super) fn require_iden(&mut self) -> Result { + /// 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) + if self.tdark { + Ok(Iden(id.replace("lang", "script"))) + } else { + Ok(Iden(id)) + } } else { Err(Error { 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) -> 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(), } } - 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 = { @@ -70,7 +83,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..4aa3038 --- /dev/null +++ b/src/repl.rs @@ -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; + } + } + } +} diff --git a/src/variables.rs b/src/variables.rs index cb476b9..8c94fba 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(), @@ -17,12 +17,13 @@ impl Into for Abool { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Value { Str(String), Int(i32), Bool(bool), Abool(Abool), + Nul, } #[derive(Debug)]