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

Add vectors #159

Merged
merged 20 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from 16 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
1 change: 1 addition & 0 deletions docs/.vitepress/components/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ const beforeMount = (monaco: Monaco) => {
"Instance",
"Color3",
"Vector3",
"vector",
"AlignedCFrame",
"CFrame",
] as const;
Expand Down
11 changes: 10 additions & 1 deletion docs/config/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,16 @@ local CFrameSpecialCases = {

## Vectors

Zap supports serializing Vector3s, although zap has both a Vector3 type and a Vector2 type. Zap does not allow the use of Vector2s, and instead only allows Vector3s as Vector2s don't use Luau's native vector type and instead are allocated on the heap. And thus, Zap's Vector2 type is almost the same as the Vector3 type, except it doesn't serialize the Z axis.
Zap supports `vector`s with any numeric component types. The Z component is optional, and will result in a `0` if omitted.

<CodeBlock code="type Position = vector(f64, f64, f64)" />
<CodeBlock code="type Size = vector(u8, f64)" />

Omitting all components will emit `vector(f32, f32, f32)`.
<CodeBlock code="type Position = vector()" />
Ketasaja marked this conversation as resolved.
Show resolved Hide resolved

Zap also supports serializing Vector3s, although zap has both a Vector3 type and a Vector2 type. Zap does not allow the use of Vector2s, and instead only allows Vector3s as Vector2s don't use Luau's native vector type and instead are allocated on the heap. And thus, Zap's Vector2 type is almost the same as the Vector3 type, except it doesn't serialize the Z axis.
Ketasaja marked this conversation as resolved.
Show resolved Hide resolved


## DateTimes

Expand Down
22 changes: 22 additions & 0 deletions zap/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ pub enum Ty<'src> {
Num(NumTy, Range),
Str(Range),
Buf(Range),
Vector(Box<Ty<'src>>, Box<Ty<'src>>, Option<Box<Ty<'src>>>),
Arr(Box<Ty<'src>>, Range),
Map(Box<Ty<'src>>, Box<Ty<'src>>),
Set(Box<Ty<'src>>),
Expand Down Expand Up @@ -291,6 +292,27 @@ impl<'src> Ty<'src> {
Self::Color3 => (12, Some(12)),
Self::Vector2 => (8, Some(8)),
Self::Vector3 => (12, Some(12)),
Self::Vector(x_ty, y_ty, z_ty) => {
let x_size = match **x_ty {
Ty::Num(numty, _) => numty.size(),
_ => unreachable!(),
Ketasaja marked this conversation as resolved.
Show resolved Hide resolved
};
let y_size = match **y_ty {
Ty::Num(numty, _) => numty.size(),
_ => unreachable!(),
};
let z_size = if let Some(z_ty) = z_ty {
match **z_ty {
Ty::Num(numty, _) => numty.size(),
_ => unreachable!(),
}
} else {
0
};

let total = x_size + y_size + z_size;
(total, Some(total))
}
Self::AlignedCFrame => (13, Some(13)),
Self::CFrame => (24, Some(24)),
Self::Unknown => (0, None),
Expand Down
38 changes: 38 additions & 0 deletions zap/src/irgen/des.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,44 @@ impl Des<'_> {
),
),
Ty::Vector3 => self.push_assign(into, self.readvector3()),
Ty::Vector(x_ty, y_ty, z_ty) => {
let x_numty = match **x_ty {
Ty::Num(numty, range) => {
if self.checks {
self.push_range_check(into_expr.clone(), range);
}

numty
}
_ => unreachable!(),
};
let y_numty = match **y_ty {
Ty::Num(numty, range) => {
if self.checks {
self.push_range_check(into_expr.clone(), range);
}

numty
}
_ => unreachable!(),
};
let z_numty = if let Some(z_ty) = z_ty {
match **z_ty {
Ty::Num(numty, range) => {
if self.checks {
self.push_range_check(into_expr.clone(), range);
}

Some(numty)
}
_ => unreachable!(),
}
} else {
None
};

self.push_assign(into, self.readvector(x_numty, y_numty, z_numty));
}

Ty::AlignedCFrame => {
let (axis_alignment_name, axis_alignment_expr) = self.add_occurrence("axis_alignment");
Expand Down
16 changes: 16 additions & 0 deletions zap/src/irgen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,14 @@ pub trait Gen {
)
}

fn readvector(&self, x_numty: NumTy, y_numty: NumTy, z_numty: Option<NumTy>) -> Expr {
Expr::Vector(
Box::new(self.readnumty(x_numty)),
Box::new(self.readnumty(y_numty)),
z_numty.map(|z_numty| Box::new(self.readnumty(z_numty))),
)
}

fn push_write_copy(&mut self, expr: Expr, count: Expr) {
self.push_alloc(count.clone());

Expand Down Expand Up @@ -345,6 +353,7 @@ pub enum Expr {
// Datatypes
Color3(Box<Expr>, Box<Expr>, Box<Expr>),
Vector3(Box<Expr>, Box<Expr>, Box<Expr>),
Vector(Box<Expr>, Box<Expr>, Option<Box<Expr>>),

// Unary Operators
Len(Box<Expr>),
Expand Down Expand Up @@ -477,6 +486,13 @@ impl Display for Expr {

Self::Color3(x, y, z) => write!(f, "Color3.fromRGB({}, {}, {})", x, y, z),
Self::Vector3(x, y, z) => write!(f, "Vector3.new({}, {}, {})", x, y, z),
Self::Vector(x, y, z) => write!(
f,
"vector.create({}, {}, {})",
x,
y,
z.clone().unwrap_or(Box::new(Expr::Num(0f64)))
Ketasaja marked this conversation as resolved.
Show resolved Hide resolved
),

Self::Len(expr) => write!(f, "#{}", expr),
Self::Not(expr) => write!(f, "not {}", expr),
Expand Down
36 changes: 36 additions & 0 deletions zap/src/irgen/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,42 @@ impl Ser<'_> {
self.push_writef32(from.clone().nindex("Y").into());
self.push_writef32(from.clone().nindex("Z").into());
}
Ty::Vector(x_ty, y_ty, z_ty) => {
match **x_ty {
Ty::Num(numty, range) => {
if self.checks {
self.push_range_check(from_expr.clone(), range);
}

self.push_writenumty(from.clone().nindex("x").into(), numty)
}
_ => unreachable!(),
};

match **y_ty {
Ty::Num(numty, range) => {
if self.checks {
self.push_range_check(from_expr.clone(), range);
}

self.push_writenumty(from.clone().nindex("y").into(), numty)
}
_ => unreachable!(),
};

if let Some(z_ty) = z_ty {
match **z_ty {
Ty::Num(numty, range) => {
if self.checks {
self.push_range_check(from_expr.clone(), range);
}

self.push_writenumty(from.clone().nindex("z").into(), numty)
}
_ => unreachable!(),
};
}
}

Ty::AlignedCFrame => {
let (axis_alignment_name, axis_alignment_expr) = self.add_occurrence("axis_alignment");
Expand Down
1 change: 1 addition & 0 deletions zap/src/output/luau/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ pub trait Output {
Ty::Color3 => self.push("Color3"),
Ty::Vector2 => self.push("Vector3"),
Ty::Vector3 => self.push("Vector3"),
Ty::Vector(..) => self.push("vector"),
Ty::AlignedCFrame => self.push("CFrame"),
Ty::CFrame => self.push("CFrame"),
}
Expand Down
1 change: 1 addition & 0 deletions zap/src/output/typescript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ pub trait Output: ConfigProvider {
Ty::Color3 => self.push("Color3"),
Ty::Vector2 => self.push("Vector3"),
Ty::Vector3 => self.push("Vector3"),
Ty::Vector(..) => self.push("vector"),
Ty::AlignedCFrame => self.push("CFrame"),
Ty::CFrame => self.push("CFrame"),
}
Expand Down
40 changes: 39 additions & 1 deletion zap/src/parser/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,44 @@ impl<'src> Converter<'src> {
.unwrap_or_default(),
),

SyntaxTyKind::Vector(x_ty, y_ty, z_ty) => {
match self.ty(x_ty) {
Ty::Num(_, _) => (),
_ => self.report(Report::AnalyzeInvalidVectorType {
span: Span {
start: x_ty.start,
end: x_ty.end,
},
}),
};
match self.ty(y_ty) {
Ty::Num(_, _) => (),
_ => self.report(Report::AnalyzeInvalidVectorType {
span: Span {
start: y_ty.start,
end: y_ty.end,
},
}),
};
if let Some(z_ty) = z_ty {
match self.ty(z_ty) {
Ty::Num(_, _) => (),
_ => self.report(Report::AnalyzeInvalidVectorType {
span: Span {
start: z_ty.start,
end: z_ty.end,
},
}),
};
}

Ty::Vector(
Box::new(self.ty(x_ty)),
Box::new(self.ty(y_ty)),
z_ty.as_ref().map(|z_ty| Box::new(self.ty(z_ty))),
)
}

SyntaxTyKind::Arr(ty, len) => Ty::Arr(
Box::new(self.ty(ty)),
len.map(|len| self.checked_range_within(&len, 0.0, u16::MAX as f64))
Expand Down Expand Up @@ -674,7 +712,7 @@ impl<'src> Converter<'src> {
match ref_name {
ref_name if ref_name == name => Some(*ref_ty),

"boolean" | "Color3" | "Vector3" | "AlignedCFrame" | "CFrame" | "unknown" => None,
"boolean" | "Color3" | "Vector3" | "vector" | "AlignedCFrame" | "CFrame" | "unknown" => None,

_ => {
if searched.contains(ref_name) {
Expand Down
7 changes: 7 additions & 0 deletions zap/src/parser/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ TyKind: SyntaxTyKind<'input> = {

"string" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Str(r),
"buffer" <r:("(" <IntRange> ")")?> => SyntaxTyKind::Buf(r),
"vector" => SyntaxTyKind::Vector(
Box::new(SyntaxTy { start: 0, end: 0, kind: SyntaxTyKind::Num(NumTy::F32, None)}),
Box::new(SyntaxTy { start: 0, end: 0, kind: SyntaxTyKind::Num(NumTy::F32, None)}),
Some(Box::new(SyntaxTy { start: 0, end: 0, kind: SyntaxTyKind::Num(NumTy::F32, None)}))
),
"vector" "(" <x_ty:Ty> "," <y_ty:Ty> ")" => SyntaxTyKind::Vector(Box::new(x_ty), Box::new(y_ty), None),
"vector" "(" <x_ty:Ty> "," <y_ty:Ty> "," <z_ty:Ty> ")" => SyntaxTyKind::Vector(Box::new(x_ty), Box::new(y_ty), Some(Box::new(z_ty))),

<ty:Ty> "[" <r:IntRange?> "]" => SyntaxTyKind::Arr(Box::new(ty), r),
"map" "{" "[" <k:Ty> "]" ":" <v:Ty> "}" => SyntaxTyKind::Map(Box::new(k), Box::new(v)),
Expand Down
14 changes: 14 additions & 0 deletions zap/src/parser/reports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub enum Report<'src> {
span: Span,
},

AnalyzeInvalidVectorType {
span: Span,
},

AnalyzeEmptyEnum {
span: Span,
},
Expand Down Expand Up @@ -126,6 +130,7 @@ impl Report<'_> {
Self::AnalyzeOversizeUnreliable { .. } => Severity::Error,
Self::AnalyzePotentiallyOversizeUnreliable { .. } => Severity::Warning,
Self::AnalyzeInvalidRange { .. } => Severity::Error,
Self::AnalyzeInvalidVectorType { .. } => Severity::Error,
Self::AnalyzeEmptyEnum { .. } => Severity::Error,
Self::AnalyzeEnumTagUsed { .. } => Severity::Error,
Self::AnalyzeInvalidOptValue { .. } => Severity::Error,
Expand Down Expand Up @@ -157,6 +162,7 @@ impl Report<'_> {
Self::AnalyzeOversizeUnreliable { .. } => "oversize unreliable".to_string(),
Self::AnalyzePotentiallyOversizeUnreliable { .. } => "potentially oversize unreliable".to_string(),
Self::AnalyzeInvalidRange { .. } => "invalid range".to_string(),
Self::AnalyzeInvalidVectorType { .. } => "invalid vector type".to_string(),
Self::AnalyzeEmptyEnum { .. } => "empty enum".to_string(),
Self::AnalyzeEnumTagUsed { .. } => "enum tag used in variant".to_string(),
Self::AnalyzeInvalidOptValue { expected, .. } => format!("invalid opt value, expected {}", expected),
Expand Down Expand Up @@ -197,6 +203,7 @@ impl Report<'_> {
Self::AnalyzeDuplicateDecl { .. } => "3014",
Self::AnalyzeDuplicateParameter { .. } => "3015",
Self::AnalyzeNamedReturn { .. } => "3016",
Self::AnalyzeInvalidVectorType { .. } => "3017",
}
}

Expand Down Expand Up @@ -240,6 +247,10 @@ impl Report<'_> {
vec![Label::primary((), span.clone()).with_message("invalid range")]
}

Self::AnalyzeInvalidVectorType { span } => {
vec![Label::primary((), span.clone()).with_message("invalid vector component type")]
}

Self::AnalyzeEmptyEnum { span } => {
vec![Label::primary((), span.clone()).with_message("empty enum")]
}
Expand Down Expand Up @@ -336,6 +347,9 @@ impl Report<'_> {
"ranges must be in the form `min..max`".to_string(),
"ranges can be invalid if `min` is greater than `max`".to_string(),
]),
Self::AnalyzeInvalidVectorType { .. } => {
Some(vec!["only numeric types are valid vector components".to_string()])
}
Self::AnalyzeEmptyEnum { .. } => Some(vec![
"enums cannot be empty".to_string(),
"if you're looking to create an empty type, use a struct with no fields".to_string(),
Expand Down
1 change: 1 addition & 0 deletions zap/src/parser/syntax_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ pub enum SyntaxTyKind<'src> {
Num(NumTy, Option<SyntaxRange<'src>>),
Str(Option<SyntaxRange<'src>>),
Buf(Option<SyntaxRange<'src>>),
Vector(Box<SyntaxTy<'src>>, Box<SyntaxTy<'src>>, Option<Box<SyntaxTy<'src>>>),
Arr(Box<SyntaxTy<'src>>, Option<SyntaxRange<'src>>),
Map(Box<SyntaxTy<'src>>, Box<SyntaxTy<'src>>),
Set(Box<SyntaxTy<'src>>),
Expand Down
Loading