use super::{
lua_doc_parser::LuaDocParser,
marker::{MarkEvent, MarkerEventContainer},
parser_config::ParserConfig,
};
use crate::text::Reader;
use crate::{
LuaSyntaxTree, LuaTreeBuilder,
grammar::parse_chunk,
kind::LuaTokenKind,
lexer::{LuaLexer, LuaTokenData},
parser_error::LuaParseError,
text::SourceRange,
};
#[allow(unused)]
pub struct LuaParser<'a> {
text: &'a str,
events: Vec<MarkEvent>,
tokens: Vec<LuaTokenData>,
token_index: usize,
current_token: LuaTokenKind,
mark_level: usize,
pub parse_config: ParserConfig<'a>,
pub(crate) errors: &'a mut Vec<LuaParseError>,
}
impl MarkerEventContainer for LuaParser<'_> {
fn get_mark_level(&self) -> usize {
self.mark_level
}
fn incr_mark_level(&mut self) {
self.mark_level += 1;
}
fn decr_mark_level(&mut self) {
self.mark_level -= 1;
}
fn get_events(&mut self) -> &mut Vec<MarkEvent> {
&mut self.events
}
}
impl<'a> LuaParser<'a> {
pub fn parse(text: &'a str, config: ParserConfig) -> LuaSyntaxTree {
let mut errors: Vec<LuaParseError> = Vec::new();
let tokens = {
let mut lexer =
LuaLexer::new(Reader::new(text), config.lexer_config(), Some(&mut errors));
lexer.tokenize()
};
let mut parser = LuaParser {
text,
events: Vec::new(),
tokens,
token_index: 0,
current_token: LuaTokenKind::None,
parse_config: config,
mark_level: 0,
errors: &mut errors,
};
parse_chunk(&mut parser);
let errors = parser.get_errors();
let root = {
let mut builder = LuaTreeBuilder::new(
parser.origin_text(),
parser.events,
parser.parse_config.node_cache(),
);
builder.build();
builder.finish()
};
LuaSyntaxTree::new(root, errors)
}
pub fn init(&mut self) {
if self.tokens.is_empty() {
self.current_token = LuaTokenKind::TkEof;
} else {
self.current_token = self.tokens[0].kind;
}
if is_trivia_kind(self.current_token) {
self.bump();
}
}
pub fn origin_text(&self) -> &'a str {
self.text
}
pub fn current_token(&self) -> LuaTokenKind {
self.current_token
}
pub fn current_token_index(&self) -> usize {
self.token_index
}
pub fn current_token_range(&self) -> SourceRange {
if self.token_index >= self.tokens.len() {
if self.tokens.is_empty() {
return SourceRange::EMPTY;
} else {
return self.tokens[self.tokens.len() - 1].range;
}
}
self.tokens[self.token_index].range
}
pub fn previous_token_range(&self) -> SourceRange {
if self.token_index == 0 || self.tokens.is_empty() {
return SourceRange::EMPTY;
}
let mut prev_index = self.token_index - 1;
while prev_index > 0 && is_trivia_kind(self.tokens[prev_index].kind) {
prev_index -= 1;
}
if prev_index < self.tokens.len() && !is_trivia_kind(self.tokens[prev_index].kind) {
self.tokens[prev_index].range
} else if prev_index == 0 {
self.tokens[0].range
} else {
SourceRange::EMPTY
}
}
pub fn current_token_text(&self) -> &str {
let range = &self.tokens[self.token_index].range;
&self.text[range.start_offset..range.end_offset()]
}
pub fn set_current_token_kind(&mut self, kind: LuaTokenKind) {
if self.token_index < self.tokens.len() {
self.tokens[self.token_index].kind = kind;
self.current_token = kind;
}
}
pub fn bump(&mut self) {
if !is_invalid_kind(self.current_token) && self.token_index < self.tokens.len() {
let token = &self.tokens[self.token_index];
self.events.push(MarkEvent::EatToken {
kind: token.kind,
range: token.range,
});
}
let mut next_index = self.token_index + 1;
self.skip_trivia(&mut next_index);
self.parse_trivia_tokens(next_index);
self.token_index = next_index;
if self.token_index >= self.tokens.len() {
self.current_token = LuaTokenKind::TkEof;
return;
}
self.current_token = self.tokens[self.token_index].kind;
}
pub fn peek_next_token(&self) -> LuaTokenKind {
let mut next_index = self.token_index + 1;
self.skip_trivia(&mut next_index);
if next_index >= self.tokens.len() {
LuaTokenKind::None
} else {
self.tokens[next_index].kind
}
}
fn skip_trivia(&self, index: &mut usize) {
if index >= &mut self.tokens.len() {
return;
}
let mut kind = self.tokens[*index].kind;
while is_trivia_kind(kind) {
*index += 1;
if *index >= self.tokens.len() {
break;
}
kind = self.tokens[*index].kind;
}
}
fn parse_trivia_tokens(&mut self, next_index: usize) {
let mut line_count = 0;
let start = self.token_index;
let mut doc_tokens: Vec<LuaTokenData> = Vec::new();
for i in start..next_index {
let token = &self.tokens[i];
match token.kind {
LuaTokenKind::TkShortComment | LuaTokenKind::TkLongComment => {
line_count = 0;
doc_tokens.push(*token);
}
LuaTokenKind::TkEndOfLine => {
line_count += 1;
if doc_tokens.is_empty() {
self.events.push(MarkEvent::EatToken {
kind: token.kind,
range: token.range,
});
} else {
doc_tokens.push(*token);
}
if line_count > 1 && !doc_tokens.is_empty() {
self.parse_comments(&doc_tokens);
doc_tokens.clear();
}
else if doc_tokens.len() == 2 && i >= 2 {
let mut temp_index = i as isize - 2;
let mut inline_comment = false;
while temp_index >= 0 {
let kind = self.tokens[temp_index as usize].kind;
match kind {
LuaTokenKind::TkEndOfLine => {
break;
}
LuaTokenKind::TkWhitespace => {
temp_index -= 1;
continue;
}
_ => {
inline_comment = true;
break;
}
}
}
if inline_comment {
self.parse_comments(&doc_tokens);
doc_tokens.clear();
}
}
}
LuaTokenKind::TkShebang | LuaTokenKind::TkWhitespace => {
if doc_tokens.is_empty() {
self.events.push(MarkEvent::EatToken {
kind: token.kind,
range: token.range,
});
} else {
doc_tokens.push(*token);
}
}
_ => {
if !doc_tokens.is_empty() {
self.parse_comments(&doc_tokens);
doc_tokens.clear();
}
}
}
}
if !doc_tokens.is_empty() {
self.parse_comments(&doc_tokens);
}
}
fn parse_comments(&mut self, comment_tokens: &[LuaTokenData]) {
if !self.parse_config.support_emmylua_doc() {
for token in comment_tokens {
self.events.push(MarkEvent::EatToken {
kind: token.kind,
range: token.range,
});
}
return;
}
let mut trivia_token_start = comment_tokens.len();
for i in (0..comment_tokens.len()).rev() {
if matches!(
comment_tokens[i].kind,
LuaTokenKind::TkWhitespace | LuaTokenKind::TkEndOfLine
) {
trivia_token_start = i;
} else {
break;
}
}
let tokens = &comment_tokens[..trivia_token_start];
LuaDocParser::parse(self, tokens);
for token in comment_tokens.iter().skip(trivia_token_start) {
self.events.push(MarkEvent::EatToken {
kind: token.kind,
range: token.range,
});
}
}
pub fn push_error(&mut self, err: LuaParseError) {
self.errors.push(err);
}
pub fn has_error(&self) -> bool {
!self.errors.is_empty()
}
pub fn get_errors(&self) -> Vec<LuaParseError> {
self.errors.clone()
}
}
fn is_trivia_kind(kind: LuaTokenKind) -> bool {
matches!(
kind,
LuaTokenKind::TkShortComment
| LuaTokenKind::TkLongComment
| LuaTokenKind::TkEndOfLine
| LuaTokenKind::TkWhitespace
| LuaTokenKind::TkShebang
)
}
fn is_invalid_kind(kind: LuaTokenKind) -> bool {
matches!(
kind,
LuaTokenKind::None
| LuaTokenKind::TkEof
| LuaTokenKind::TkWhitespace
| LuaTokenKind::TkShebang
| LuaTokenKind::TkEndOfLine
| LuaTokenKind::TkShortComment
| LuaTokenKind::TkLongComment
)
}
#[cfg(test)]
mod tests {
use crate::grammar::parse_chunk;
use crate::text::Reader;
use crate::{
LuaParser, kind::LuaTokenKind, lexer::LuaLexer, parser::ParserConfig,
parser_error::LuaParseError,
};
#[allow(unused)]
fn new_parser<'a>(
text: &'a str,
config: ParserConfig<'a>,
errors: &'a mut Vec<LuaParseError>,
show_tokens: bool,
) -> LuaParser<'a> {
let tokens = {
let mut lexer = LuaLexer::new(Reader::new(text), config.lexer_config(), Some(errors));
lexer.tokenize()
};
if show_tokens {
println!("tokens: ");
for t in &tokens {
println!("{:?}", t);
}
}
let mut parser = LuaParser {
text,
events: Vec::new(),
tokens,
token_index: 0,
current_token: LuaTokenKind::None,
parse_config: config,
mark_level: 0,
errors,
};
parser.init();
parser
}
#[test]
fn test_parse_and_ast() {
let lua_code = r#"
function foo(a, b)
return a + b
end
"#;
let tree = LuaParser::parse(lua_code, ParserConfig::default());
println!("{:#?}", tree.get_red_root());
}
#[test]
fn test_parse_and_ast_with_error() {
let lua_code = r#"
function foo(a, b)
return a + b
"#;
let tree = LuaParser::parse(lua_code, ParserConfig::default());
println!("{:#?}", tree.get_red_root());
}
#[test]
fn test_parse_comment() {
let lua_code = r#"
-- comment
local t
-- inline comment
"#;
let tree = LuaParser::parse(lua_code, ParserConfig::default());
println!("{:#?}", tree.get_red_root());
}
#[test]
fn test_parse_empty_file() {
let lua_code = r#""#;
let tree = LuaParser::parse(lua_code, ParserConfig::default());
println!("{:#?}", tree.get_red_root());
}
#[test]
fn test_invalid_suffixed_expr_keeps_mark_level_balanced() {
let lua_code = r#"
function MyClass:Test1(data)
if self.nId == 123 and self. then
end
print("Test1")
end"#;
let mut errors = Vec::new();
let mut parser = new_parser(lua_code, ParserConfig::default(), &mut errors, false);
parse_chunk(&mut parser);
assert_eq!(parser.mark_level, 0);
}
#[test]
fn test_invalid_suffixed_expr_still_builds_recoverable_tree() {
let lua_code = r#"
function MyClass:Test1(data)
if self.nId == 123 and self. then
end
print("Test1")
end"#;
let tree = LuaParser::parse(lua_code, ParserConfig::default());
let result = format!("{:#?}", tree.get_red_root());
assert!(result.contains("Syntax(FuncStat)"));
assert!(result.contains("Syntax(IfStat)"));
}
}