mirror of
https://github.com/lune-org/lune.git
synced 2025-01-05 19:09:10 +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,
|
cwd: string,
|
||||||
--[=[
|
--[=[
|
||||||
@within process
|
@within process
|
||||||
@new_fields
|
@read_write
|
||||||
|
|
||||||
Current environment variables for this process.
|
Current environment variables for this process.
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,10 @@ impl DocItem {
|
||||||
self.kind.is_tag()
|
self.kind.is_tag()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> DocItemKind {
|
||||||
|
self.kind
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_name(&self) -> Option<&str> {
|
pub fn get_name(&self) -> Option<&str> {
|
||||||
self.name.as_deref()
|
self.name.as_deref()
|
||||||
}
|
}
|
||||||
|
@ -82,12 +86,8 @@ impl DocItem {
|
||||||
pub fn get_value(&self) -> Option<&str> {
|
pub fn get_value(&self) -> Option<&str> {
|
||||||
self.value.as_deref()
|
self.value.as_deref()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for DocItem {
|
pub fn children(&self) -> &[DocItem] {
|
||||||
type Item = DocItem;
|
&self.children
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.children.into_iter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use regex::Regex;
|
||||||
|
|
||||||
use super::{builder::DocItemBuilder, item::DocItem, kind::DocItemKind};
|
use super::{builder::DocItemBuilder, item::DocItem, kind::DocItemKind};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
struct DocVisitorItem {
|
struct DocVisitorItem {
|
||||||
name: String,
|
name: String,
|
||||||
comment: Option<String>,
|
comment: Option<String>,
|
||||||
|
@ -20,9 +21,9 @@ struct DocVisitorItem {
|
||||||
impl From<DocVisitorItem> for DocItem {
|
impl From<DocVisitorItem> for DocItem {
|
||||||
fn from(value: DocVisitorItem) -> Self {
|
fn from(value: DocVisitorItem) -> Self {
|
||||||
let mut builder = DocItemBuilder::new()
|
let mut builder = DocItemBuilder::new()
|
||||||
.with_kind(match value.type_info {
|
.with_kind(match &value.type_info {
|
||||||
TypeInfo::Array { .. } | TypeInfo::Table { .. } => DocItemKind::Table,
|
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")
|
_ => unimplemented!("Support for globals that are not properties or functions is not yet implemented")
|
||||||
})
|
})
|
||||||
.with_name(value.name);
|
.with_name(value.name);
|
||||||
|
@ -39,7 +40,7 @@ impl From<DocVisitorItem> for DocItem {
|
||||||
builder = builder.with_child(
|
builder = builder.with_child(
|
||||||
DocItemBuilder::new()
|
DocItemBuilder::new()
|
||||||
.with_kind(match field.value() {
|
.with_kind(match field.value() {
|
||||||
TypeInfo::Callback { .. } => DocItemKind::Function,
|
typ if type_info_is_fn(typ) => DocItemKind::Function,
|
||||||
_ => DocItemKind::Property,
|
_ => DocItemKind::Property,
|
||||||
})
|
})
|
||||||
.with_name(name.token().to_string())
|
.with_name(name.token().to_string())
|
||||||
|
@ -84,6 +85,18 @@ where
|
||||||
.collect())
|
.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 {
|
fn should_separate_tag_meta(tag_kind: &str) -> bool {
|
||||||
matches!(tag_kind.trim().to_ascii_lowercase().as_ref(), "param")
|
matches!(tag_kind.trim().to_ascii_lowercase().as_ref(), "param")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,43 @@ pub enum DocsItemTag {
|
||||||
Return(String),
|
Return(String),
|
||||||
MustUse,
|
MustUse,
|
||||||
ReadOnly,
|
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;
|
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() {
|
if let Some(name) = value.get_name() {
|
||||||
Ok(match name.trim().to_ascii_lowercase().as_ref() {
|
Ok(match name.trim().to_ascii_lowercase().as_ref() {
|
||||||
"class" => Self::Class(
|
"class" => Self::Class(
|
||||||
|
@ -50,7 +81,7 @@ impl TryFrom<DocItem> for DocsItemTag {
|
||||||
),
|
),
|
||||||
"must_use" => Self::MustUse,
|
"must_use" => Self::MustUse,
|
||||||
"read_only" => Self::ReadOnly,
|
"read_only" => Self::ReadOnly,
|
||||||
"new_fields" => Self::NewFields,
|
"read_write" => Self::ReadWrite,
|
||||||
s => bail!("Unknown docs tag: '{}'", s),
|
s => bail!("Unknown docs tag: '{}'", s),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -46,11 +46,3 @@ impl DerefMut for DocTree {
|
||||||
&mut self.0
|
&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 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> {
|
pub fn generate_from_type_definitions(contents: &str) -> Result<String> {
|
||||||
let _visitor = DocumentationVisitor::from_definitions(contents)?;
|
let tree = DocTree::from_type_definitions(contents)?;
|
||||||
serde_yaml::to_string(&YamlValue::Null).context("Failed to encode docs as json")
|
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