2023-02-22 11:35:49 +00:00
|
|
|
use std::collections::{BTreeMap, HashMap};
|
|
|
|
|
2023-02-21 20:06:11 +00:00
|
|
|
use anyhow::{Context, Result};
|
|
|
|
use full_moon::{
|
|
|
|
ast::{
|
2023-02-22 10:31:58 +00:00
|
|
|
types::{TypeFieldKey, TypeInfo},
|
2023-02-21 20:06:11 +00:00
|
|
|
Stmt,
|
|
|
|
},
|
|
|
|
tokenizer::{TokenReference, TokenType},
|
|
|
|
};
|
|
|
|
use regex::Regex;
|
|
|
|
|
2023-02-22 09:11:36 +00:00
|
|
|
use super::{
|
2023-02-22 10:31:58 +00:00
|
|
|
builder::DefinitionsItemBuilder, item::DefinitionsItem, moonwave::parse_moonwave_style_comment,
|
|
|
|
type_info_ext::TypeInfoExt,
|
2023-02-22 09:11:36 +00:00
|
|
|
};
|
|
|
|
|
2023-02-21 21:14:00 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2023-02-22 09:53:00 +00:00
|
|
|
struct DefinitionsParserItem {
|
2023-02-21 20:06:11 +00:00
|
|
|
name: String,
|
|
|
|
comment: Option<String>,
|
|
|
|
type_info: TypeInfo,
|
|
|
|
}
|
|
|
|
|
2023-02-22 11:35:49 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2023-02-22 10:31:58 +00:00
|
|
|
pub struct DefinitionsParser {
|
2023-02-22 11:35:49 +00:00
|
|
|
found_top_level_items: BTreeMap<String, DefinitionsParserItem>,
|
|
|
|
found_top_level_types: HashMap<String, TypeInfo>,
|
2023-02-22 10:31:58 +00:00
|
|
|
found_top_level_declares: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DefinitionsParser {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2023-02-22 11:35:49 +00:00
|
|
|
found_top_level_items: BTreeMap::new(),
|
|
|
|
found_top_level_types: HashMap::new(),
|
|
|
|
found_top_level_declares: Vec::new(),
|
2023-02-22 10:31:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-22 11:18:30 +00:00
|
|
|
/**
|
|
|
|
Parses the given Luau type definitions into parser items.
|
|
|
|
|
|
|
|
The parser items will be stored internally and can be converted
|
|
|
|
into usable definition items using [`DefinitionsParser::drain`].
|
|
|
|
*/
|
2023-02-22 10:31:58 +00:00
|
|
|
pub fn parse<S>(&mut self, contents: S) -> Result<()>
|
|
|
|
where
|
|
|
|
S: AsRef<str>,
|
|
|
|
{
|
|
|
|
// TODO: Properly handle the "declare class" syntax, for now we just skip it
|
2023-02-26 20:41:15 +00:00
|
|
|
let mut no_class_declares = contents.as_ref().replace("\r\n", "\n");
|
2023-02-22 10:31:58 +00:00
|
|
|
while let Some(dec) = no_class_declares.find("\ndeclare class") {
|
|
|
|
let end = no_class_declares.find("\nend").unwrap();
|
|
|
|
let before = &no_class_declares[0..dec];
|
|
|
|
let after = &no_class_declares[end + 4..];
|
|
|
|
no_class_declares = format!("{before}{after}");
|
|
|
|
}
|
|
|
|
// Replace declares with export type syntax that can be parsed by full_moon,
|
|
|
|
// find all declare statements and save declared names for later parsing
|
|
|
|
let regex_declare = Regex::new(r#"declare (\w+): "#).unwrap();
|
|
|
|
let resulting_contents = regex_declare
|
|
|
|
.replace_all(&no_class_declares, "export type $1 =")
|
|
|
|
.to_string();
|
|
|
|
let found_declares = regex_declare
|
|
|
|
.captures_iter(&no_class_declares)
|
|
|
|
.map(|cap| cap[1].to_string())
|
|
|
|
.collect();
|
|
|
|
// Parse contents into top-level parser items for later use
|
2023-02-22 11:35:49 +00:00
|
|
|
let mut found_top_level_items = BTreeMap::new();
|
|
|
|
let mut found_top_level_types = HashMap::new();
|
2023-02-22 10:31:58 +00:00
|
|
|
let ast =
|
|
|
|
full_moon::parse(&resulting_contents).context("Failed to parse type definitions")?;
|
|
|
|
for stmt in ast.nodes().stmts() {
|
|
|
|
if let Some((declaration, token_reference)) = match stmt {
|
|
|
|
Stmt::ExportedTypeDeclaration(exp) => {
|
|
|
|
Some((exp.type_declaration(), exp.export_token()))
|
|
|
|
}
|
|
|
|
Stmt::TypeDeclaration(typ) => Some((typ, typ.type_token())),
|
|
|
|
_ => None,
|
|
|
|
} {
|
2023-02-22 11:35:49 +00:00
|
|
|
let name = declaration.type_name().token().to_string();
|
|
|
|
found_top_level_items.insert(
|
|
|
|
name.clone(),
|
|
|
|
DefinitionsParserItem {
|
|
|
|
name: name.clone(),
|
|
|
|
comment: find_token_moonwave_comment(token_reference),
|
|
|
|
type_info: declaration.type_definition().clone(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
found_top_level_types.insert(name, declaration.type_definition().clone());
|
2023-02-22 10:31:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Store results
|
|
|
|
self.found_top_level_items = found_top_level_items;
|
2023-02-22 11:35:49 +00:00
|
|
|
self.found_top_level_types = found_top_level_types;
|
2023-02-22 10:31:58 +00:00
|
|
|
self.found_top_level_declares = found_declares;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn convert_parser_item_into_doc_item(&self, item: DefinitionsParserItem) -> DefinitionsItem {
|
2023-02-22 09:53:00 +00:00
|
|
|
let mut builder = DefinitionsItemBuilder::new()
|
2023-02-22 11:18:30 +00:00
|
|
|
.with_kind(item.type_info.parse_definitions_kind())
|
2023-02-22 10:31:58 +00:00
|
|
|
.with_name(&item.name);
|
|
|
|
if self.found_top_level_declares.contains(&item.name) {
|
2023-02-22 09:48:04 +00:00
|
|
|
builder = builder.as_exported();
|
|
|
|
}
|
2023-02-22 10:31:58 +00:00
|
|
|
if let Some(comment) = item.comment {
|
2023-02-21 20:06:11 +00:00
|
|
|
builder = builder.with_children(&parse_moonwave_style_comment(&comment));
|
|
|
|
}
|
2023-02-22 11:35:49 +00:00
|
|
|
if let Some(args) = item
|
|
|
|
.type_info
|
|
|
|
.extract_args_normalized(&self.found_top_level_types)
|
|
|
|
{
|
2023-02-22 09:11:36 +00:00
|
|
|
builder = builder.with_arg_types(&args);
|
|
|
|
}
|
2023-02-22 10:31:58 +00:00
|
|
|
if let TypeInfo::Table { fields, .. } = item.type_info {
|
2023-02-21 20:06:11 +00:00
|
|
|
for field in fields.iter() {
|
|
|
|
if let TypeFieldKey::Name(name) = field.key() {
|
2023-02-22 10:31:58 +00:00
|
|
|
builder = builder.with_child(self.convert_parser_item_into_doc_item(
|
|
|
|
DefinitionsParserItem {
|
2023-02-22 09:48:04 +00:00
|
|
|
name: name.token().to_string(),
|
|
|
|
comment: find_token_moonwave_comment(name),
|
|
|
|
type_info: field.value().clone(),
|
2023-02-22 10:31:58 +00:00
|
|
|
},
|
|
|
|
));
|
2023-02-21 20:06:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
builder.build().unwrap()
|
|
|
|
}
|
|
|
|
|
2023-02-22 11:18:30 +00:00
|
|
|
/**
|
|
|
|
Converts currently stored parser items into definition items.
|
|
|
|
|
|
|
|
This will consume (drain) all stored parser items, leaving the parser empty.
|
|
|
|
*/
|
2023-02-22 10:31:58 +00:00
|
|
|
#[allow(clippy::unnecessary_wraps)]
|
2023-02-22 11:18:30 +00:00
|
|
|
pub fn drain(&mut self) -> Result<Vec<DefinitionsItem>> {
|
2023-02-22 11:35:49 +00:00
|
|
|
let mut results = Vec::new();
|
|
|
|
for top_level_item in self.found_top_level_items.values() {
|
|
|
|
results.push(self.convert_parser_item_into_doc_item(top_level_item.clone()));
|
|
|
|
}
|
|
|
|
self.found_top_level_items = BTreeMap::new();
|
2023-02-22 11:40:48 +00:00
|
|
|
self.found_top_level_types = HashMap::new();
|
2023-02-22 11:18:30 +00:00
|
|
|
self.found_top_level_declares = Vec::new();
|
2023-02-22 10:31:58 +00:00
|
|
|
Ok(results)
|
2023-02-21 20:06:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn find_token_moonwave_comment(token: &TokenReference) -> Option<String> {
|
|
|
|
token
|
|
|
|
.leading_trivia()
|
|
|
|
.filter_map(|trivia| match trivia.token_type() {
|
|
|
|
TokenType::MultiLineComment { blocks, comment } if blocks == &1 => Some(comment),
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.last()
|
|
|
|
.map(|comment| comment.trim().to_string())
|
|
|
|
}
|