Initial parser work
This commit is contained in:
parent
08af75fbda
commit
7026711b64
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 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");
|
||||
|
|
|
@ -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"#.*")]
|
||||
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,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Value {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
Str(String),
|
||||
Int(i32),
|
||||
Bool(bool),
|
||||
|
|
Loading…
Reference in a new issue