Skip to content

Commit

Permalink
refactor: use ast for all codegen, style changes
Browse files Browse the repository at this point in the history
  • Loading branch information
paradoxuum committed Jul 24, 2024
1 parent 566607a commit 7b0c8c4
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ pub(crate) enum AstTarget {
}

pub(crate) struct AstStream<'a, 'b> {
number_of_spaces: usize,
indents: usize,
is_start_of_line: bool,
writer: &'a mut (dyn Write),
Expand All @@ -37,7 +36,6 @@ pub(crate) struct AstStream<'a, 'b> {
impl<'a, 'b> AstStream<'a, 'b> {
pub fn new(writer: &'a mut (dyn fmt::Write + 'a), target: &'b AstTarget) -> Self {
Self {
number_of_spaces: 4,
indents: 0,
is_start_of_line: true,
writer,
Expand Down Expand Up @@ -75,11 +73,8 @@ impl Write for AstStream<'_, '_> {
if !line.is_empty() {
if self.is_start_of_line {
self.is_start_of_line = false;
self.writer.write_str(&format!(
"{: >1$}",
"",
self.number_of_spaces * self.indents
))?;
self.writer
.write_str(&format!("{:\t>1$}", "", self.indents))?;
}

self.writer.write_str(line)?;
Expand Down Expand Up @@ -108,7 +103,9 @@ impl AstFormat for ReturnStatement {

let result = self.0.fmt_ast(output);
if let AstTarget::Typescript { output_dir } = output.target {
write!(output, "\nexport = {output_dir}")?
write!(output, "\nexport = {output_dir};\n")?
} else {
writeln!(output)?;
}
result
}
Expand Down Expand Up @@ -149,9 +146,11 @@ pub(crate) struct Table {

impl AstFormat for Table {
fn fmt_ast(&self, output: &mut AstStream<'_, '_>) -> fmt::Result {
let assignment = match output.target {
AstTarget::Luau => " = ",
AstTarget::Typescript { .. } => ": ",
let typescript = matches!(output.target, AstTarget::Typescript { .. });
let (assignment, ending) = if typescript {
(": ", ";")
} else {
(" = ", ",")
};

writeln!(output, "{{")?;
Expand All @@ -161,11 +160,25 @@ impl AstFormat for Table {
key.fmt_key(output)?;
write!(output, "{assignment}")?;
value.fmt_ast(output)?;
writeln!(output, ",")?;

// If the value is a table and the target is TypeScript, we don't need the ending
// as it will be added after - this avoids double semi-colons for nested tables.
if let Expression::Table(_) = value {
if typescript {
writeln!(output)?;
continue;
}
}

writeln!(output, "{ending}")?;
}

output.unindent();
write!(output, "}}")
if typescript {
write!(output, "}};")
} else {
write!(output, "}}")
}
}
}

Expand Down
55 changes: 32 additions & 23 deletions src/commands/sync/codegen/flat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ use std::{
path::{Path, PathBuf},
};

use super::{
ast::{AstTarget, Expression},
generate_code,
};

fn asset_path(file_path: &str, strip_dir: &str, strip_extension: bool) -> anyhow::Result<String> {
if strip_extension {
Path::new(file_path).with_extension("")
Expand All @@ -17,21 +22,30 @@ fn asset_path(file_path: &str, strip_dir: &str, strip_extension: bool) -> anyhow
.map(|s| s.to_string())
}

fn generate_table(
assets: &BTreeMap<String, String>,
strip_dir: &str,
strip_extension: bool,
) -> anyhow::Result<Expression> {
let mut expressions: Vec<(Expression, Expression)> = Vec::new();
for (file_path, asset_id) in assets.iter() {
let file_stem = asset_path(file_path, strip_dir, strip_extension)?;
expressions.push((
Expression::String(file_stem),
Expression::String(asset_id.clone()),
));
}
Ok(Expression::table(expressions))
}

pub fn generate_luau(
assets: &BTreeMap<String, String>,
strip_dir: &str,
strip_extension: bool,
) -> anyhow::Result<String> {
let table = assets
.iter()
.map(|(file_path, asset_id)| {
let file_stem = asset_path(file_path, strip_dir, strip_extension)?;
Ok(format!("\t[\"{}\"] = \"{}\"", file_stem, asset_id))
})
.collect::<Result<Vec<String>, anyhow::Error>>()?
.join(",\n");

Ok(format!("return {{\n{}\n}}", table))
let table =
generate_table(assets, strip_dir, strip_extension).context("Failed to generate table")?;
generate_code(table, AstTarget::Luau)
}

pub fn generate_ts(
Expand All @@ -40,17 +54,12 @@ pub fn generate_ts(
output_dir: &str,
strip_extension: bool,
) -> anyhow::Result<String> {
let interface = assets
.keys()
.map(|file_path| {
let file_stem = asset_path(file_path, strip_dir, strip_extension)?;
Ok(format!("\t\"{}\": string", file_stem))
})
.collect::<Result<Vec<String>, anyhow::Error>>()?
.join(",\n");

Ok(format!(
"declare const {}: {{\n{}\n}}\nexport = {}",
output_dir, interface, output_dir
))
let table =
generate_table(assets, strip_dir, strip_extension).context("Failed to generate table")?;
generate_code(
table,
AstTarget::Typescript {
output_dir: output_dir.to_owned(),
},
)
}
31 changes: 23 additions & 8 deletions src/commands/sync/codegen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use std::collections::BTreeMap;
use std::fmt::Write;

use ast::{AstTarget, Expression, ReturnStatement};

use crate::commands::sync::config::CodegenStyle;

mod ast;
mod flat;
mod nested;

Expand Down Expand Up @@ -29,6 +34,12 @@ pub fn generate_ts(
}
}

fn generate_code(expression: Expression, target: AstTarget) -> anyhow::Result<String> {
let mut buffer = String::new();
write!(buffer, "{}", ReturnStatement(expression, target))?;
Ok(buffer)
}

#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
Expand All @@ -48,12 +59,12 @@ mod tests {
let lockfile = test_assets();

let lua = super::flat::generate_luau(&lockfile, "assets", false).unwrap();
assert_eq!(lua, "return {\n\t[\"/bar/baz.png\"] = \"rbxasset://.asphalt/bar/baz.png\",\n\t[\"/foo.png\"] = \"rbxassetid://1\"\n}");
assert_eq!(lua, "return {\n\t[\"/bar/baz.png\"] = \"rbxasset://.asphalt/bar/baz.png\",\n\t[\"/foo.png\"] = \"rbxassetid://1\",\n}\n");

let lua = super::flat::generate_luau(&lockfile, "assets", true).unwrap();
assert_eq!(
lua,
"return {\n\t[\"/bar/baz\"] = \"rbxasset://.asphalt/bar/baz.png\",\n\t[\"/foo\"] = \"rbxassetid://1\"\n}"
"return {\n\t[\"/bar/baz\"] = \"rbxasset://.asphalt/bar/baz.png\",\n\t[\"/foo\"] = \"rbxassetid://1\",\n}\n"
);
}

Expand All @@ -62,10 +73,10 @@ mod tests {
let lockfile = test_assets();

let ts = super::flat::generate_ts(&lockfile, "assets", "assets", false).unwrap();
assert_eq!(ts, "declare const assets: {\n\t\"/bar/baz.png\": string,\n\t\"/foo.png\": string\n}\nexport = assets");
assert_eq!(ts, "declare const assets: {\n\t\"/bar/baz.png\": \"rbxasset://.asphalt/bar/baz.png\";\n\t\"/foo.png\": \"rbxassetid://1\";\n};\nexport = assets;\n");

let ts = super::flat::generate_ts(&lockfile, "assets", "assets", true).unwrap();
assert_eq!(ts, "declare const assets: {\n\t\"/bar/baz\": string,\n\t\"/foo\": string\n}\nexport = assets");
assert_eq!(ts, "declare const assets: {\n\t\"/bar/baz\": \"rbxasset://.asphalt/bar/baz.png\";\n\t\"/foo\": \"rbxassetid://1\";\n};\nexport = assets;\n");
}

#[test]
Expand All @@ -75,12 +86,14 @@ mod tests {
let lua = super::nested::generate_luau(&lockfile, "assets", false).unwrap();
assert_eq!(
lua,
"return {\n bar = {\n [\"baz.png\"] = \"rbxasset://.asphalt/bar/baz.png\",\n },\n [\"foo.png\"] = \"rbxassetid://1\",\n}");
"return {\n\tbar = {\n\t\t[\"baz.png\"] = \"rbxasset://.asphalt/bar/baz.png\",\n\t},\n\t[\"foo.png\"] = \"rbxassetid://1\",\n}\n"
);

let lua = super::nested::generate_luau(&lockfile, "assets", true).unwrap();
assert_eq!(
lua,
"return {\n bar = {\n baz = \"rbxasset://.asphalt/bar/baz.png\",\n },\n foo = \"rbxassetid://1\",\n}");
"return {\n\tbar = {\n\t\tbaz = \"rbxasset://.asphalt/bar/baz.png\",\n\t},\n\tfoo = \"rbxassetid://1\",\n}\n"
);
}

#[test]
Expand All @@ -90,11 +103,13 @@ mod tests {
let ts = super::nested::generate_ts(&lockfile, "assets", "assets", false).unwrap();
assert_eq!(
ts,
"declare const assets: {\n bar: {\n \"baz.png\": \"rbxasset://.asphalt/bar/baz.png\",\n },\n \"foo.png\": \"rbxassetid://1\",\n}\nexport = assets");
"declare const assets: {\n\tbar: {\n\t\t\"baz.png\": \"rbxasset://.asphalt/bar/baz.png\";\n\t};\n\t\"foo.png\": \"rbxassetid://1\";\n};\nexport = assets;\n"
);

let ts = super::nested::generate_ts(&lockfile, "assets", "assets", true).unwrap();
assert_eq!(
ts,
"declare const assets: {\n bar: {\n baz: \"rbxasset://.asphalt/bar/baz.png\",\n },\n foo: \"rbxassetid://1\",\n}\nexport = assets");
"declare const assets: {\n\tbar: {\n\t\tbaz: \"rbxasset://.asphalt/bar/baz.png\";\n\t};\n\tfoo: \"rbxassetid://1\";\n};\nexport = assets;\n"
);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use self::types::NestedTable;
use super::ast::{AstTarget, Expression};
use super::generate_code;
use anyhow::{bail, Context};
use ast::{AstTarget, Expression, ReturnStatement};
use std::collections::BTreeMap;
use std::fmt::Write;
use std::path::PathBuf;
use std::{path::Component as PathComponent, path::Path};

mod ast;

pub(crate) mod types {
use std::collections::BTreeMap;

Expand Down Expand Up @@ -99,7 +97,7 @@ pub fn generate_luau(
) -> anyhow::Result<String> {
generate_code(
generate_expressions(assets, strip_dir, strip_extension)
.context("Failed to create nested expressions")?,
.context("Failed to generate nested table")?,
AstTarget::Luau,
)
}
Expand All @@ -112,15 +110,9 @@ pub fn generate_ts(
) -> anyhow::Result<String> {
generate_code(
generate_expressions(assets, strip_dir, strip_extension)
.context("Failed to create nested expressions")?,
.context("Failed to generate nested table")?,
AstTarget::Typescript {
output_dir: output_dir.to_owned(),
},
)
}

fn generate_code(expression: Expression, target: AstTarget) -> anyhow::Result<String> {
let mut buffer = String::new();
write!(buffer, "{}", ReturnStatement(expression, target))?;
Ok(buffer)
}

0 comments on commit 7b0c8c4

Please sign in to comment.