diff --git a/able-script-test/parse_test.able b/able-script-test/parse_test.able new file mode 100644 index 0000000..04c522b --- /dev/null +++ b/able-script-test/parse_test.able @@ -0,0 +1,2 @@ +var hello = true; +var test; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 73bcfea..69be9c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,14 @@ +#![forbid(unsafe_code)] + mod base_55; mod parser; -mod scanner; mod tokens; mod variables; use clap::{App, Arg}; -use scanner::Scanner; - +use parser::Parser; fn main() { - variables::test(); + // variables::test(); let matches = App::new("AbleScript") .version(env!("CARGO_PKG_VERSION")) @@ -29,9 +29,10 @@ fn main() { // Read file let source = std::fs::read_to_string(file_path).unwrap(); - // Print token type: `value` - let mut scanner = Scanner::new(&source); - scanner.scan(); + // Parse + let mut parser = Parser::new(&source); + let ast = parser.parse(); + println!("{:#?}", ast); } None => { println!("hi"); diff --git a/src/parser.rs b/src/parser.rs deleted file mode 100644 index 1c4be80..0000000 --- a/src/parser.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::tokens::Abool; - -pub fn abool2num(abool: Abool) -> i32 { - match abool { - Abool::Never => -1, - Abool::Sometimes => 0, - Abool::Always => 1, - } -} -pub fn num2abool(number: i32) -> Abool { - match number { - -1 => Abool::Never, - 0 => Abool::Sometimes, - 1 => Abool::Always, - _ => Abool::Sometimes, - } -} diff --git a/src/parser/item.rs b/src/parser/item.rs new file mode 100644 index 0000000..030b3b8 --- /dev/null +++ b/src/parser/item.rs @@ -0,0 +1,4 @@ +#[derive(Debug, Clone)] +pub enum Expr { + DeclareVariable { iden: String, init: Option }, +} \ No newline at end of file diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..891f18b --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1,68 @@ +mod item; +mod utils; + +use item::Expr; + +use crate::tokens::Token; +use crate::variables::Value; + +use logos::Logos; + +#[derive(Debug)] +pub enum ParseError { + UnexpectedToken, + LexError, + UnexpectedEOF, +} + +/// Parser structure / state machine +pub struct Parser<'a> { + lexer: logos::Lexer<'a, Token>, + ast: Vec, +} + +impl<'a> Parser<'a> { + /// Create a new parser object + pub fn new(source: &'a str) -> Self { + Self { + lexer: Token::lexer(source), + ast: vec![], + } + } + + /// Start parsing Token Vector into Abstract Syntax Tree + pub fn parse(&mut self) -> Vec { + while let Some(token) = self.lexer.next() { + let expr = match token { + Token::Variable => self.variable(), + tok => { + // TODO: Better error handling + println!("Parse error"); + break; + } + }; + self.ast.push(expr.unwrap()); + } + + self.ast.clone() + } + + /// Parse variable declaration + /// + /// `var [iden] = [literal];` + fn variable(&mut self) -> Result { + let iden = self.require(Token::Identifier)?; + + let init = match self.lexer.next() { + Some(Token::Semicolon) => None, + Some(Token::Assignment) => { + let value = self.require(Token::Boolean)?; // TODO: Shouldn't be limited to boolean (pattern match?) + self.require(Token::Semicolon)?; + Some(value) + } + _ => return Err(ParseError::UnexpectedToken), + }; + + Ok(Expr::DeclareVariable { iden, init }) + } +} diff --git a/src/parser/utils.rs b/src/parser/utils.rs new file mode 100644 index 0000000..496f857 --- /dev/null +++ b/src/parser/utils.rs @@ -0,0 +1,30 @@ +use crate::tokens::{Abool, Token}; + +use super::{ParseError, Parser}; + +pub fn abool2num(abool: Abool) -> i32 { + match abool { + Abool::Never => -1, + Abool::Sometimes => 0, + Abool::Always => 1, + } +} +pub fn num2abool(number: i32) -> Abool { + match number { + -1 => Abool::Never, + 0 => Abool::Sometimes, + 1 => Abool::Always, + _ => Abool::Sometimes, + } +} + +impl<'a> Parser<'a> { + /// Require type of token as next and return it's value (sometimes irrelevant) + pub(super) fn require(&mut self, with: Token) -> Result { + if self.lexer.next() == Some(with) { + Ok(self.lexer.slice().to_owned()) + } else { + Err(ParseError::UnexpectedToken) + } + } +} diff --git a/src/scanner.rs b/src/scanner.rs deleted file mode 100644 index 1e96826..0000000 --- a/src/scanner.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::ops::Range; - -use logos::Logos; - -use crate::tokens::{self, Token}; -pub struct Scanner<'a> { - source: &'a str, - lexer: logos::Lexer<'a, Token>, -} - -impl<'a> Scanner<'a> { - pub fn new(source: &'a str) -> Self { - Self { - source, - lexer: tokens::Token::lexer(source), - } - } - - pub fn scan(&mut self) { - while let Some(tok) = self.lexer.next() { - if matches!(tok, Token::Error) { - self.throw_err(&self.lexer.span()); - } else { - println!("Token: {:?}", tok); - } - } - } - - fn throw_err(&self, location: &Range) { - let part = &self.source[location.clone()]; - println!("Unknown keyword `{}` found on {:?}", part, location); - } -} diff --git a/src/tokens.rs b/src/tokens.rs index bf1e315..a41e401 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -30,6 +30,7 @@ pub enum Token { #[regex(r"#.*")] Comment, + // Operators #[token("-")] Subtract, @@ -87,6 +88,16 @@ pub enum Token { #[token("T-Dark")] TDark, + // Expressions + #[token("if")] + If, + + #[token("else")] + Else, + + #[token("loop")] + Loop, + #[regex(r"[ \t\n\f]+", logos::skip)] #[error] Error, diff --git a/src/variables.rs b/src/variables.rs index df9cd06..1c9963f 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -#[derive(Debug)] -enum Value { +#[derive(Debug, Clone)] +pub enum Value { Str(String), Int(i32), Bool(bool),