mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Implement most of the selene typedefs generation
This commit is contained in:
parent
c252db4598
commit
f4c55ff2cf
6 changed files with 172 additions and 26 deletions
|
@ -305,7 +305,7 @@ declare process: {
|
|||
cwd: string,
|
||||
--[=[
|
||||
@within process
|
||||
@new_fields
|
||||
@read_write
|
||||
|
||||
Current environment variables for this process.
|
||||
|
||||
|
|
|
@ -71,6 +71,10 @@ impl DocItem {
|
|||
self.kind.is_tag()
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> DocItemKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> Option<&str> {
|
||||
self.name.as_deref()
|
||||
}
|
||||
|
@ -82,12 +86,8 @@ impl DocItem {
|
|||
pub fn get_value(&self) -> Option<&str> {
|
||||
self.value.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for DocItem {
|
||||
type Item = DocItem;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.children.into_iter()
|
||||
pub fn children(&self) -> &[DocItem] {
|
||||
&self.children
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use regex::Regex;
|
|||
|
||||
use super::{builder::DocItemBuilder, item::DocItem, kind::DocItemKind};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DocVisitorItem {
|
||||
name: String,
|
||||
comment: Option<String>,
|
||||
|
@ -20,9 +21,9 @@ struct DocVisitorItem {
|
|||
impl From<DocVisitorItem> for DocItem {
|
||||
fn from(value: DocVisitorItem) -> Self {
|
||||
let mut builder = DocItemBuilder::new()
|
||||
.with_kind(match value.type_info {
|
||||
.with_kind(match &value.type_info {
|
||||
TypeInfo::Array { .. } | TypeInfo::Table { .. } => DocItemKind::Table,
|
||||
TypeInfo::Callback { .. } => DocItemKind::Function,
|
||||
typ if type_info_is_fn(typ) => DocItemKind::Function,
|
||||
_ => unimplemented!("Support for globals that are not properties or functions is not yet implemented")
|
||||
})
|
||||
.with_name(value.name);
|
||||
|
@ -39,7 +40,7 @@ impl From<DocVisitorItem> for DocItem {
|
|||
builder = builder.with_child(
|
||||
DocItemBuilder::new()
|
||||
.with_kind(match field.value() {
|
||||
TypeInfo::Callback { .. } => DocItemKind::Function,
|
||||
typ if type_info_is_fn(typ) => DocItemKind::Function,
|
||||
_ => DocItemKind::Property,
|
||||
})
|
||||
.with_name(name.token().to_string())
|
||||
|
@ -84,6 +85,18 @@ where
|
|||
.collect())
|
||||
}
|
||||
|
||||
fn type_info_is_fn(typ: &TypeInfo) -> bool {
|
||||
match typ {
|
||||
TypeInfo::Callback { .. } => true,
|
||||
TypeInfo::Tuple { types, .. } => types.iter().all(type_info_is_fn),
|
||||
TypeInfo::Union { left, right, .. } => type_info_is_fn(left) && type_info_is_fn(right),
|
||||
TypeInfo::Intersection { left, right, .. } => {
|
||||
type_info_is_fn(left) && type_info_is_fn(right)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn should_separate_tag_meta(tag_kind: &str) -> bool {
|
||||
matches!(tag_kind.trim().to_ascii_lowercase().as_ref(), "param")
|
||||
}
|
||||
|
|
|
@ -12,12 +12,43 @@ pub enum DocsItemTag {
|
|||
Return(String),
|
||||
MustUse,
|
||||
ReadOnly,
|
||||
NewFields,
|
||||
ReadWrite,
|
||||
}
|
||||
|
||||
impl TryFrom<DocItem> for DocsItemTag {
|
||||
#[allow(dead_code)]
|
||||
impl DocsItemTag {
|
||||
pub fn is_class(&self) -> bool {
|
||||
matches!(self, Self::Class(_))
|
||||
}
|
||||
|
||||
pub fn is_within(&self) -> bool {
|
||||
matches!(self, Self::Within(_))
|
||||
}
|
||||
|
||||
pub fn is_param(&self) -> bool {
|
||||
matches!(self, Self::Param(_))
|
||||
}
|
||||
|
||||
pub fn is_return(&self) -> bool {
|
||||
matches!(self, Self::Return(_))
|
||||
}
|
||||
|
||||
pub fn is_must_use(&self) -> bool {
|
||||
self == &Self::MustUse
|
||||
}
|
||||
|
||||
pub fn is_read_only(&self) -> bool {
|
||||
self == &Self::ReadOnly
|
||||
}
|
||||
|
||||
pub fn is_read_write(&self) -> bool {
|
||||
self == &Self::ReadWrite
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&DocItem> for DocsItemTag {
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(value: DocItem) -> Result<Self> {
|
||||
fn try_from(value: &DocItem) -> Result<Self> {
|
||||
if let Some(name) = value.get_name() {
|
||||
Ok(match name.trim().to_ascii_lowercase().as_ref() {
|
||||
"class" => Self::Class(
|
||||
|
@ -50,7 +81,7 @@ impl TryFrom<DocItem> for DocsItemTag {
|
|||
),
|
||||
"must_use" => Self::MustUse,
|
||||
"read_only" => Self::ReadOnly,
|
||||
"new_fields" => Self::NewFields,
|
||||
"read_write" => Self::ReadWrite,
|
||||
s => bail!("Unknown docs tag: '{}'", s),
|
||||
})
|
||||
} else {
|
||||
|
|
|
@ -46,11 +46,3 @@ impl DerefMut for DocTree {
|
|||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for DocTree {
|
||||
type Item = DocItem;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,119 @@
|
|||
use anyhow::{Context, Result};
|
||||
use serde_yaml::Value as YamlValue;
|
||||
use serde_yaml::{Mapping as YamlMapping, Sequence as YamlSequence, Value as YamlValue};
|
||||
|
||||
use super::doc::DocumentationVisitor;
|
||||
use crate::gen::doc2::DocsItemTag;
|
||||
|
||||
use super::doc2::{DocItem, DocItemKind, DocTree};
|
||||
|
||||
pub fn generate_from_type_definitions(contents: &str) -> Result<String> {
|
||||
let _visitor = DocumentationVisitor::from_definitions(contents)?;
|
||||
serde_yaml::to_string(&YamlValue::Null).context("Failed to encode docs as json")
|
||||
let tree = DocTree::from_type_definitions(contents)?;
|
||||
let mut globals = YamlMapping::new();
|
||||
let top_level_items = tree.children().iter().filter(|top_level| {
|
||||
top_level.is_function()
|
||||
|| top_level.children().iter().any(|top_level_child| {
|
||||
top_level_child.is_tag() && top_level_child.get_name().unwrap() == "class"
|
||||
})
|
||||
});
|
||||
for top_level_item in top_level_items {
|
||||
match top_level_item.kind() {
|
||||
DocItemKind::Table => {
|
||||
let top_level_name = top_level_item
|
||||
.get_name()
|
||||
.context("Missing name for top-level doc item")?
|
||||
.to_string();
|
||||
for child_item in top_level_item
|
||||
.children()
|
||||
.iter()
|
||||
.filter(|item| item.is_function() || item.is_property())
|
||||
{
|
||||
let child_name = child_item
|
||||
.get_name()
|
||||
.context("Missing name for top-level child doc item")?
|
||||
.to_string();
|
||||
globals.insert(
|
||||
YamlValue::String(format!("{top_level_name}.{child_name}")),
|
||||
YamlValue::Mapping(doc_item_to_selene_yaml_mapping(child_item)?),
|
||||
);
|
||||
}
|
||||
}
|
||||
DocItemKind::Function => {
|
||||
globals.insert(
|
||||
YamlValue::String(
|
||||
top_level_item
|
||||
.get_name()
|
||||
.context("Missing name for top-level doc item")?
|
||||
.to_string(),
|
||||
),
|
||||
YamlValue::Mapping(doc_item_to_selene_yaml_mapping(top_level_item)?),
|
||||
);
|
||||
}
|
||||
_ => unimplemented!("Globals other than tables and functions are not yet implemented"),
|
||||
}
|
||||
}
|
||||
let mut contents = YamlMapping::new();
|
||||
contents.insert(
|
||||
YamlValue::String("globals".to_string()),
|
||||
YamlValue::Mapping(globals),
|
||||
);
|
||||
Ok(format!(
|
||||
"# Lune v{}\n---\n{}",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
serde_yaml::to_string(&contents).context("Failed to encode type definitions as yaml")?
|
||||
))
|
||||
}
|
||||
|
||||
fn doc_item_to_selene_yaml_mapping(item: &DocItem) -> Result<YamlMapping> {
|
||||
let mut mapping = YamlMapping::new();
|
||||
if item.is_property() {
|
||||
let property_access_tag = item
|
||||
.children()
|
||||
.iter()
|
||||
.find_map(|child| {
|
||||
if let Ok(tag) = DocsItemTag::try_from(child) {
|
||||
if tag.is_read_only() || tag.is_read_write() {
|
||||
Some(tag)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Missing property access tag for doc item:\n{}",
|
||||
item.get_name().unwrap()
|
||||
)
|
||||
})?;
|
||||
mapping.insert(
|
||||
YamlValue::String("property".to_string()),
|
||||
YamlValue::String(
|
||||
match property_access_tag {
|
||||
DocsItemTag::ReadOnly => "read-only",
|
||||
DocsItemTag::ReadWrite => "new-fields",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.to_string(),
|
||||
),
|
||||
);
|
||||
} else if item.is_function() {
|
||||
let is_must_use = item.children().iter().any(|child| {
|
||||
if let Ok(tag) = DocsItemTag::try_from(child) {
|
||||
tag.is_must_use()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
if is_must_use {
|
||||
mapping.insert(
|
||||
YamlValue::String("must_use".to_string()),
|
||||
YamlValue::Bool(true),
|
||||
);
|
||||
}
|
||||
mapping.insert(
|
||||
YamlValue::String("args".to_string()),
|
||||
YamlValue::Mapping(YamlMapping::new()),
|
||||
);
|
||||
}
|
||||
Ok(mapping)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue