Formatting of machines inside modules.

This commit is contained in:
chriseth
2023-12-18 20:21:10 +01:00
parent 3691be7aff
commit 46c1d83805
6 changed files with 226 additions and 10 deletions

View File

@@ -162,9 +162,7 @@ mod tests {
env!("CARGO_MANIFEST_DIR")
));
let file_name = base_path.join(path);
let expected = fs::read_to_string(file_name)
.unwrap()
.replace("machine Main", "machine ::Main");
let expected = fs::read_to_string(file_name).unwrap();
// remove the batch comments from the expected output before compiling
let input = expected

View File

@@ -13,4 +13,7 @@ diff = "0.1"
log = "0.4.18"
derive_more = "0.99.17"
[dev-dependencies]
pretty_assertions = "1.3.0"

View File

@@ -5,7 +5,11 @@ use std::{
use itertools::Itertools;
use crate::{indent, write_items_indented};
use crate::{
indent,
parsed::asm::{AbsoluteSymbolPath, Part},
write_indented_by, write_items_indented,
};
use super::{
AnalysisASMFile, AssignmentStatement, CallableSymbol, CallableSymbolDefinitionRef,
@@ -17,9 +21,36 @@ use super::{
impl<T: Display> Display for AnalysisASMFile<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let mut current_path = AbsoluteSymbolPath::default();
for (name, machine) in &self.machines {
write!(f, "machine {name}{machine}")?;
let [diff @ .., Part::Named(machine_name)] = &name.relative_to(&current_path).parts[..]
else {
unreachable!()
};
for part in diff {
match part {
Part::Super => {
current_path.pop();
write_indented_by(f, "}\n", current_path.parts.len())?;
}
Part::Named(m) => {
write_indented_by(f, format!("mod {m} {{\n"), current_path.parts.len())?;
current_path.push(m.clone());
}
}
}
write_indented_by(
f,
format!("machine {machine_name}{machine}"),
current_path.parts.len(),
)?;
}
for (i, _) in current_path.parts.iter().enumerate().rev() {
write_indented_by(f, "}\n", i)?;
}
Ok(())
}
}
@@ -247,3 +278,59 @@ impl Display for IncompatibleSet {
write!(f, "{}", self.0.iter().format(", "))
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::parsed::asm::parse_absolute_path;
use number::GoldilocksField;
use pretty_assertions::assert_eq;
#[test]
fn display_asm_analysis_file() {
let file = AnalysisASMFile::<GoldilocksField> {
machines: [
"::x::Y",
"::x::r::T",
"::x::f::Y",
"::M",
"::t::x::y::R",
"::t::F",
"::X",
]
.into_iter()
.map(|s| (parse_absolute_path(s), Machine::default()))
.collect(),
};
assert_eq!(
file.to_string(),
r#"machine M {
}
machine X {
}
mod t {
machine F {
}
mod x {
mod y {
machine R {
}
}
}
}
mod x {
machine Y {
}
mod f {
machine Y {
}
}
mod r {
machine T {
}
}
}
"#
);
}
}

View File

@@ -77,7 +77,7 @@ pub fn evaluate_unary_operation<T: FieldElement>(op: UnaryOperator, v: T) -> T {
}
/// quick and dirty String to String indentation
fn indent<S: ToString>(s: S, indentation: usize) -> String {
pub fn indent<S: ToString>(s: S, indentation: usize) -> String {
s.to_string()
.split('\n')
.map(|line| match line {
@@ -87,6 +87,14 @@ fn indent<S: ToString>(s: S, indentation: usize) -> String {
.join("\n")
}
pub fn write_indented_by<S, W>(f: &mut W, s: S, indentation: usize) -> Result
where
S: Display,
W: Write,
{
write!(f, "{}", indent(s, indentation))
}
fn write_items<S, I, W>(f: &mut W, items: I) -> Result
where
S: Display,
@@ -112,7 +120,8 @@ where
W: Write,
{
for item in items.into_iter() {
writeln!(f, "{}", indent(item, by))?;
write_indented_by(f, item, by)?;
writeln!(f)?;
}
Ok(())
}

View File

@@ -1,4 +1,7 @@
use std::{fmt::Display, iter::once};
use std::{
fmt::Display,
iter::{once, repeat},
};
use number::AbstractNumberType;
@@ -121,6 +124,7 @@ pub struct AbsoluteSymbolPath {
/// Panics if the path does not start with '::'.
pub fn parse_absolute_path(s: &str) -> AbsoluteSymbolPath {
match s.strip_prefix("::") {
Some("") => AbsoluteSymbolPath::default(),
Some(s) => s
.split("::")
.fold(AbsoluteSymbolPath::default(), |path, part| {
@@ -131,12 +135,48 @@ pub fn parse_absolute_path(s: &str) -> AbsoluteSymbolPath {
}
impl AbsoluteSymbolPath {
/// Removes and returns the last path component (unless empty).
pub fn pop(&mut self) -> Option<String> {
self.parts.pop()
}
}
impl AbsoluteSymbolPath {
/// Appends a part to the end of the path.
pub fn push(&mut self, part: String) {
self.parts.push(part);
}
/// Returns the relative path from base to self.
/// In other words, base.join(self.relative_to(base)) == self.
pub fn relative_to(&self, base: &AbsoluteSymbolPath) -> SymbolPath {
let common_prefix_len = self.common_prefix(base).parts.len();
// Start with max(0, base.parts.len() - common_root.parts.len())
// repetitions of "super".
let parts = repeat(Part::Super)
.take(base.parts.len().saturating_sub(common_prefix_len))
// append the parts of self after the common root.
.chain(
self.parts
.iter()
.skip(common_prefix_len)
.cloned()
.map(Part::Named),
)
.collect();
SymbolPath { parts }
}
/// Returns the common prefix of two paths.
pub fn common_prefix(&self, other: &AbsoluteSymbolPath) -> AbsoluteSymbolPath {
let parts = self
.parts
.iter()
.zip(other.parts.iter())
.map_while(|(a, b)| if a == b { Some(a.clone()) } else { None })
.collect();
AbsoluteSymbolPath { parts }
}
/// Resolves a relative path in the context of this absolute path.
pub fn join<P: Into<SymbolPath> + Display>(mut self, other: P) -> Self {
for part in other.into().parts {
@@ -339,3 +379,80 @@ pub struct Param<T> {
pub index: Option<T>,
pub ty: Option<String>,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn common_prefix() {
assert_eq!(
parse_absolute_path("::a::b").common_prefix(&parse_absolute_path("::a::c")),
parse_absolute_path("::a")
);
assert_eq!(
parse_absolute_path("::a::b").common_prefix(&parse_absolute_path("::a")),
parse_absolute_path("::a")
);
assert_eq!(
parse_absolute_path("::a").common_prefix(&parse_absolute_path("::a::c")),
parse_absolute_path("::a")
);
assert_eq!(
parse_absolute_path("::x").common_prefix(&parse_absolute_path("::y::t")),
parse_absolute_path("::")
);
assert_eq!(
parse_absolute_path("::x::r::v").common_prefix(&parse_absolute_path("::x::r::t")),
parse_absolute_path("::x::r")
);
}
#[test]
fn relative_to() {
assert_eq!(
parse_absolute_path("::a::b")
.relative_to(&parse_absolute_path("::a::c"))
.to_string(),
"super::b".to_string()
);
assert_eq!(
parse_absolute_path("::a::b")
.relative_to(&parse_absolute_path("::a"))
.to_string(),
"b".to_string()
);
assert_eq!(
parse_absolute_path("::x")
.relative_to(&parse_absolute_path("::y::t"))
.to_string(),
"super::super::x".to_string()
);
assert_eq!(
parse_absolute_path("::x::r::v")
.relative_to(&parse_absolute_path("::x::r"))
.to_string(),
"v".to_string()
);
assert_eq!(
parse_absolute_path("::x")
.relative_to(&parse_absolute_path("::x::t::k"))
.to_string(),
"super::super".to_string()
);
assert_eq!(
parse_absolute_path("::x")
.relative_to(&parse_absolute_path("::x"))
.to_string(),
"".to_string()
);
}
#[test]
fn relative_to_join() {
let v = parse_absolute_path("::x::r::v");
let base = parse_absolute_path("::x::t");
let rel = v.relative_to(&base);
assert_eq!(base.join(rel), v);
}
}

View File

@@ -14,6 +14,8 @@ use ast::parsed::{
folder::Folder,
};
/// Changes all symbol references (symbol paths) from relative paths
/// to absolute paths, and removes all import statements.
pub fn canonicalize_paths<T: FieldElement>(
program: ASMProgram<T>,
) -> Result<ASMProgram<T>, String> {