Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support CfxLua #338

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@ jobs:
run: |
cd full-moon
cargo test --features luajit
- name: Test (CfxLua feature)
run: |
cd full-moon
cargo test --features cfxlua
- name: Test (all features)
run: |
cd full-moon
cargo test --features luau,lua52,lua53,lua54,luajit
cargo test --features luau,lua52,lua53,lua54,luajit,cfxlua
- name: Test (no default features)
run: |
cd full-moon
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"rust-analyzer.cargo.features": ["luau", "lua52", "lua53", "lua54", "luau"]
"rust-analyzer.cargo.features": ["luau", "lua52", "lua53", "lua54", "luau", "cfxlua"]
}
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
- Implemented CFX Lua as a feature flag - `cfxlua`
- Compound Operators: `+=, -=, *=, /=, <<=, >>=, &=, |=, and ^=`
- Safe Navigation e.g. `t?.x?.y == nil`
- In Unpacking e.g. `local a, b, c = t` is equivalent to `local a, b, c = t.a, t.b, t.c`
- Set Constructors e.g. `t = { .x, .y }` is equivalent to `t = { x = true, y = true }`
- C-Style Comments (single & multiline) e.g. `/* comment */`
- Compile Time Jenkins' Hashes e.g. ``` `Hello, World!` -> 1395890823```

## [1.2.0] - 2025-01-09

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</h1>
</div>

A lossless Lua 5.1 / 5.2 / 5.3 / 5.4 / [Luau](https://luau-lang.org/) parser written in Rust.
A lossless Lua 5.1 / 5.2 / 5.3 / 5.4 / [Luau](https://luau-lang.org/) / [CfxLua](https://docs.fivem.net/docs/scripting-manual/runtimes/lua/) parser written in Rust.


## Lossless?
Expand Down
7 changes: 4 additions & 3 deletions full-moon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ license = "MPL-2.0"
readme = "README.md"
repository = "https://github.com/Kampfkarren/full-moon"
categories = ["parsing"]
keywords = ["lua", "parser", "lua51", "lua52", "luau"]
keywords = ["lua", "parser", "lua51", "lua52", "luau", "cfxlua"]
edition = "2021"

[package.metadata.docs.rs]
# Build Locally: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --features luau,lua52,lua53,lua54,luajit --no-deps --open
features = ["luau", "lua52", "lua53", "lua54", "luajit"]
# Build Locally: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --features luau,lua52,lua53,lua54,luajit,cfxlua --no-deps --open
features = ["luau", "lua52", "lua53", "lua54", "luajit", "cfxlua"]
rustdoc-args = ["--cfg", "docsrs"]

[features]
Expand All @@ -23,6 +23,7 @@ lua52 = []
lua53 = ["lua52"]
lua54 = ["lua53"]
luajit = []
cfxlua = ["lua54"]
no-source-tests = []

[dependencies]
Expand Down
140 changes: 140 additions & 0 deletions full-moon/src/ast/compound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use crate::ast::{Expression, Var};
use crate::tokenizer::{Symbol, TokenReference};
use derive_more::Display;
use full_moon_derive::{Node, Visit};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Display, PartialEq, Eq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
#[allow(missing_docs)]
#[display("{_0}")]
/// Compound operators, such as X += Y or X -= Y
pub enum CompoundOp {
PlusEqual(TokenReference),
MinusEqual(TokenReference),
StarEqual(TokenReference),
SlashEqual(TokenReference),
CaretEqual(TokenReference),

// luau sepcific
DoubleSlashEqual(TokenReference),
PercentEqual(TokenReference),
TwoDotsEqual(TokenReference),

// cfxlua sepcific
LeftShift(TokenReference),
RightShift(TokenReference),
BitwiseAndAssignment(TokenReference),
BitwiseOrAssignment(TokenReference),
}

impl CompoundOp {
/// The token associated with the operator
pub fn token(&self) -> &TokenReference {
match self {
Self::PlusEqual(token)
| Self::MinusEqual(token)
| Self::StarEqual(token)
| Self::SlashEqual(token)
| Self::DoubleSlashEqual(token)
| Self::PercentEqual(token)
| Self::CaretEqual(token)
| Self::TwoDotsEqual(token)
| Self::LeftShift(token)
| Self::RightShift(token)
| Self::BitwiseAndAssignment(token)
| Self::BitwiseOrAssignment(token) => token,
}
}

pub(crate) fn from_token(token: TokenReference) -> Self {
if token.is_symbol(Symbol::PlusEqual) {
return Self::PlusEqual(token);
} else if token.is_symbol(Symbol::MinusEqual) {
return Self::MinusEqual(token);
} else if token.is_symbol(Symbol::StarEqual) {
return Self::StarEqual(token);
} else if token.is_symbol(Symbol::SlashEqual) {
return Self::SlashEqual(token);
} else if token.is_symbol(Symbol::CaretEqual) {
return Self::CaretEqual(token);
}

#[cfg(feature = "luau")]
if token.is_symbol(Symbol::DoubleSlashEqual) {
return Self::DoubleSlashEqual(token);
} else if token.is_symbol(Symbol::PercentEqual) {
return Self::PercentEqual(token);
} else if token.is_symbol(Symbol::TwoDotsEqual) {
return Self::TwoDotsEqual(token);
}

#[cfg(feature = "cfxlua")]
if token.is_symbol(Symbol::LeftShift) {
return Self::LeftShift(token);
} else if token.is_symbol(Symbol::RightShift) {
return Self::RightShift(token);
} else if token.is_symbol(Symbol::BitwiseAndAssignment) {
return Self::BitwiseAndAssignment(token);
} else if token.is_symbol(Symbol::BitwiseOrAssignment) {
return Self::BitwiseOrAssignment(token);
}

unreachable!("converting an unknown token into a compound operator")
}
}

/// A Compound Assignment statement, such as `x += 1` or `x -= 1`
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{lhs}{compound_operator}{rhs}")]
pub struct CompoundAssignment {
pub(crate) lhs: Var,
pub(crate) compound_operator: CompoundOp,
pub(crate) rhs: Expression,
}

impl CompoundAssignment {
/// Creates a new CompoundAssignment from the left and right hand side
pub fn new(lhs: Var, compound_operator: CompoundOp, rhs: Expression) -> Self {
Self {
lhs,
compound_operator,
rhs,
}
}

/// The variable assigned to, the `x` part of `x += 1`
pub fn lhs(&self) -> &Var {
&self.lhs
}

/// The operator used, the `+=` part of `x += 1`
pub fn compound_operator(&self) -> &CompoundOp {
&self.compound_operator
}

/// The value being assigned, the `1` part of `x += 1`
pub fn rhs(&self) -> &Expression {
&self.rhs
}

/// Returns a new CompoundAssignment with the given variable being assigned to
pub fn with_lhs(self, lhs: Var) -> Self {
Self { lhs, ..self }
}

/// Returns a new CompoundAssignment with the given operator used
pub fn with_compound_operator(self, compound_operator: CompoundOp) -> Self {
Self {
compound_operator,
..self
}
}

/// Returns a new CompoundAssignment with the given value being assigned
pub fn with_rhs(self, rhs: Expression) -> Self {
Self { rhs, ..self }
}
}
111 changes: 3 additions & 108 deletions full-moon/src/ast/luau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,114 +893,9 @@ impl ExportedTypeFunction {
}
}

#[derive(Clone, Debug, Display, PartialEq, Eq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
#[allow(missing_docs)]
#[display("{_0}")]
/// Compound operators, such as X += Y or X -= Y
pub enum CompoundOp {
PlusEqual(TokenReference),
MinusEqual(TokenReference),
StarEqual(TokenReference),
SlashEqual(TokenReference),
DoubleSlashEqual(TokenReference),
PercentEqual(TokenReference),
CaretEqual(TokenReference),
TwoDotsEqual(TokenReference),
}

impl CompoundOp {
/// The token associated with the operator
pub fn token(&self) -> &TokenReference {
match self {
Self::PlusEqual(token)
| Self::MinusEqual(token)
| Self::StarEqual(token)
| Self::SlashEqual(token)
| Self::DoubleSlashEqual(token)
| Self::PercentEqual(token)
| Self::CaretEqual(token)
| Self::TwoDotsEqual(token) => token,
}
}

pub(crate) fn from_token(token: TokenReference) -> Self {
if token.is_symbol(Symbol::PlusEqual) {
Self::PlusEqual(token)
} else if token.is_symbol(Symbol::MinusEqual) {
Self::MinusEqual(token)
} else if token.is_symbol(Symbol::StarEqual) {
Self::StarEqual(token)
} else if token.is_symbol(Symbol::SlashEqual) {
Self::SlashEqual(token)
} else if token.is_symbol(Symbol::DoubleSlashEqual) {
Self::DoubleSlashEqual(token)
} else if token.is_symbol(Symbol::PercentEqual) {
Self::PercentEqual(token)
} else if token.is_symbol(Symbol::CaretEqual) {
Self::CaretEqual(token)
} else if token.is_symbol(Symbol::TwoDotsEqual) {
Self::TwoDotsEqual(token)
} else {
unreachable!("converting an unknown token into a compound operator")
}
}
}

/// A Compound Assignment statement, such as `x += 1` or `x -= 1`
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[display("{lhs}{compound_operator}{rhs}")]
pub struct CompoundAssignment {
pub(crate) lhs: Var,
pub(crate) compound_operator: CompoundOp,
pub(crate) rhs: Expression,
}

impl CompoundAssignment {
/// Creates a new CompoundAssignment from the left and right hand side
pub fn new(lhs: Var, compound_operator: CompoundOp, rhs: Expression) -> Self {
Self {
lhs,
compound_operator,
rhs,
}
}

/// The variable assigned to, the `x` part of `x += 1`
pub fn lhs(&self) -> &Var {
&self.lhs
}

/// The operator used, the `+=` part of `x += 1`
pub fn compound_operator(&self) -> &CompoundOp {
&self.compound_operator
}

/// The value being assigned, the `1` part of `x += 1`
pub fn rhs(&self) -> &Expression {
&self.rhs
}

/// Returns a new CompoundAssignment with the given variable being assigned to
pub fn with_lhs(self, lhs: Var) -> Self {
Self { lhs, ..self }
}

/// Returns a new CompoundAssignment with the given operator used
pub fn with_compound_operator(self, compound_operator: CompoundOp) -> Self {
Self {
compound_operator,
..self
}
}

/// Returns a new CompoundAssignment with the given value being assigned
pub fn with_rhs(self, rhs: Expression) -> Self {
Self { rhs, ..self }
}
}
/*
CompoundOp and CompoundAssignment have been moved to `compound.rs´, since CfxLua makes use of them as well.
*/

/// An if statement
#[derive(Clone, Debug, Display, PartialEq, Node, Visit)]
Expand Down
Loading