diff --git a/able-script-test/parse_test.able b/able-script-test/parse_test.able index 9597bd1..6d7ceca 100644 --- a/able-script-test/parse_test.able +++ b/able-script-test/parse_test.able @@ -1 +1,3 @@ -bff a {+++<<>>>[]]]][[]]} \ No newline at end of file +if (true) { + var a = 3; +} \ No newline at end of file diff --git a/src/base_55.rs b/src/base_55.rs index 2efa1c3..bb9d5ce 100644 --- a/src/base_55.rs +++ b/src/base_55.rs @@ -126,8 +126,9 @@ pub fn num2char(number: i32) -> char { #[cfg(test)] mod tests { use super::*; - #[test] fn str_to_base55() { + #[test] + fn str_to_base55() { let chrs: Vec = "AbleScript".chars().map(char2num).collect(); assert_eq!(chrs, &[-1, 2, 12, 5, -19, 3, 18, 9, 16, 20]); } -} \ No newline at end of file +} diff --git a/src/error.rs b/src/error.rs index 0d725ac..1b3ac84 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,4 +11,4 @@ pub enum ErrorKind { SyntaxError(String), EndOfTokenStream, InvalidIdentifier, -} \ No newline at end of file +} diff --git a/src/parser/item.rs b/src/parser/item.rs index d5f670f..46616dc 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -1,5 +1,8 @@ use crate::variables::Value; +#[derive(Debug, Clone)] +pub struct Iden(pub String); + #[derive(Debug, Clone)] pub enum Expr { VariableDeclaration { @@ -14,5 +17,11 @@ pub enum Expr { iden: String, body: String, }, + If { + cond: Box, + body: Vec, + }, + Literal(Value), + Melo(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 57cd844..36a44c0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3,11 +3,11 @@ mod utils; use item::Expr; -use crate::tokens::Token; use crate::{ error::{Error, ErrorKind}, variables::Value, }; +use crate::{parser::item::Iden, tokens::Token}; use logos::Logos; @@ -50,13 +50,28 @@ impl<'a> Parser<'a> { let start = self.lexer.span().start; match token { + // Control flow + Token::If => self.if_cond(), + + // 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))) + } + _ => Err(Error { kind: ErrorKind::SyntaxError("Unexpected identifier".to_owned()), position: start..self.lexer.span().end, @@ -99,25 +114,7 @@ impl<'a> Parser<'a> { self.require(Token::LeftBrace)?; // Parse function body - 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_expr(Some(token))?); - } + let body = self.parse_body()?; Ok(Expr::FunctionDeclaration { iden, body }) } @@ -161,4 +158,21 @@ impl<'a> Parser<'a> { } Ok(Expr::BfFDeclaration { iden, body }) } + + /// Parse If-expression + pub fn if_cond(&mut self) -> Result { + self.require(Token::LeftParenthesis)?; + let cond = self.lexer.next(); + let cond = self.parse_expr(cond)?; + self.require(Token::RightParenthesis)?; + + self.require(Token::LeftBrace)?; + + let body = self.parse_body()?; + + Ok(Expr::If { + cond: Box::new(cond), + body, + }) + } } diff --git a/src/parser/utils.rs b/src/parser/utils.rs index 90b6138..42c2b63 100644 --- a/src/parser/utils.rs +++ b/src/parser/utils.rs @@ -2,7 +2,7 @@ use crate::error::{Error, ErrorKind}; use crate::tokens::Token; use crate::variables::Abool; -use super::Parser; +use super::{item::Expr, Parser}; pub fn abool2num(abool: Abool) -> i32 { match abool { @@ -51,4 +51,27 @@ impl<'a> Parser<'a> { position: self.lexer.span(), } } + + pub(super) fn parse_body(&mut self) -> Result, Error> { + 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_expr(Some(token))?); + } + Ok(body) + } } diff --git a/src/tokens.rs b/src/tokens.rs index 6ee599a..b21c4d5 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -4,6 +4,38 @@ use crate::variables::Abool; #[derive(Logos, Debug, PartialEq, Clone)] pub enum Token { + #[token("functio")] + Function, + + /// Brain fuck FFI + #[token("bff")] + BfFunction, + + /// Variable bro + #[token("var")] + Variable, + + /// Prints the preceding things + #[token("print")] + Print, + + /// Ban the following variable from ever being used again + #[token("melo")] + Melo, + + #[token("T-Dark")] + TDark, + + // Expressions + #[token("if")] + If, + + #[token("else")] + Else, + + #[token("loop")] + Loop, + // Literals /// True, False #[regex("true|false", get_bool)] @@ -88,38 +120,6 @@ pub enum Token { #[token("'.*'")] Char, - #[token("functio")] - Function, - - /// Brain fuck FFI - #[token("bff")] - BfFunction, - - /// Variable bro - #[token("var")] - Variable, - - /// Prints the preceding things - #[token("print")] - Print, - - /// Ban the following variable from ever being used again - #[token("melo")] - Melo, - - #[token("T-Dark")] - TDark, - - // Expressions - #[token("if")] - If, - - #[token("else")] - Else, - - #[token("loop")] - Loop, - #[regex(r"[ \t\n\f]+", logos::skip)] #[error] Error, @@ -184,4 +184,4 @@ mod tests { let result: Vec = lexer.collect(); assert_eq!(result, expected); } -} \ No newline at end of file +} diff --git a/src/variables.rs b/src/variables.rs index 4bfed39..cb476b9 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -29,4 +29,4 @@ pub enum Value { pub struct Variable { melo: bool, value: Value, -} \ No newline at end of file +}