Skip to content

Commit

Permalink
Merge branch 'master' into chore/update-borsh-1.5.x
Browse files Browse the repository at this point in the history
  • Loading branch information
monoid authored May 25, 2024
2 parents 01d85dc + 04bacb7 commit 5d330ac
Show file tree
Hide file tree
Showing 12 changed files with 728 additions and 1,374 deletions.
25 changes: 25 additions & 0 deletions crates/beautifier/src/beautifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/

use super::r#virtual::try_hopon;

use air_parser::ast;

use std::fmt::Display;
Expand Down Expand Up @@ -82,25 +84,42 @@ pub enum BeautifyError {
pub struct Beautifier<W: io::Write> {
output: W,
indent_step: usize,
try_hopon: bool,
}

impl<W: io::Write> Beautifier<W> {
/// Beautifier for the output with default indentation step.
#[inline]
pub fn new(output: W) -> Self {
Self {
output,
indent_step: DEFAULT_INDENT_STEP,
try_hopon: false,
}
}

/// Beautifier for the output with custom indentation step.
#[inline]
pub fn new_with_indent(output: W, indent_step: usize) -> Self {
Self {
output,
indent_step,
try_hopon: false,
}
}

#[inline]
/// Enable all patterns in the emited code.
pub fn enable_all_patterns(self) -> Self {
self.enable_try_hopon()
}

#[inline]
pub fn enable_try_hopon(mut self) -> Self {
self.try_hopon = true;
self
}

/// Unwrap the Beautifier into the underlying writer.
pub fn into_inner(self) -> W {
self.output
Expand Down Expand Up @@ -253,6 +272,12 @@ impl<W: io::Write> Beautifier<W> {
}

fn beautify_new(&mut self, new: &ast::New<'_>, indent: usize) -> io::Result<()> {
if self.try_hopon {
if let Some(hop_on) = try_hopon(new) {
return self.beautify_simple(&hop_on, indent);
}
}
// else
compound!(self, indent, new);
Ok(())
}
Expand Down
10 changes: 9 additions & 1 deletion crates/beautifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,22 @@
)]

mod beautifier;
mod r#virtual;

pub use crate::beautifier::{Beautifier, BeautifyError, DEFAULT_INDENT_STEP};

use std::io;

/// Beautify the `air_script` with default settings to the `output`.
pub fn beautify(air_script: &str, output: &mut impl io::Write) -> Result<(), BeautifyError> {
pub fn beautify(
air_script: &str,
output: &mut impl io::Write,
enable_patterns: bool,
) -> Result<(), BeautifyError> {
let mut beautifier = Beautifier::new(output);
if enable_patterns {
beautifier = beautifier.enable_all_patterns();
}
beautifier.beautify(air_script)
}

Expand Down
103 changes: 103 additions & 0 deletions crates/beautifier/src/tests/beautifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,106 @@ fn fail_error() {
let output = beautify_to_string(script).unwrap();
assert_eq!(output, "fail :error:\n");
}

#[test]
fn hopon_on() {
let script = r#"(new $ephemeral (new #ephemeral (canon "relay" $ephemeral #ephemeral)))"#;

let mut output = vec![];
let mut beautifier = Beautifier::new(&mut output).enable_all_patterns();
beautifier.beautify(script).unwrap();

assert_eq!(String::from_utf8(output).unwrap(), "hopon \"relay\"\n");
}

#[test]
fn hopon_off() {
let script = r#"(new $ephemeral (new #ephemeral (canon "relay" $ephemeral #ephemeral)))"#;

let mut output = vec![];
let mut beautifier = Beautifier::new(&mut output);
beautifier.beautify(script).unwrap();

assert_eq!(
String::from_utf8(output).unwrap(),
concat!(
"new $ephemeral:\n",
" new #ephemeral:\n",
" canon \"relay\" $ephemeral #ephemeral\n"
),
);
}

#[test]
fn hopon_canon_mismatch() {
let script = r#"(new $ephemeral (new #can (canon "relay" $ephemeral #ephemeral)))"#;

let mut output = vec![];
let mut beautifier = Beautifier::new(&mut output).enable_all_patterns();
beautifier.beautify(script).unwrap();

assert_eq!(
String::from_utf8(output).unwrap(),
concat!(
"new $ephemeral:\n",
" new #can:\n",
" canon \"relay\" $ephemeral #ephemeral\n"
),
);
}

#[test]
fn hopon_stream_mismatch() {
let script = r#"(new $stream (new #ephemeral (canon "relay" $ephemeral #ephemeral)))"#;

let mut output = vec![];
let mut beautifier = Beautifier::new(&mut output).enable_all_patterns();
beautifier.beautify(script).unwrap();

assert_eq!(
String::from_utf8(output).unwrap(),
concat!(
"new $stream:\n",
" new #ephemeral:\n",
" canon \"relay\" $ephemeral #ephemeral\n"
),
);
}

#[test]
fn hopon_nested() {
let script =
r#"(new $other (new $ephemeral (new #ephemeral (canon "relay" $ephemeral #ephemeral))) )"#;

let mut output = vec![];
let mut beautifier = Beautifier::new(&mut output).enable_all_patterns();
beautifier.beautify(script).unwrap();

assert_eq!(
String::from_utf8(output).unwrap(),
"new $other:\n hopon \"relay\"\n",
);
}

// this is bug that should be eventually fixed: it uses top-level #can
// instead of the nested one which disappeared
//
// the compiler doesn't generate such code, but it can be crafted manually
#[test]
fn hopon_shadowing() {
let script = r#"(new #can (new $ephemeral (new #can (canon #can.$.[0] $ephemeral #can))) )"#;

let mut output = vec![];
let mut beautifier = Beautifier::new(&mut output).enable_all_patterns();
beautifier.beautify(script).unwrap();

assert_eq!(
String::from_utf8(output).unwrap(),
concat!(
"new #can:\n",
" new $ephemeral:\n",
" new #can:\n",
" canon #can.$.[0] $ephemeral #can\n"
),
);
}
17 changes: 13 additions & 4 deletions crates/beautifier/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,25 @@ use crate::{beautify, beautify_to_string, BeautifyError};
fn beautify_valid() {
let air_script = "(seq (null) (null))";
let mut buffer = vec![];
let res = beautify(air_script, &mut buffer);
assert!(matches!(res, Ok(())));
let res = beautify(air_script, &mut buffer, false);
assert!(res.is_ok());
assert_eq!(std::str::from_utf8(&buffer).unwrap(), "null\nnull\n");
}

#[test]
fn beautify_valid_with_patterns() {
let air_script = "(seq (null) (null))";
let mut buffer = vec![];
let res = beautify(air_script, &mut buffer, true);
assert!(res.is_ok());
assert_eq!(std::str::from_utf8(&buffer).unwrap(), "null\nnull\n");
}

#[test]
fn beautify_invalid() {
let air_script = "(seq (null))";
let mut buffer = vec![];
let res = beautify(air_script, &mut buffer);
let res = beautify(air_script, &mut buffer, false);
assert!(matches!(res, Err(BeautifyError::Parse(_))));
}

Expand All @@ -56,5 +65,5 @@ fn beautify_to_string_valid() {
fn beautify_to_string_invalid() {
let air_script = "(seq (null))";
let res = beautify_to_string(air_script);
assert!(matches!(res, Err(_)));
assert!(res.is_err());
}
84 changes: 84 additions & 0 deletions crates/beautifier/src/virtual.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2024 Fluence DAO
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

use air_parser::ast;

use core::fmt;
use std::fmt::Display;

/// A virtual `hopon` instruction.
pub(crate) struct HopOn<'i> {
pub peer_id: ast::ResolvableToPeerIdVariable<'i>,
}

impl Display for HopOn<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "hopon {}", self.peer_id)
}
}

/// Try to parse the `new` instruction and its nested elements as a virtual `hopon` instruction.
///
/// For example:
/// ```clojure
/// (new #uniq1_name
/// (new $uniq2_name
/// (canon peer_id $uniq2_name #uniq1_name)))
/// ```
/// is parsed as a virtual instruction
/// ```clojure
/// (hopon peer_id)
/// ```
pub(crate) fn try_hopon<'i>(root_new: &ast::New<'i>) -> Option<HopOn<'i>> {
let expected_stream_name = &root_new.argument;

if let (ast::Instruction::New(nested_new), ast::NewArgument::Stream(stream_name)) =
(&root_new.instruction, expected_stream_name)
{
let expected_nested_canon_name = &nested_new.argument;

if let (ast::Instruction::Canon(canon), ast::NewArgument::CanonStream(nested_canon_name)) =
(&nested_new.instruction, expected_nested_canon_name)
{
if canon.canon_stream.name == nested_canon_name.name
&& canon.stream.name == stream_name.name
// this condition handles case that is never generated by an Aqua compiler, but
// can be crafted manually
//
// see `hopon_shadowing` test for an example
&& !canon_shadows_peer_id(nested_canon_name.name, &canon.peer_id)
{
return Some(HopOn {
peer_id: canon.peer_id.clone(),
});
}
}
}

None
}

fn canon_shadows_peer_id(canon_name: &str, peer_id: &ast::ResolvableToPeerIdVariable<'_>) -> bool {
use ast::ResolvableToPeerIdVariable::*;
match peer_id {
InitPeerId => false,
Literal(_) => false,
Scalar(_) => false,
ScalarWithLambda(_) => false,
CanonStreamMapWithLambda(_) => false,
CanonStreamWithLambda(canon_with_lambda) => canon_with_lambda.name == canon_name,
}
}
2 changes: 2 additions & 0 deletions tools/cli/air/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This subcommand reads an AIR script from standard input and prints it in human-r

It outputs to standard output or a file.

With `--patterns` options, it tries to recognize certain patterns that Aqua compiler emits and outputs it as more human readable Aqua-like syntax. Currently only `hopon` syntax is recognized.

## `air run`

Alias: `air r`.
Expand Down
10 changes: 9 additions & 1 deletion tools/cli/air/src/beautify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use std::{io, path::PathBuf};
pub(crate) struct Args {
#[clap(short, long, default_value_t = air_beautifier::DEFAULT_INDENT_STEP)]
indent_step: usize,
#[clap(short, long, help = "Recognize virtual instruction patterns")]
patterns: bool,
#[clap(short, long)]
output: Option<PathBuf>,
input: Option<PathBuf>,
Expand Down Expand Up @@ -65,6 +67,12 @@ pub(crate) fn beautify(args: Args) -> Result<()> {
let air_script = read_script(&args).context("failed to read the input")?;
let output = build_output(&args).context("failed to open the output")?;

Beautifier::new_with_indent(output, args.indent_step).beautify(&air_script)?;
let mut beautifier = Beautifier::new_with_indent(output, args.indent_step);

if args.patterns {
beautifier = beautifier.enable_all_patterns();
}

beautifier.beautify(&air_script)?;
Ok(())
}
9 changes: 8 additions & 1 deletion tools/wasm/air-beautify-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn beautify(air_script: String) -> Result<String, JsError> {
let mut output = vec![];
air_beautifier::beautify(&air_script, &mut output)?;
air_beautifier::beautify(&air_script, &mut output, true)?;
Ok(unsafe { String::from_utf8_unchecked(output) })
}

#[wasm_bindgen]
pub fn beautify_raw(air_script: String) -> Result<String, JsError> {
let mut output = vec![];
air_beautifier::beautify(&air_script, &mut output, false)?;
Ok(unsafe { String::from_utf8_unchecked(output) })
}
Loading

0 comments on commit 5d330ac

Please sign in to comment.