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

Change how string output is generated for atoms and strings #100

Merged
merged 8 commits into from
Jul 18, 2024
Merged
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
20 changes: 20 additions & 0 deletions resources/tests/test_string_repr.clsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
;;
;; This program is used by the test_quote_string_generation test
;; which checks the representation of these outputs and ensures
;; that they're properly accepted by brun, which uses the classic
;; chialisp tokenizer/parser.
;;
;; the qs macro appends the given string onto "test"
;; the atom macro gives a quoted atom starting with test and
;; ending with the text given in the string.
;;
;; modern's macro system makes these different objects.
;;
(mod ()
(include *standard-cl-23*)

(defmac qs (X) (string-append "test" X))
(defmac atom (X) (c 1 (string->symbol (string-append "test" X))))

(list (qs '"') (qs "'") (qs " hi") (atom '"') (atom "'") (atom "_hi"))
)
8 changes: 5 additions & 3 deletions src/compiler/sexp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,11 @@ pub fn decode_string(v: &[u8]) -> String {

pub fn printable(a: &[u8], quoted: bool) -> bool {
!a.iter().any(|ch| {
(*ch as char).is_control()
|| !(*ch as char).is_ascii()
|| (!quoted && ch.is_ascii_whitespace())
*ch < 32
|| *ch > 126
|| (!quoted && ((*ch as char).is_ascii_whitespace() || *ch == b'\''))
|| *ch == b'"'
|| *ch == b'\\'
})
}

Expand Down
35 changes: 35 additions & 0 deletions src/tests/classic/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2429,6 +2429,41 @@ fn test_assign_cse_tricky_2() {
assert_eq!(program, wanted_repr);
}

#[test]
fn test_quote_string_generation() {
// The program run here produces a list of strings and quoted atoms that have
// representations that must be flattened in various ways in this test.
let filename = "resources/tests/test_string_repr.clsp";
let program = do_basic_run(&vec!["run".to_string(), filename.to_string()])
.trim()
.to_string();
// The proram produces this list
// (list (qs '"') (qs "'") (qs " hi") (atom '"') (atom "'") (atom "_hi"))
//
// where qs puts "test" in front of the given string and atom puts test in front of the
// given string, converts it to an atom and quotes it so that it won't be interpreted
// as an identifier.
//
// in other words
// (list 'test"' "test'" "test hi"
// (q . (string->symbol (string-append "test" '"')))
// (q . (string->symbol (string-append "test" "'")))
// (q . test_hi)
// )
//
// The result below shows that the strings and atoms are reproduced as expected.
let wanted_repr = "(4 (1 . 0x7465737422) (4 (1 . \"test'\") (4 (1 . \"test hi\") (4 (1 . 499918271522) (4 (1 . 499918271527) (4 (1 . test_hi) ()))))))";
assert_eq!(program, wanted_repr);
let brun_result = do_basic_brun(&vec!["brun".to_string(), program])
.trim()
.to_string();
// This shows that brun interpreted and passed through the values successfully.
assert_eq!(
brun_result,
"(0x7465737422 \"test'\" \"test hi\" 0x7465737422 \"test'\" \"test_hi\")"
);
}

#[test]
fn test_classic_modpow() {
let result = do_basic_brun(&vec![
Expand Down
81 changes: 80 additions & 1 deletion src/tests/compiler/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ use std::rc::Rc;
use clvm_rs::allocator::Allocator;

use crate::classic::clvm::__type_compatibility__::bi_one;
use crate::classic::clvm_tools::binutils::disassemble;
use crate::classic::clvm_tools::stages::stage_0::DefaultProgramRunner;
use crate::compiler::clvm::run;
use crate::compiler::compiler::{compile_file, DefaultCompilerOpts};
use crate::compiler::comptypes::{CompileErr, CompilerOpts};
use crate::compiler::dialect::AcceptedDialect;
use crate::compiler::dialect::{AcceptedDialect, KNOWN_DIALECTS};
use crate::compiler::frontend::{collect_used_names_sexp, frontend};
use crate::compiler::rename::rename_in_cons;
use crate::compiler::runtypes::RunFailure;
use crate::compiler::sexp::{decode_string, enlist, parse_sexp, SExp};
use crate::compiler::srcloc::Srcloc;

use crate::tests::classic::run::do_basic_brun;

const TEST_TIMEOUT: usize = 1000000;

fn compile_string(content: &String) -> Result<String, CompileErr> {
Expand Down Expand Up @@ -2449,6 +2452,82 @@ fn test_almost_empty_lambda_gives_error() {
assert!(format!("{res:?}").contains("Must provide at least arguments and body to lambda"));
}

#[test]
fn test_exhaustive_chars() {
// Verify that we can create a program that gives the expected output using
// every byte value in the first, mid and last position of a value.
let mut substitute = vec![b'x', b'x', b'x'];

let srcloc = Srcloc::start("*extest*");
let make_test_program = |sub: Rc<SExp>| {
// (mod () (include *standard-cl-23.1*) (q . <sub>))
Rc::new(SExp::Cons(
srcloc.clone(),
Rc::new(SExp::Atom(srcloc.clone(), b"mod".to_vec())),
Rc::new(SExp::Cons(
srcloc.clone(),
Rc::new(SExp::Nil(srcloc.clone())),
Rc::new(SExp::Cons(
srcloc.clone(),
Rc::new(SExp::Cons(
srcloc.clone(),
Rc::new(SExp::Atom(srcloc.clone(), b"include".to_vec())),
Rc::new(SExp::Cons(
srcloc.clone(),
Rc::new(SExp::Atom(srcloc.clone(), b"*standard-cl-23.1*".to_vec())),
Rc::new(SExp::Nil(srcloc.clone())),
)),
)),
Rc::new(SExp::Cons(
srcloc.clone(),
Rc::new(SExp::Cons(
srcloc.clone(),
Rc::new(SExp::Integer(srcloc.clone(), bi_one())),
sub,
)),
Rc::new(SExp::Nil(srcloc.clone())),
)),
)),
)),
))
};

let runner = Rc::new(DefaultProgramRunner::new());

for i in 0..=2 {
for j in 0..=255 {
substitute[i] = j;

let sub_qe = Rc::new(SExp::QuotedString(srcloc.clone(), b'"', substitute.clone()));

let mut allocator = Allocator::new();
let mut opts: Rc<dyn CompilerOpts> = Rc::new(DefaultCompilerOpts::new("*extest*"));
let dialect = KNOWN_DIALECTS["*standard-cl-23.1*"].accepted.clone();
opts = opts.set_dialect(dialect);

let compiled = opts
.compile_program(
&mut allocator,
runner.clone(),
make_test_program(sub_qe),
&mut HashMap::new(),
)
.expect("should compile");

let compiled_output = compiled.to_string();
let result = do_basic_brun(&vec!["brun".to_string(), compiled_output])
.trim()
.to_string();

let classic_atom = allocator.new_atom(&substitute).expect("should work");
let disassembled = disassemble(&mut allocator, classic_atom, None);
assert_eq!(result, disassembled);

substitute[i] = b'x';
}
}
}

#[test]
fn test_odd_hex_works() {
let res = run_string(
Expand Down
Loading