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; + } + } + } +}