diff --git a/rust_compiler/Cargo.lock b/rust_compiler/Cargo.lock index 0c3ea01..7a09c6c 100644 --- a/rust_compiler/Cargo.lock +++ b/rust_compiler/Cargo.lock @@ -360,6 +360,10 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "helpers" +version = "0.1.0" + [[package]] name = "indexmap" version = "2.12.1" @@ -495,6 +499,7 @@ name = "parser" version = "0.1.0" dependencies = [ "anyhow", + "helpers", "lsp-types", "pretty_assertions", "quick-error", @@ -829,6 +834,7 @@ dependencies = [ "anyhow", "clap", "compiler", + "helpers", "lsp-types", "parser", "quick-error", @@ -926,6 +932,7 @@ name = "tokenizer" version = "0.1.0" dependencies = [ "anyhow", + "helpers", "lsp-types", "quick-error", "rust_decimal", diff --git a/rust_compiler/Cargo.toml b/rust_compiler/Cargo.toml index 8286678..c0b0744 100644 --- a/rust_compiler/Cargo.toml +++ b/rust_compiler/Cargo.toml @@ -40,6 +40,7 @@ rust_decimal = { workspace = true } tokenizer = { path = "libs/tokenizer" } parser = { path = "libs/parser" } compiler = { path = "libs/compiler" } +helpers = { path = "libs/helpers" } safer-ffi = { workspace = true } [dev-dependencies] diff --git a/rust_compiler/libs/helpers/Cargo.toml b/rust_compiler/libs/helpers/Cargo.toml new file mode 100644 index 0000000..cb9df2c --- /dev/null +++ b/rust_compiler/libs/helpers/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "helpers" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/rust_compiler/libs/helpers/src/lib.rs b/rust_compiler/libs/helpers/src/lib.rs new file mode 100644 index 0000000..3060c3e --- /dev/null +++ b/rust_compiler/libs/helpers/src/lib.rs @@ -0,0 +1,12 @@ +mod macros; + +/// This trait will allow the LSP to emit documentation for various tokens and expressions. +/// You can easily create documentation for large enums with the `documented!` macro. +pub trait Documentation { + /// Retreive documentation for this specific item. + fn docs(&self) -> String; +} + +pub mod prelude { + pub use super::{Documentation, documented}; +} diff --git a/rust_compiler/libs/parser/src/macros.rs b/rust_compiler/libs/helpers/src/macros.rs similarity index 99% rename from rust_compiler/libs/parser/src/macros.rs rename to rust_compiler/libs/helpers/src/macros.rs index 6eefb02..822096b 100644 --- a/rust_compiler/libs/parser/src/macros.rs +++ b/rust_compiler/libs/helpers/src/macros.rs @@ -82,4 +82,3 @@ macro_rules! documented { } }; } - diff --git a/rust_compiler/libs/parser/Cargo.toml b/rust_compiler/libs/parser/Cargo.toml index 504d535..336b498 100644 --- a/rust_compiler/libs/parser/Cargo.toml +++ b/rust_compiler/libs/parser/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] quick-error = { workspace = true } tokenizer = { path = "../tokenizer" } +helpers = { path = "../helpers" } lsp-types = { workspace = true } diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index f58560f..f293a4d 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -1,4 +1,3 @@ -mod macros; #[cfg(test)] mod test; diff --git a/rust_compiler/libs/parser/src/sys_call.rs b/rust_compiler/libs/parser/src/sys_call.rs index ed9d2c9..9e217fc 100644 --- a/rust_compiler/libs/parser/src/sys_call.rs +++ b/rust_compiler/libs/parser/src/sys_call.rs @@ -1,6 +1,6 @@ use super::LiteralOrVariable; use crate::tree_node::{Expression, Literal, Spanned}; -use crate::{Documentation, documented}; +use helpers::prelude::*; documented! { #[derive(Debug, PartialEq, Eq)] @@ -48,7 +48,7 @@ documented! { /// `(number|var).cos();` Cos(LiteralOrVariable), /// Rounds a number down to the nearest whole number. - /// ## In Game + /// ## IC10 /// `floor r? a(r?|num)` /// ## Slang /// `(number|var).floor();` @@ -131,30 +131,35 @@ documented! { #[derive(Debug, PartialEq, Eq)] pub enum System { /// Pauses execution for exactly 1 tick and then resumes. - /// ## In Game - /// yield + /// ## IC10 + /// `yield` + /// ## Slang + /// `yield();` Yield, /// Represents a function that can be called to sleep for a certain amount of time. - /// ## In Game + /// ## IC10 /// `sleep a(r?|num)` + /// ## Slang + /// `sleep(number|var);` Sleep(Box>), /// Gets the in-game hash for a specific prefab name. - /// ## In Game + /// ## IC10 /// `HASH("prefabName")` + /// ## Slang + /// `HASH("prefabName");` Hash(Literal), /// Represents a function which loads a device variable into a register. - /// ## In Game + /// ## IC10 /// `l r? d? var` - /// ## Examples - /// `l r0 d0 Setting` - /// `l r1 d5 Pressure` + /// ## Slang + /// `loadFromDevice(deviceType, "LogicType");` LoadFromDevice(LiteralOrVariable, Literal), /// Function which gets a LogicType from all connected network devices that match /// the provided device hash and name, aggregating them via a batchMode - /// ## In Game - /// lbn r? deviceHash nameHash logicType batchMode - /// ## Examples - /// lbn r0 HASH("StructureWallLight") HASH("wallLight") On Minimum + /// ## IC10 + /// `lbn r? deviceHash nameHash logicType batchMode` + /// ## Slang + /// `loadFromDeviceBatchedNamed(deviceHash, deviceName, "LogicType", "BatchMode");` LoadBatchNamed( LiteralOrVariable, Box>, @@ -163,30 +168,28 @@ documented! { ), /// Loads a LogicType from all connected network devices, aggregating them via a /// batchMode - /// ## In Game - /// lb r? deviceHash logicType batchMode - /// ## Examples - /// lb r0 HASH("StructureWallLight") On Minimum + /// ## IC10 + /// `lb r? deviceHash logicType batchMode` + /// ## Slang + /// `loadFromDeviceBatched(deviceHash, "Variable", "LogicType");` LoadBatch(LiteralOrVariable, Literal, Literal), /// Represents a function which stores a setting into a specific device. - /// ## In Game + /// ## IC10 /// `s d? logicType r?` - /// ## Example - /// `s d0 Setting r0` + /// ## Slang + /// `setOnDevice(deviceType, "Variable", (number|var));` SetOnDevice(LiteralOrVariable, Literal, Box>), /// Represents a function which stores a setting to all devices that match /// the given deviceHash - /// ## In Game + /// ## IC10 /// `sb deviceHash logicType r?` - /// ## Example - /// `sb HASH("Doors") Lock 1` SetOnDeviceBatched(LiteralOrVariable, Literal, Box>), /// Represents a function which stores a setting to all devices that match /// both the given deviceHash AND the given nameHash - /// ## In Game + /// ## IC10 /// `sbn deviceHash nameHash logicType r?` - /// ## Example - /// `sbn HASH("Doors") HASH("Exterior") Lock 1` + /// ## Slang + /// `setOnDeviceBatchedNamed(deviceType, nameHash, "LogicType", (number|var))` SetOnDeviceBatchedNamed( LiteralOrVariable, LiteralOrVariable, @@ -226,6 +229,15 @@ pub enum SysCall { Math(Math), } +impl Documentation for SysCall { + fn docs(&self) -> String { + match self { + Self::System(s) => s.docs(), + Self::Math(m) => m.docs(), + } + } +} + impl std::fmt::Display for SysCall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/rust_compiler/libs/parser/src/test/docs.rs b/rust_compiler/libs/parser/src/test/docs.rs index 9a442f9..28aae6b 100644 --- a/rust_compiler/libs/parser/src/test/docs.rs +++ b/rust_compiler/libs/parser/src/test/docs.rs @@ -1,5 +1,5 @@ -use crate::Documentation; use crate::sys_call; +use helpers::Documentation; use pretty_assertions::assert_eq; #[test] diff --git a/rust_compiler/libs/tokenizer/Cargo.toml b/rust_compiler/libs/tokenizer/Cargo.toml index 38032f4..7433cab 100644 --- a/rust_compiler/libs/tokenizer/Cargo.toml +++ b/rust_compiler/libs/tokenizer/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" rust_decimal = { workspace = true } quick-error = { workspace = true } lsp-types = { workspace = true } +helpers = { path = "../helpers" } [dev-dependencies] anyhow = { version = "^1" } diff --git a/rust_compiler/libs/tokenizer/src/token.rs b/rust_compiler/libs/tokenizer/src/token.rs index b471d6d..5938ddc 100644 --- a/rust_compiler/libs/tokenizer/src/token.rs +++ b/rust_compiler/libs/tokenizer/src/token.rs @@ -1,3 +1,4 @@ +use helpers::prelude::*; use rust_decimal::Decimal; #[derive(Debug, PartialEq, Eq, Clone)] @@ -264,28 +265,130 @@ impl std::fmt::Display for Symbol { } } -#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)] -pub enum Keyword { - /// Represents the `continue` keyword - Continue, - /// Represents the `let` keyword - Let, - /// Represents the `fn` keyword - Fn, - /// Represents the `if` keyword - If, - /// Represents the `device` keyword. Useful for defining a device at a specific address (ex. d0, d1, d2, etc.) - Device, - /// Represents the `else` keyword - Else, - /// Represents the `return` keyword - Return, - /// Represents the `enum` keyword - Enum, - /// Represents the `loop` keyword - Loop, - /// Represents the `break` keyword - Break, - /// Represents the `while` keyword - While, +documented! { + #[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)] + pub enum Keyword { + /// Represents the `continue` keyword. This will allow you to bypass the current iteration in a loop and start the next one. + /// ## Example + /// ``` + /// let item = 0; + /// loop { + /// if (item % 2 == 0) { + /// // This will NOT increment `item` and will continue with the next iteration of the + /// // loop + /// continue; + /// } + /// item = item + 1; + /// } + /// ``` + Continue, + /// Represents the `let` keyword, used to declare variables within Slang. + /// ## Example + /// ``` + /// // This variable now exists either in a register or the stack depending on how many + /// // free registers were available when declaring it. + /// let item = 0; + /// ``` + Let, + /// Represents the `fn` keyword, used to declare functions within Slang. + /// ## Example + /// ``` + /// // This allows you to now call `doSomething` with specific arguments. + /// fn doSomething(arg1, arg2) { + /// + /// } + /// ``` + Fn, + /// Represents the `if` keyword, allowing you to create branched logic. + /// ## Example + /// ``` + /// let i = 0; + /// if (i == 0) { + /// i = 1; + /// } + /// // At this line, `i` is now `1` + /// ``` + If, + /// Represents the `device` keyword. Useful for defining a device at a specific address + /// (ex. d0, d1, d2, etc.). This also allows you to perform direct operations ON a device. + /// ## Example + /// ``` + /// device self = "db"; + /// + /// // This is the same as `s db Setting 123` + /// self.Setting = 123; + /// ``` + Device, + /// Represents the `else` keyword. Useful if you want to check a condition but run run + /// seperate logic in case that condition fails. + /// ## Example + /// ``` + /// device self = "db"; + /// let i = 0; + /// if (i < 0) { + /// self.Setting = 0; + /// } else { + /// self.Setting = 1; + /// } + /// // Here, the `Setting` on the current housing is `1` because i was NOT less than 0 + /// ``` + Else, + /// Represents the `return` keyword. Allows you to pass values from a function back to + /// the caller. + /// ## Example + /// ``` + /// fn doSomething() { + /// return 1 + 2; + /// } + /// + /// // `returnedValue` now holds the value `3` + /// let returnedValue = doSomething(); + /// ``` + Return, + /// Represents the `enum` keyword. This is currently not supported, but is kept as a + /// reserved keyword in the future case that this is implemented. + Enum, + /// Represents the `loop` keyword. This allows you to create an infinate loop, but can be + /// broken with the `break` keyword. + /// ## Example + /// ``` + /// device self = "db"; + /// let i = 0; + /// loop { + /// i = i + 1; + /// // The current housing will infinately increment it's `Setting` value. + /// self.Setting = i; + /// } + /// ``` + Loop, + /// Represents the `break` keyword. This allows you to "break out of" a loop prematurely, + /// such as when an if() conditon is true, etc. + /// ## Example + /// ``` + /// let i = 0; + /// // This loop will run until the value of `i` is greater than 10,000, + /// // which will then trigger the `break` keyword and it will stop looping + /// loop { + /// if (i > 10_000) { + /// break; + /// } + /// i = i + 1; + /// } + /// ``` + Break, + /// Represents the `while` keyword. This is similar to the `loop` keyword but different in + /// that you don't need an `if` statement to break out of a loop, that is handled + /// automatically when invoking `while` + /// ## Example + /// ``` + /// let i = 0; + /// // This loop will run until the value of `i` is greater than 10,000, in which case the + /// // while loop will automatically stop running and code will continue AFTER the last + /// // bracket. + /// while (i < 10_000) { + /// i = i + 1; + /// } + /// ``` + While, + } }