Initial parser work
This commit is contained in:
parent
bc1eb42af4
commit
48dd930872
2
able-script-test/parse_test.able
Normal file
2
able-script-test/parse_test.able
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
var hello = true;
|
||||||
|
var test;
|
15
src/main.rs
15
src/main.rs
|
@ -1,14 +1,14 @@
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
mod base_55;
|
mod base_55;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod scanner;
|
|
||||||
mod tokens;
|
mod tokens;
|
||||||
mod variables;
|
mod variables;
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use scanner::Scanner;
|
use parser::Parser;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
variables::test();
|
// variables::test();
|
||||||
|
|
||||||
let matches = App::new("AbleScript")
|
let matches = App::new("AbleScript")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
@ -29,9 +29,10 @@ fn main() {
|
||||||
// Read file
|
// Read file
|
||||||
let source = std::fs::read_to_string(file_path).unwrap();
|
let source = std::fs::read_to_string(file_path).unwrap();
|
||||||
|
|
||||||
// Print token type: `value`
|
// Parse
|
||||||
let mut scanner = Scanner::new(&source);
|
let mut parser = Parser::new(&source);
|
||||||
scanner.scan();
|
let ast = parser.parse();
|
||||||
|
println!("{:#?}", ast);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("hi");
|
println!("hi");
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
4
src/parser/item.rs
Normal file
4
src/parser/item.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Expr {
|
||||||
|
DeclareVariable { iden: String, init: Option<String> },
|
||||||
|
}
|
68
src/parser/mod.rs
Normal file
68
src/parser/mod.rs
Normal file
|
@ -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<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Expr> {
|
||||||
|
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<Expr, ParseError> {
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
30
src/parser/utils.rs
Normal file
30
src/parser/utils.rs
Normal file
|
@ -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<String, ParseError> {
|
||||||
|
if self.lexer.next() == Some(with) {
|
||||||
|
Ok(self.lexer.slice().to_owned())
|
||||||
|
} else {
|
||||||
|
Err(ParseError::UnexpectedToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<usize>) {
|
|
||||||
let part = &self.source[location.clone()];
|
|
||||||
println!("Unknown keyword `{}` found on {:?}", part, location);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -30,6 +30,7 @@ pub enum Token {
|
||||||
#[regex(r"#.*")]
|
#[regex(r"#.*")]
|
||||||
Comment,
|
Comment,
|
||||||
|
|
||||||
|
// Operators
|
||||||
#[token("-")]
|
#[token("-")]
|
||||||
Subtract,
|
Subtract,
|
||||||
|
|
||||||
|
@ -87,6 +88,16 @@ pub enum Token {
|
||||||
#[token("T-Dark")]
|
#[token("T-Dark")]
|
||||||
TDark,
|
TDark,
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
#[token("if")]
|
||||||
|
If,
|
||||||
|
|
||||||
|
#[token("else")]
|
||||||
|
Else,
|
||||||
|
|
||||||
|
#[token("loop")]
|
||||||
|
Loop,
|
||||||
|
|
||||||
#[regex(r"[ \t\n\f]+", logos::skip)]
|
#[regex(r"[ \t\n\f]+", logos::skip)]
|
||||||
#[error]
|
#[error]
|
||||||
Error,
|
Error,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
enum Value {
|
pub enum Value {
|
||||||
Str(String),
|
Str(String),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
|
Loading…
Reference in a new issue