Initial
This commit is contained in:
103
src/parser/ast/expr.rs
Normal file
103
src/parser/ast/expr.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::tokeniser::tokentype::*;
|
||||
|
||||
use super::{typ::Type, Ast};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expr {
|
||||
// Comment(Comment),
|
||||
Group(Box<Expr>),
|
||||
UnOp {
|
||||
typ: Punctuation,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
BinOp {
|
||||
typ: Punctuation,
|
||||
left: Box<Expr>,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
Literal(super::literal::Literal),
|
||||
ArrayIndex {
|
||||
name: Box<Expr>,
|
||||
index: Box<Expr>,
|
||||
},
|
||||
Path(Path),
|
||||
Call {
|
||||
path: Box<Expr>,
|
||||
params: CallParams, // Expr ~ (, Expr)*
|
||||
},
|
||||
//MethodCall {
|
||||
// var_name: Box<Expr>,
|
||||
// method_name: Ident,
|
||||
// params: CallParams,
|
||||
//},
|
||||
|
||||
/// the left side only exists on the /.|->/ chain
|
||||
FieldAccess {
|
||||
left: Box<Option<Expr>>,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
PtrFieldAccess {
|
||||
left: Box<Option<Expr>>,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
ForLoop {
|
||||
init: Box<Ast>,
|
||||
test: Box<Expr>,
|
||||
on_loop: Box<Expr>,
|
||||
body: Block,
|
||||
},
|
||||
WhileLoop {
|
||||
test: Box<Expr>,
|
||||
body: Block,
|
||||
},
|
||||
InfLoop {
|
||||
body: Block,
|
||||
},
|
||||
If(IfExpr),
|
||||
Struct {
|
||||
path: Path,
|
||||
fields: HashMap<Ident, Expr>,
|
||||
},
|
||||
Return(Box<Option<Expr>>),
|
||||
Break,
|
||||
Continue,
|
||||
Cast {
|
||||
left: Box<Expr>,
|
||||
right: Box<Type>
|
||||
},
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn unwrap_path(&self) -> Path {
|
||||
let Expr::Path(p) = self else {panic!("Unwrapping")};
|
||||
p.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallParams(pub Vec<Expr>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block(pub Vec<Ast>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Path(pub Vec<Ident>);
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfExpr {
|
||||
pub test: Box<Expr>,
|
||||
pub body: Block,
|
||||
pub else_if: Option<IfBranchExpr>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IfBranchExpr {
|
||||
ElseIf(Box<IfExpr>),
|
||||
Else(Block)
|
||||
}
|
||||
|
||||
22
src/parser/ast/literal.rs
Normal file
22
src/parser/ast/literal.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::tokeniser::tokentype::*;
|
||||
|
||||
use super::{expr::Expr, typ::Type, Ast};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Literal {
|
||||
Number(Number),
|
||||
Ident(Ident),
|
||||
String(TString),
|
||||
Char(Char),
|
||||
Array(Vec<Expr>),
|
||||
ArrayRepeat {
|
||||
typ: Box<Type>,
|
||||
count: Box<Expr>,
|
||||
},
|
||||
Struct {
|
||||
name: Ident,
|
||||
fields: HashMap<Ident, Ast>
|
||||
},
|
||||
}
|
||||
28
src/parser/ast/mod.rs
Normal file
28
src/parser/ast/mod.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use typ::Type;
|
||||
|
||||
pub use crate::tokeniser::tokentype::*;
|
||||
|
||||
pub mod expr;
|
||||
pub mod literal;
|
||||
pub mod statement;
|
||||
pub mod typ;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Program {
|
||||
pub ast: expr::Block,
|
||||
pub structs: HashMap<Ident, HashMap<Ident, usize>>,
|
||||
pub enums: HashMap<Ident, usize>,
|
||||
pub types: HashMap<Type, Type>,
|
||||
pub functions: HashMap<Ident, (Vec<(Ident, Type)>, Type)>,
|
||||
pub member_functions: HashMap<Ident, HashMap<Ident, (Vec<(Ident, Type)>, Type)>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Ast {
|
||||
Expr(expr::Expr),
|
||||
Statement(statement::Statement),
|
||||
}
|
||||
|
||||
|
||||
44
src/parser/ast/statement.rs
Normal file
44
src/parser/ast/statement.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{expr::{Block, Expr}, typ::Type, Ident, TString};
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Statement {
|
||||
Fn {
|
||||
struct_name: Option<Ident>,
|
||||
name: Ident,
|
||||
params: Vec<(Ident, Type)>,
|
||||
ret_type: Option<Type>,
|
||||
qual_const: bool,
|
||||
qual_extern: Option<TString>, // abi
|
||||
body: Option<Block>, // If None then its a type declaration
|
||||
},
|
||||
TypeAlias {
|
||||
name: Ident,
|
||||
typ: Type,
|
||||
},
|
||||
Struct {
|
||||
name: Ident,
|
||||
fields: Vec<(Ident, Type)>,
|
||||
},
|
||||
Enum {
|
||||
name: Ident,
|
||||
fields: Vec<Ident>,
|
||||
},
|
||||
ConstVar {
|
||||
name: Ident,
|
||||
typ: Type,
|
||||
val: Expr
|
||||
},
|
||||
StaticVar {
|
||||
name: Ident,
|
||||
typ: Type,
|
||||
val: Expr,
|
||||
},
|
||||
Let {
|
||||
name: Ident,
|
||||
typ: Option<Type>,
|
||||
val: Option<Expr>,
|
||||
},
|
||||
}
|
||||
17
src/parser/ast/typ.rs
Normal file
17
src/parser/ast/typ.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use super::{expr::Expr, Ident, Number};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Type {
|
||||
Ref {
|
||||
inner: Box<Type>,
|
||||
mutable: bool,
|
||||
},
|
||||
Array {
|
||||
inner: Box<Type>,
|
||||
},
|
||||
ArrayRepeat {
|
||||
inner: Box<Type>,
|
||||
count: Expr,
|
||||
},
|
||||
Owned(Ident),
|
||||
}
|
||||
454
src/parser/expr.rs
Normal file
454
src/parser/expr.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use crate::{debug, lerror, parser::{typ::parse_type, Punctuation}, tokeniser::Token};
|
||||
|
||||
use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword};
|
||||
|
||||
const BINOP_LIST: &[TokenType] = &[
|
||||
TokenType::Punct(Punctuation::Plus),
|
||||
TokenType::Punct(Punctuation::Minus),
|
||||
TokenType::Punct(Punctuation::Div),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
TokenType::Punct(Punctuation::Mod),
|
||||
TokenType::Punct(Punctuation::Shl),
|
||||
TokenType::Punct(Punctuation::Shr),
|
||||
TokenType::Punct(Punctuation::AndAnd),
|
||||
TokenType::Punct(Punctuation::OrOr),
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Or),
|
||||
TokenType::Punct(Punctuation::Xor),
|
||||
TokenType::Punct(Punctuation::AddEq),
|
||||
TokenType::Punct(Punctuation::SubEq),
|
||||
TokenType::Punct(Punctuation::DivEq),
|
||||
TokenType::Punct(Punctuation::MulEq),
|
||||
TokenType::Punct(Punctuation::ModEq),
|
||||
TokenType::Punct(Punctuation::ShlEq),
|
||||
TokenType::Punct(Punctuation::ShrEq),
|
||||
TokenType::Punct(Punctuation::AndEq),
|
||||
TokenType::Punct(Punctuation::OrEq),
|
||||
TokenType::Punct(Punctuation::XorEq),
|
||||
TokenType::Punct(Punctuation::Eq),
|
||||
TokenType::Punct(Punctuation::EqEq),
|
||||
TokenType::Punct(Punctuation::Lt),
|
||||
TokenType::Punct(Punctuation::Gt),
|
||||
TokenType::Punct(Punctuation::Le),
|
||||
TokenType::Punct(Punctuation::Ge),
|
||||
];
|
||||
|
||||
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool) -> Result<Option<Expr>> {
|
||||
let res = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenL)) {
|
||||
Some(parse_group(tokens)?)
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
|
||||
let p = parse_path(tokens)?;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
Some(parse_struct_literal(tokens, p.unwrap_path())?)
|
||||
} else {
|
||||
Some(p)
|
||||
}
|
||||
} else
|
||||
if let Some(_) = utils::check_from_many(tokens, &[
|
||||
TokenType::Punct(Punctuation::Not),
|
||||
TokenType::Punct(Punctuation::Plus),
|
||||
TokenType::Punct(Punctuation::Minus),
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
]) {
|
||||
Some(parse_unop(tokens)?)
|
||||
} else
|
||||
if let Some(_) = utils::check_from_many(tokens, &[
|
||||
TokenType::string("", false),
|
||||
TokenType::number(0, 0, false),
|
||||
TokenType::char('\0'),
|
||||
TokenType::Delim(Delimiter::SquareL),
|
||||
]) {
|
||||
Some(parse_literal(tokens)?)
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
|
||||
return Ok(Some(parse_while_loop(tokens)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::For)) {
|
||||
return Ok(Some(parse_for_loop(tokens)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Loop)) {
|
||||
return Ok(Some(parse_inf_loop(tokens)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
|
||||
return Ok(Some(parse_return(tokens)?));
|
||||
} else if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) {
|
||||
return Ok(Some(Expr::Break));
|
||||
} else if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) {
|
||||
return Ok(Some(Expr::Continue));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||
return Ok(Some(Expr::If(parse_if(tokens)?)));
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
|
||||
if let Some(res) = res {
|
||||
// check for binop
|
||||
let res = match res {
|
||||
_ if utils::check(tokens, TokenType::Punct(Punctuation::Fieldaccess)).is_some() => {
|
||||
parse_field_access(tokens, res)?
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Punct(Punctuation::Arrow)).is_some() => {
|
||||
parse_ptr_field_access(tokens, res)?
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() => {
|
||||
parse_fn_call(tokens, res)?
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() => {
|
||||
parse_cast(tokens, res)?
|
||||
}
|
||||
_ if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() => {
|
||||
parse_array_index(tokens, res)?
|
||||
}
|
||||
_ => res
|
||||
};
|
||||
|
||||
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) {
|
||||
return Ok(Some(parse_binop(tokens, res, precedence)?));
|
||||
} else {
|
||||
return Ok(Some(res));
|
||||
}
|
||||
|
||||
}
|
||||
if consume_semi {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn parse_return(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Return));
|
||||
let item = parse_expr(tokens, 0, true)?;
|
||||
Ok(Expr::Return(Box::new(item)))
|
||||
}
|
||||
|
||||
fn parse_cast(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::As), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
Ok(Expr::Cast {
|
||||
left: Box::new(left),
|
||||
right: Box::new(typ)
|
||||
})
|
||||
}
|
||||
fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected test for if statement, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Else)) {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens)?));
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
else_if: Some(branch)
|
||||
})
|
||||
} else {
|
||||
let branch = IfBranchExpr::Else(parse_block(tokens)?);
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
else_if: Some(branch)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
else_if: None
|
||||
})
|
||||
}
|
||||
}
|
||||
fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected test comparrison for while loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
Ok(Expr::WhileLoop {
|
||||
test: Box::new(test),
|
||||
body: block
|
||||
})
|
||||
}
|
||||
fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
|
||||
let Some(pre) = parse_item(tokens)? else {
|
||||
lerror!(loc.loc(), "Expected init stat for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected test comparrison for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
|
||||
let Some(post) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
|
||||
Ok(Expr::ForLoop {
|
||||
init: Box::new(pre),
|
||||
test: Box::new(test),
|
||||
on_loop: Box::new(post),
|
||||
body: block
|
||||
})
|
||||
}
|
||||
fn parse_inf_loop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "");
|
||||
let block = parse_block(tokens)?;
|
||||
Ok(Expr::InfLoop { body: block })
|
||||
}
|
||||
fn parse_fn_call(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "");
|
||||
let mut params = Vec::new();
|
||||
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
break;
|
||||
}
|
||||
let Some(param) = parse_expr(tokens, 0, false)? else {break};
|
||||
params.push(param);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
lerror!(&utils::get_last_loc(), "Expected ',' or ')' but didnt find either");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
|
||||
Ok(Expr::Call { path: Box::new(left), params: CallParams(params) })
|
||||
}
|
||||
fn parse_array_index(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
|
||||
let Some(idx) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected index for in array index but found nothing.");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "");
|
||||
Ok(Expr::ArrayIndex {
|
||||
name: Box::new(left),
|
||||
index: Box::new(idx)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_field_access(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Fieldaccess), "unreachable")?;
|
||||
|
||||
let right = if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_ptr_field_access(tokens, right)?
|
||||
} else if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_field_access(tokens, right)?
|
||||
} else {
|
||||
parse_path(tokens)?
|
||||
};
|
||||
Ok(Expr::FieldAccess {
|
||||
left: Box::new(Some(left)),
|
||||
right: Box::new(right)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_ptr_field_access(tokens: &mut Vec<Token>, left: Expr) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Arrow), "unreachable")?;
|
||||
let right = if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_ptr_field_access(tokens, right)?
|
||||
} else if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_field_access(tokens, right)?
|
||||
} else {
|
||||
parse_path(tokens)?
|
||||
};
|
||||
Ok(Expr::PtrFieldAccess {
|
||||
left: Box::new(Some(left)),
|
||||
right: Box::new(right)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_literal(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::string("", false)) {
|
||||
let TokenType::String(str) = tkn.tt() else {unreachable!()};
|
||||
return Ok(Expr::Literal(Literal::String(str.clone())));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) {
|
||||
let TokenType::Number(val) = tkn.tt() else {unreachable!()};
|
||||
return Ok(Expr::Literal(Literal::Number(val.clone())));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) {
|
||||
let TokenType::Char(val) = tkn.tt() else {unreachable!()};
|
||||
return Ok(Expr::Literal(Literal::Char(val.clone())));
|
||||
} else
|
||||
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) {
|
||||
return Ok(Expr::Literal(Literal::Array(Vec::new())));
|
||||
}
|
||||
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
|
||||
let first = parse_expr(tokens, 0, false)?;
|
||||
let Some(first) = first else { unreachable!() };
|
||||
|
||||
let mut values = Vec::new();
|
||||
values.push(first);
|
||||
while !tokens.is_empty() {
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else{break};
|
||||
|
||||
values.push(val);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
return Ok(Expr::Literal(Literal::Array(values)));
|
||||
} else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
|
||||
let typ = parse_type(tokens)?;
|
||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
return Ok(Expr::Literal(Literal::ArrayRepeat {
|
||||
typ: Box::new(typ),
|
||||
count: Box::new(count)
|
||||
}));
|
||||
} else {
|
||||
if let Some(curr) = tokens.last() {
|
||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found {}", curr.tt());
|
||||
} else {
|
||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found nothing");
|
||||
}
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<Expr> {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||
let mut fields = HashMap::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_expr(tokens, 0, false)?.unwrap();
|
||||
fields.insert(name.tt().unwrap_ident(), typ);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Expr::Struct { path: name, fields })
|
||||
}
|
||||
|
||||
fn parse_group(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let Some(expr) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected expr found nothing");
|
||||
bail!("")
|
||||
};
|
||||
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
Ok(Expr::Group(Box::new(expr)))
|
||||
}
|
||||
|
||||
fn parse_path(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let mut buf = Vec::new();
|
||||
let part = utils::check_consume(tokens, TokenType::ident("")).unwrap();
|
||||
|
||||
|
||||
buf.push(part.tt().unwrap_ident());
|
||||
while let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Pathaccess)) {
|
||||
let Some(part) = utils::check_consume(tokens, TokenType::ident("")) else {
|
||||
break;
|
||||
};
|
||||
buf.push(part.tt().unwrap_ident());
|
||||
}
|
||||
|
||||
Ok(Expr::Path(Path(buf)))
|
||||
}
|
||||
|
||||
fn parse_unop(tokens: &mut Vec<Token>) -> Result<Expr> {
|
||||
let typ = utils::check_consume_or_err_from_many(tokens, &[
|
||||
TokenType::Punct(Punctuation::Not),
|
||||
TokenType::Punct(Punctuation::Plus),
|
||||
TokenType::Punct(Punctuation::Minus),
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
], "")?;
|
||||
let loc = typ.loc().clone();
|
||||
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()};
|
||||
|
||||
let Some(right) = parse_expr(tokens, 5, false)? else {
|
||||
lerror!(&loc, "Expected expression after unary token, found nothing");
|
||||
bail!("")
|
||||
};
|
||||
Ok(Expr::UnOp {
|
||||
typ,
|
||||
right: Box::new(right)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: Expr, precedence: usize) -> Result<Expr> {
|
||||
// TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
|
||||
|
||||
loop {
|
||||
let op = match tokens.last() {
|
||||
Some(op) if BINOP_LIST.contains(&op.tt()) => {
|
||||
let TokenType::Punct(op) = op.tt() else {unreachable!()};
|
||||
op.clone()
|
||||
}
|
||||
Some(op) if [
|
||||
TokenType::Delim(Delimiter::ParenR),
|
||||
TokenType::Punct(Punctuation::Semi)
|
||||
].contains(&op.tt()) => {
|
||||
break
|
||||
}
|
||||
Some(op) if matches!(&op.tt(), TokenType::Ident(_)) => {
|
||||
lerror!(op.loc(), "Unexpected identifier, did you forget a semicolon? ';'");
|
||||
bail!("");
|
||||
}
|
||||
Some(_) |
|
||||
None => break,
|
||||
};
|
||||
debug!("OP: {op:?}");
|
||||
let (lp, rp) = op.precedence().unwrap();
|
||||
if lp < precedence {
|
||||
break
|
||||
}
|
||||
|
||||
_ = tokens.pop();
|
||||
let Some(rhs) = parse_expr(tokens, rp, false)? else {break;};
|
||||
lhs = Expr::BinOp {
|
||||
typ: op,
|
||||
left: Box::new(lhs),
|
||||
right: Box::new(rhs)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Ok(lhs)
|
||||
|
||||
}
|
||||
|
||||
pub fn parse_block(tokens: &mut Vec<Token>) -> Result<Block> {
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||
let mut items = Vec::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(item) = parse_item(tokens)? {
|
||||
items.push(item);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
|
||||
Ok(Block(items))
|
||||
}
|
||||
50
src/parser/mod.rs
Normal file
50
src/parser/mod.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ast::{expr::Block, Ast, Program};
|
||||
|
||||
use crate::tokeniser::{Token, tokentype::*};
|
||||
|
||||
pub mod ast;
|
||||
mod expr;
|
||||
mod stat;
|
||||
mod utils;
|
||||
mod typ;
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
|
||||
let mut prog_body = Vec::new();
|
||||
|
||||
while !tokens.is_empty() {
|
||||
if let Some(item) = parse_item(&mut tokens)? {
|
||||
prog_body.push(item);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(Program {
|
||||
ast: Block(prog_body),
|
||||
enums: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
member_functions: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
structs: HashMap::new()
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_item(tokens: &mut Vec<Token>) -> Result<Option<Ast>> {
|
||||
if let Some(stat) = stat::parse_statement(tokens)? {
|
||||
return Ok(Some(Ast::Statement(stat)));
|
||||
}
|
||||
if let Some(expr) = expr::parse_expr(tokens, 0, true)? {
|
||||
return Ok(Some(Ast::Expr(expr)));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
211
src/parser/stat.rs
Normal file
211
src/parser/stat.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use anyhow::bail;
|
||||
|
||||
use crate::lerror;
|
||||
use crate::parser::ast::TokenType;
|
||||
use crate::parser::expr::parse_expr;
|
||||
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
|
||||
use crate::tokeniser::Token;
|
||||
use super::ast::typ::Type;
|
||||
use super::expr::parse_block;
|
||||
use super::typ::parse_type;
|
||||
use super::utils;
|
||||
use super::ast::statement::Statement;
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<Statement>> {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||
Ok(Some(parse_fn(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
|
||||
Ok(Some(parse_type_alias(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
|
||||
Ok(Some(parse_constant(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
|
||||
Ok(Some(parse_static(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
|
||||
Ok(Some(parse_struct(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Enum)) {
|
||||
Ok(Some(parse_enum(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
|
||||
Ok(Some(parse_let(tokens)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_enum(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Enum));
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||
let mut fields = Vec::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
let field_name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
let loc = field_name.loc().clone();
|
||||
let field_name = field_name.tt().unwrap_ident();
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
lerror!(&loc, "Expected comma after struct field");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
fields.push(field_name);
|
||||
}
|
||||
|
||||
Ok(Statement::Enum { name, fields })
|
||||
}
|
||||
|
||||
fn parse_struct(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Struct));
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||
let mut fields = Vec::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
let field_name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
let loc = field_name.loc().clone();
|
||||
let field_name = field_name.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
lerror!(&loc, "Expected comma after struct field");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
fields.push((field_name, typ));
|
||||
}
|
||||
|
||||
Ok(Statement::Struct { name, fields })
|
||||
}
|
||||
|
||||
fn parse_static(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Static));
|
||||
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(Statement::StaticVar { name, typ, val })
|
||||
}
|
||||
|
||||
fn parse_let(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Let));
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
let mut typ = None;
|
||||
let mut val = None;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Colon)) {
|
||||
typ = Some(parse_type(tokens)?);
|
||||
}
|
||||
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) {
|
||||
let Some(_val) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
val = Some(_val);
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(Statement::Let { name, typ, val })
|
||||
}
|
||||
fn parse_constant(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Const));
|
||||
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||
unimplemented!()
|
||||
}
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(Statement::ConstVar { name, typ, val })
|
||||
}
|
||||
|
||||
fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Type));
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
|
||||
Ok(Statement::TypeAlias { name, typ })
|
||||
}
|
||||
|
||||
fn parse_fn(tokens: &mut Vec<Token>) -> Result<Statement> {
|
||||
// Just remove the kw since we checked it before
|
||||
_ = utils::check_consume(tokens, TokenType::Keyword(Keyword::Fn));
|
||||
|
||||
let mut struct_name = None;
|
||||
let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
// Check if this is a struct method
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||
struct_name = Some(name);
|
||||
name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
}
|
||||
let params = parse_fn_params(tokens)?;
|
||||
|
||||
// Check for return type cause it optional
|
||||
let mut ret_type = None;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
ret_type = Some(parse_type(tokens)?);
|
||||
}
|
||||
let body;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
body = Some(parse_block(tokens)?);
|
||||
} else {
|
||||
// Check if its just a declaration
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
body = None;
|
||||
}
|
||||
Ok(Statement::Fn {
|
||||
struct_name,
|
||||
name,
|
||||
params,
|
||||
ret_type,
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, Type)>> {
|
||||
let mut args = Vec::new();
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
while !tokens.is_empty() {
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
//dbg!(&name);
|
||||
let typ = parse_type(tokens)?;
|
||||
args.push((name.tt().unwrap_ident(), typ));
|
||||
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
|
||||
53
src/parser/typ.rs
Normal file
53
src/parser/typ.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::tokeniser::Token;
|
||||
|
||||
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
|
||||
|
||||
pub fn parse_type(tokens: &mut Vec<Token>) -> Result<Type> {
|
||||
let mut ref_cnt = Vec::new();
|
||||
while let Some(tok) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Ampersand)) {
|
||||
if let Some(tok) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Mut)) {
|
||||
ref_cnt.push(tok.clone());
|
||||
} else {
|
||||
ref_cnt.push(tok.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut typ;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(super::Delimiter::SquareL)) {
|
||||
let itm_typ = parse_type(tokens)?;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
|
||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
||||
typ = Type::ArrayRepeat {
|
||||
inner: Box::new(itm_typ),
|
||||
count
|
||||
}
|
||||
} else {
|
||||
typ = Type::Array {
|
||||
inner: Box::new(itm_typ),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let ident = utils::check_consume_or_err(tokens, TokenType::ident(""), "a")?;
|
||||
typ = Type::Owned(ident.tt().unwrap_ident());
|
||||
}
|
||||
while let Some(reft) = ref_cnt.pop() {
|
||||
match reft.tt() {
|
||||
TokenType::Keyword(Keyword::Mut) => {
|
||||
typ = Type::Ref {
|
||||
inner: Box::new(typ),
|
||||
mutable: true
|
||||
}
|
||||
},
|
||||
TokenType::Punct(Punctuation::Ampersand) => {
|
||||
typ = Type::Ref {
|
||||
inner: Box::new(typ),
|
||||
mutable: false
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
Ok(typ)
|
||||
}
|
||||
129
src/parser/utils.rs
Normal file
129
src/parser/utils.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::{common::Loc, debug, lerror, tokeniser::Token};
|
||||
|
||||
use super::ast::TokenType;
|
||||
|
||||
|
||||
lazy_static!(
|
||||
static ref LAST_LOC: Arc<Mutex<Loc>> = Arc::new(Mutex::new(Loc::default()));
|
||||
);
|
||||
|
||||
pub fn check(tokens: &Vec<Token>, tt: TokenType) -> Option<&Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
debug!("check: {}", tkn);
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn check_2_last(tokens: &Vec<Token>, tt: TokenType) -> Option<&Token> {
|
||||
if tokens.len() < 2 {
|
||||
return None
|
||||
}
|
||||
if let Some(tkn) = tokens.get(tokens.len() - 2) {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_consume(tokens: &mut Vec<Token>, tt: TokenType) -> Option<Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
*LAST_LOC.lock().expect("Could not lock LAST_LOC") = tkn.loc().clone();
|
||||
debug!("check_consume: {}", tokens.last()?);
|
||||
return Some(tokens.pop()?);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_consume_or_err(tokens: &mut Vec<Token>, tt: TokenType, err_msg: &'static str) -> anyhow::Result<Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
*LAST_LOC.lock().expect("Could not lock LAST_LOC") = tkn.loc().clone();
|
||||
return Ok(tokens.pop().expect("Unreachable"));
|
||||
} else {
|
||||
lerror!(tkn.loc(), "Expected: '{tt}', got: '{}': {err_msg}", tkn.tt());
|
||||
//anyhow::bail!(format!("{}: ERROR: Expected: '{tt:?}', got: '{:?}': {err_msg}", tkn.loc(), tkn.tt()))
|
||||
anyhow::bail!("")
|
||||
}
|
||||
}
|
||||
let loc = LAST_LOC.lock().expect("Could not lock LAST_LOC");
|
||||
lerror!(&loc, "Expected: '{tt}', got: '(empty)': {err_msg}");
|
||||
// anyhow::bail!(format!("{loc}: ERROR: Expected '{tt:?}', got (empty): {err_msg}"))
|
||||
anyhow::bail!("")
|
||||
}
|
||||
|
||||
pub fn check_consume_from_many(tokens: &mut Vec<Token>, tts: &[TokenType]) -> Option<Token> {
|
||||
for tt in tts {
|
||||
if let Some(tkn) = check_consume(tokens, tt.clone()) {
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_from_many<'a>(tokens: &'a mut Vec<Token>, tts: &[TokenType]) -> Option<&'a Token> {
|
||||
for tt in tts {
|
||||
if let Some(tkn) = check(tokens, tt.clone()) {
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_consume_or_err_from_many(tokens: &mut Vec<Token>, tts: &[TokenType], err_msg: &'static str) -> anyhow::Result<Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
for tt in tts {
|
||||
if tkn.tt() == tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
*LAST_LOC.lock().expect("Could not lock LAST_LOC") = tkn.loc().clone();
|
||||
return Ok(tokens.pop().expect("Unreachable"));
|
||||
}
|
||||
}
|
||||
lerror!(tkn.loc(), "Expected: '{tts:?}', got: '{}': {err_msg}", tkn.tt());
|
||||
anyhow::bail!("")
|
||||
}
|
||||
let loc = LAST_LOC.lock().expect("Could not lock LAST_LOC");
|
||||
lerror!(&loc, "Expected: '{tts:?}', got: '(empty)': {err_msg}");
|
||||
anyhow::bail!("")
|
||||
}
|
||||
|
||||
pub fn get_last_loc() -> Loc {
|
||||
LAST_LOC.lock().expect("Could not lock LAST_LOC").clone()
|
||||
}
|
||||
Reference in New Issue
Block a user