Start generating documentation for built-in types and functions
This commit is contained in:
1
rust_compiler/Cargo.lock
generated
1
rust_compiler/Cargo.lock
generated
@@ -496,6 +496,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"lsp-types",
|
"lsp-types",
|
||||||
|
"pretty_assertions",
|
||||||
"quick-error",
|
"quick-error",
|
||||||
"tokenizer",
|
"tokenizer",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ lsp-types = { workspace = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = { version = "1" }
|
anyhow = { version = "1" }
|
||||||
|
pretty_assertions = "1.4"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
mod macros;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
@@ -14,6 +15,10 @@ use tokenizer::{
|
|||||||
};
|
};
|
||||||
use tree_node::*;
|
use tree_node::*;
|
||||||
|
|
||||||
|
pub trait Documentation {
|
||||||
|
fn docs(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
/// A macro to create a boxed value.
|
/// A macro to create a boxed value.
|
||||||
macro_rules! boxed {
|
macro_rules! boxed {
|
||||||
|
|||||||
85
rust_compiler/libs/parser/src/macros.rs
Normal file
85
rust_compiler/libs/parser/src/macros.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! documented {
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Internal Helper: Filter doc comments
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Case 1: Doc comment. Return Some("string").
|
||||||
|
// We match the specific structure of a doc attribute.
|
||||||
|
(@doc_filter #[doc = $doc:expr]) => {
|
||||||
|
Some($doc)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Case 2: Other attributes (derives, etc.). Return None.
|
||||||
|
// We catch any other token sequence inside the brackets.
|
||||||
|
(@doc_filter #[$($attr:tt)*]) => {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Internal Helper: Match patterns for `match self`
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
(@arm $name:ident $variant:ident) => {
|
||||||
|
$name::$variant
|
||||||
|
};
|
||||||
|
(@arm $name:ident $variant:ident ( $($tuple:tt)* )) => {
|
||||||
|
$name::$variant(..)
|
||||||
|
};
|
||||||
|
(@arm $name:ident $variant:ident { $($structure:tt)* }) => {
|
||||||
|
$name::$variant{..}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Main Macro Entry Point
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
(
|
||||||
|
$(#[$enum_attr:meta])* $vis:vis enum $name:ident {
|
||||||
|
$(
|
||||||
|
// Capture attributes as a sequence of token trees inside brackets
|
||||||
|
// to avoid "local ambiguity" and handle multi-token attributes (like doc="...").
|
||||||
|
$(#[ $($variant_attr:tt)* ])*
|
||||||
|
$variant:ident
|
||||||
|
$( ($($tuple:tt)*) )?
|
||||||
|
$( {$($structure:tt)*} )?
|
||||||
|
),* $(,)?
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
// 1. Generate the actual Enum definition
|
||||||
|
$(#[$enum_attr])*
|
||||||
|
$vis enum $name {
|
||||||
|
$(
|
||||||
|
$(#[ $($variant_attr)* ])*
|
||||||
|
$variant
|
||||||
|
$( ($($tuple)*) )?
|
||||||
|
$( {$($structure)*} )?,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Implement the Trait
|
||||||
|
impl Documentation for $name {
|
||||||
|
fn docs(&self) -> String {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
documented!(@arm $name $variant $( ($($tuple)*) )? $( {$($structure)*} )? ) => {
|
||||||
|
// Create a temporary array of Option<&str> for all attributes
|
||||||
|
let doc_lines: &[Option<&str>] = &[
|
||||||
|
$(
|
||||||
|
documented!(@doc_filter #[ $($variant_attr)* ])
|
||||||
|
),*
|
||||||
|
];
|
||||||
|
|
||||||
|
// Filter out the Nones (non-doc attributes), join, and return
|
||||||
|
doc_lines.iter()
|
||||||
|
.filter_map(|&d| d)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
.trim()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,73 +1,107 @@
|
|||||||
use crate::tree_node::{Expression, Literal, Spanned};
|
|
||||||
|
|
||||||
use super::LiteralOrVariable;
|
use super::LiteralOrVariable;
|
||||||
|
use crate::tree_node::{Expression, Literal, Spanned};
|
||||||
|
use crate::{Documentation, documented};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
documented! {
|
||||||
pub enum Math {
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
/// Returns the angle in radians whose cosine is the specified number.
|
pub enum Math {
|
||||||
/// ## In Game
|
/// Returns the angle in radians whose cosine is the specified number.
|
||||||
/// `acos r? a(r?|num)`
|
/// ## IC10
|
||||||
Acos(LiteralOrVariable),
|
/// `acos r? a(r?|num)`
|
||||||
/// Returns the angle in radians whose sine is the specified number.
|
/// ## Slang
|
||||||
/// ## In Game
|
/// `(number|var).acos();`
|
||||||
/// `asin r? a(r?|num)`
|
Acos(LiteralOrVariable),
|
||||||
Asin(LiteralOrVariable),
|
/// Returns the angle in radians whose sine is the specified number.
|
||||||
/// Returns the angle in radians whose tangent is the specified number.
|
/// ## IC10
|
||||||
/// ## In Game
|
/// `asin r? a(r?|num)`
|
||||||
/// `atan r? a(r?|num)`
|
/// ## Slang
|
||||||
Atan(LiteralOrVariable),
|
/// `(number|var).asin();`
|
||||||
/// Returns the angle in radians whose tangent is the quotient of the specified numbers.
|
Asin(LiteralOrVariable),
|
||||||
/// ## In Game
|
/// Returns the angle in radians whose tangent is the specified number.
|
||||||
/// `atan2 r? a(r?|num) b(r?|num)`
|
/// ## IC10
|
||||||
Atan2(LiteralOrVariable, LiteralOrVariable),
|
/// `atan r? a(r?|num)`
|
||||||
/// Gets the absolute value of a number.
|
/// ## Slang
|
||||||
/// ## In Game
|
/// `(number|var).atan();`
|
||||||
/// `abs r? a(r?|num)`
|
Atan(LiteralOrVariable),
|
||||||
Abs(LiteralOrVariable),
|
/// Returns the angle in radians whose tangent is the quotient of the specified numbers.
|
||||||
/// Rounds a number up to the nearest whole number.
|
/// ## IC10
|
||||||
/// ## In Game
|
/// `atan2 r? a(r?|num) b(r?|num)`
|
||||||
/// `ceil r? a(r?|num)`
|
/// ## Slang
|
||||||
Ceil(LiteralOrVariable),
|
/// `(number|var).atan2((number|var));`
|
||||||
/// Returns the cosine of the specified angle in radians.
|
Atan2(LiteralOrVariable, LiteralOrVariable),
|
||||||
/// ## In Game
|
/// Gets the absolute value of a number.
|
||||||
/// cos r? a(r?|num)
|
/// ## IC10
|
||||||
Cos(LiteralOrVariable),
|
/// `abs r? a(r?|num)`
|
||||||
/// Rounds a number down to the nearest whole number.
|
/// ## Slang
|
||||||
/// ## In Game
|
/// `(number|var).abs();`
|
||||||
/// `floor r? a(r?|num)`
|
Abs(LiteralOrVariable),
|
||||||
Floor(LiteralOrVariable),
|
/// Rounds a number up to the nearest whole number.
|
||||||
/// Computes the natural logarithm of a number.
|
/// ## IC10
|
||||||
/// ## In Game
|
/// `ceil r? a(r?|num)`
|
||||||
/// `log r? a(r?|num)`
|
/// ## Slang
|
||||||
Log(LiteralOrVariable),
|
/// `(number|var).ceil();`
|
||||||
/// Computes the maximum of two numbers.
|
Ceil(LiteralOrVariable),
|
||||||
/// ## In Game
|
/// Returns the cosine of the specified angle in radians.
|
||||||
/// `max r? a(r?|num) b(r?|num)`
|
/// ## IC10
|
||||||
Max(LiteralOrVariable, LiteralOrVariable),
|
/// `cos r? a(r?|num)`
|
||||||
/// Computes the minimum of two numbers.
|
/// ## Slang
|
||||||
/// ## In Game
|
/// `(number|var).cos();`
|
||||||
/// `min r? a(r?|num) b(r?|num)`
|
Cos(LiteralOrVariable),
|
||||||
Min(LiteralOrVariable, LiteralOrVariable),
|
/// Rounds a number down to the nearest whole number.
|
||||||
/// Gets a random number between 0 and 1.
|
/// ## In Game
|
||||||
/// ## In Game
|
/// `floor r? a(r?|num)`
|
||||||
/// `rand r?`
|
/// ## Slang
|
||||||
Rand,
|
/// `(number|var).floor();`
|
||||||
/// Returns the sine of the specified angle in radians.
|
Floor(LiteralOrVariable),
|
||||||
/// ## In Game
|
/// Computes the natural logarithm of a number.
|
||||||
/// `sin r? a(r?|num)`
|
/// ## IC10
|
||||||
Sin(LiteralOrVariable),
|
/// `log r? a(r?|num)`
|
||||||
/// Computes the square root of a number.
|
/// ## Slang
|
||||||
/// ## In Game
|
/// `(number|var).log();`
|
||||||
/// `sqrt r? a(r?|num)`
|
Log(LiteralOrVariable),
|
||||||
Sqrt(LiteralOrVariable),
|
/// Computes the maximum of two numbers.
|
||||||
/// Returns the tangent of the specified angle in radians.
|
/// ## IC10
|
||||||
/// ## In Game
|
/// `max r? a(r?|num) b(r?|num)`
|
||||||
/// `tan r? a(r?|num)`
|
/// ## Slang
|
||||||
Tan(LiteralOrVariable),
|
/// `(number|var).max((number|var));`
|
||||||
/// Truncates a number by removing the decimal portion.
|
Max(LiteralOrVariable, LiteralOrVariable),
|
||||||
/// ## In Game
|
/// Computes the minimum of two numbers.
|
||||||
/// `trunc r? a(r?|num)`
|
/// ## IC10
|
||||||
Trunc(LiteralOrVariable),
|
/// `min r? a(r?|num) b(r?|num)`
|
||||||
|
/// ## Slang
|
||||||
|
/// `(number|var).min((number|var));`
|
||||||
|
Min(LiteralOrVariable, LiteralOrVariable),
|
||||||
|
/// Gets a random number between 0 and 1.
|
||||||
|
/// ## IC10
|
||||||
|
/// `rand r?`
|
||||||
|
/// ## Slang
|
||||||
|
/// `rand();`
|
||||||
|
Rand,
|
||||||
|
/// Returns the sine of the specified angle in radians.
|
||||||
|
/// ## IC10
|
||||||
|
/// `sin r? a(r?|num)`
|
||||||
|
/// ## Slang
|
||||||
|
/// `(number|var).sin();`
|
||||||
|
Sin(LiteralOrVariable),
|
||||||
|
/// Computes the square root of a number.
|
||||||
|
/// ## IC10
|
||||||
|
/// `sqrt r? a(r?|num)`
|
||||||
|
/// ## Slang
|
||||||
|
/// `(number|var).sqrt();`
|
||||||
|
Sqrt(LiteralOrVariable),
|
||||||
|
/// Returns the tangent of the specified angle in radians.
|
||||||
|
/// ## IC10
|
||||||
|
/// `tan r? a(r?|num)`
|
||||||
|
/// ## Slang
|
||||||
|
/// `(number|var).tan();`
|
||||||
|
Tan(LiteralOrVariable),
|
||||||
|
/// Truncates a number by removing the decimal portion.
|
||||||
|
/// ## IC10
|
||||||
|
/// `trunc r? a(r?|num)`
|
||||||
|
/// ## Slang
|
||||||
|
/// `(number|var).trunc();`
|
||||||
|
Trunc(LiteralOrVariable),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Math {
|
impl std::fmt::Display for Math {
|
||||||
@@ -93,71 +127,73 @@ impl std::fmt::Display for Math {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
documented! {
|
||||||
pub enum System {
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
/// Pauses execution for exactly 1 tick and then resumes.
|
pub enum System {
|
||||||
/// ## In Game
|
/// Pauses execution for exactly 1 tick and then resumes.
|
||||||
/// yield
|
/// ## In Game
|
||||||
Yield,
|
/// yield
|
||||||
/// Represents a function that can be called to sleep for a certain amount of time.
|
Yield,
|
||||||
/// ## In Game
|
/// Represents a function that can be called to sleep for a certain amount of time.
|
||||||
/// `sleep a(r?|num)`
|
/// ## In Game
|
||||||
Sleep(Box<Spanned<Expression>>),
|
/// `sleep a(r?|num)`
|
||||||
/// Gets the in-game hash for a specific prefab name.
|
Sleep(Box<Spanned<Expression>>),
|
||||||
/// ## In Game
|
/// Gets the in-game hash for a specific prefab name.
|
||||||
/// `HASH("prefabName")`
|
/// ## In Game
|
||||||
Hash(Literal),
|
/// `HASH("prefabName")`
|
||||||
/// Represents a function which loads a device variable into a register.
|
Hash(Literal),
|
||||||
/// ## In Game
|
/// Represents a function which loads a device variable into a register.
|
||||||
/// `l r? d? var`
|
/// ## In Game
|
||||||
/// ## Examples
|
/// `l r? d? var`
|
||||||
/// `l r0 d0 Setting`
|
/// ## Examples
|
||||||
/// `l r1 d5 Pressure`
|
/// `l r0 d0 Setting`
|
||||||
LoadFromDevice(LiteralOrVariable, Literal),
|
/// `l r1 d5 Pressure`
|
||||||
/// Function which gets a LogicType from all connected network devices that match
|
LoadFromDevice(LiteralOrVariable, Literal),
|
||||||
/// the provided device hash and name, aggregating them via a batchMode
|
/// Function which gets a LogicType from all connected network devices that match
|
||||||
/// ## In Game
|
/// the provided device hash and name, aggregating them via a batchMode
|
||||||
/// lbn r? deviceHash nameHash logicType batchMode
|
/// ## In Game
|
||||||
/// ## Examples
|
/// lbn r? deviceHash nameHash logicType batchMode
|
||||||
/// lbn r0 HASH("StructureWallLight") HASH("wallLight") On Minimum
|
/// ## Examples
|
||||||
LoadBatchNamed(
|
/// lbn r0 HASH("StructureWallLight") HASH("wallLight") On Minimum
|
||||||
LiteralOrVariable,
|
LoadBatchNamed(
|
||||||
Box<Spanned<Expression>>,
|
LiteralOrVariable,
|
||||||
Literal,
|
Box<Spanned<Expression>>,
|
||||||
Literal,
|
Literal,
|
||||||
),
|
Literal,
|
||||||
/// Loads a LogicType from all connected network devices, aggregating them via a
|
),
|
||||||
/// batchMode
|
/// Loads a LogicType from all connected network devices, aggregating them via a
|
||||||
/// ## In Game
|
/// batchMode
|
||||||
/// lb r? deviceHash logicType batchMode
|
/// ## In Game
|
||||||
/// ## Examples
|
/// lb r? deviceHash logicType batchMode
|
||||||
/// lb r0 HASH("StructureWallLight") On Minimum
|
/// ## Examples
|
||||||
LoadBatch(LiteralOrVariable, Literal, Literal),
|
/// lb r0 HASH("StructureWallLight") On Minimum
|
||||||
/// Represents a function which stores a setting into a specific device.
|
LoadBatch(LiteralOrVariable, Literal, Literal),
|
||||||
/// ## In Game
|
/// Represents a function which stores a setting into a specific device.
|
||||||
/// `s d? logicType r?`
|
/// ## In Game
|
||||||
/// ## Example
|
/// `s d? logicType r?`
|
||||||
/// `s d0 Setting r0`
|
/// ## Example
|
||||||
SetOnDevice(LiteralOrVariable, Literal, Box<Spanned<Expression>>),
|
/// `s d0 Setting r0`
|
||||||
/// Represents a function which stores a setting to all devices that match
|
SetOnDevice(LiteralOrVariable, Literal, Box<Spanned<Expression>>),
|
||||||
/// the given deviceHash
|
/// Represents a function which stores a setting to all devices that match
|
||||||
/// ## In Game
|
/// the given deviceHash
|
||||||
/// `sb deviceHash logicType r?`
|
/// ## In Game
|
||||||
/// ## Example
|
/// `sb deviceHash logicType r?`
|
||||||
/// `sb HASH("Doors") Lock 1`
|
/// ## Example
|
||||||
SetOnDeviceBatched(LiteralOrVariable, Literal, Box<Spanned<Expression>>),
|
/// `sb HASH("Doors") Lock 1`
|
||||||
/// Represents a function which stores a setting to all devices that match
|
SetOnDeviceBatched(LiteralOrVariable, Literal, Box<Spanned<Expression>>),
|
||||||
/// both the given deviceHash AND the given nameHash
|
/// Represents a function which stores a setting to all devices that match
|
||||||
/// ## In Game
|
/// both the given deviceHash AND the given nameHash
|
||||||
/// `sbn deviceHash nameHash logicType r?`
|
/// ## In Game
|
||||||
/// ## Example
|
/// `sbn deviceHash nameHash logicType r?`
|
||||||
/// `sbn HASH("Doors") HASH("Exterior") Lock 1`
|
/// ## Example
|
||||||
SetOnDeviceBatchedNamed(
|
/// `sbn HASH("Doors") HASH("Exterior") Lock 1`
|
||||||
LiteralOrVariable,
|
SetOnDeviceBatchedNamed(
|
||||||
LiteralOrVariable,
|
LiteralOrVariable,
|
||||||
Literal,
|
LiteralOrVariable,
|
||||||
Box<Spanned<Expression>>,
|
Literal,
|
||||||
),
|
Box<Spanned<Expression>>,
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for System {
|
impl std::fmt::Display for System {
|
||||||
@@ -229,4 +265,3 @@ impl SysCall {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
rust_compiler/libs/parser/src/test/docs.rs
Normal file
12
rust_compiler/libs/parser/src/test/docs.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
use crate::Documentation;
|
||||||
|
use crate::sys_call;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_token_tree_docs() -> anyhow::Result<()> {
|
||||||
|
let syscall = sys_call::System::Yield;
|
||||||
|
|
||||||
|
assert_eq!(syscall.docs(), "");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -6,9 +6,11 @@ macro_rules! parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod blocks;
|
mod blocks;
|
||||||
|
mod docs;
|
||||||
use super::Parser;
|
use super::Parser;
|
||||||
use super::Tokenizer;
|
use super::Tokenizer;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unsupported_keywords() -> Result<()> {
|
fn test_unsupported_keywords() -> Result<()> {
|
||||||
@@ -99,16 +101,16 @@ fn test_priority_expression() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_binary_expression() -> Result<()> {
|
fn test_binary_expression() -> Result<()> {
|
||||||
let expr = parser!("4 ** 2 + 5 ** 2").parse()?.unwrap();
|
let expr = parser!("4 ** 2 + 5 ** 2;").parse()?.unwrap();
|
||||||
assert_eq!("((4 ** 2) + (5 ** 2))", expr.to_string());
|
assert_eq!("((4 ** 2) + (5 ** 2))", expr.to_string());
|
||||||
|
|
||||||
let expr = parser!("2 ** 3 ** 4").parse()?.unwrap();
|
let expr = parser!("2 ** 3 ** 4;").parse()?.unwrap();
|
||||||
assert_eq!("(2 ** (3 ** 4))", expr.to_string());
|
assert_eq!("(2 ** (3 ** 4))", expr.to_string());
|
||||||
|
|
||||||
let expr = parser!("45 * 2 - 15 / 5 + 5 ** 2").parse()?.unwrap();
|
let expr = parser!("45 * 2 - 15 / 5 + 5 ** 2;").parse()?.unwrap();
|
||||||
assert_eq!("(((45 * 2) - (15 / 5)) + (5 ** 2))", expr.to_string());
|
assert_eq!("(((45 * 2) - (15 / 5)) + (5 ** 2))", expr.to_string());
|
||||||
|
|
||||||
let expr = parser!("(5 - 2) * 10").parse()?.unwrap();
|
let expr = parser!("(5 - 2) * 10;").parse()?.unwrap();
|
||||||
assert_eq!("((5 - 2) * 10)", expr.to_string());
|
assert_eq!("((5 - 2) * 10)", expr.to_string());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user