Skip to content

Commit ec26625

Browse files
committed
Created a fully functionnal CLI based on Cythan and Cythan-compiler
1 parent 9422aba commit ec26625

8 files changed

Lines changed: 299 additions & 0 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ Cargo.lock
88

99
# These are backup files generated by rustfmt
1010
**/*.rs.bk
11+
12+
13+
# Added by cargo
14+
15+
/target

Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "cythan-cli"
3+
version = "0.1.0"
4+
authors = ["ccgauche <gaucheron.laurent@gmail.com>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
clap = "*"
11+
cythan = {git = "https://github.com/Cypooos/Cythan-v3#cfd832b"}
12+
cythan-compiler = {git = "https://github.com/Cythan-Project/cythan-compiler#26d0194"}
13+
compression = "*"
14+
anyhow = "*"

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,42 @@
11
# cythan-cli
22
A CLI that uses cythan-compiler and cythan to build, test and execute Cythan V3 Code
3+
4+
## Usage
5+
6+
### Compile
7+
This will compile the file `cythan.ct` to `compiled.cct`
8+
```
9+
$ cythan-cli compile cythan.ct
10+
```
11+
12+
You can define the output file:
13+
```
14+
$ cythan-cli compile cythan.ct compiled-custom.cct
15+
```
16+
17+
If you want raw (readable by human but way larger) compiled cythan use the `.rct` extension for your output path:
18+
```
19+
$ cythan-cli compile cythan.ct compiled-custom.ct
20+
```
21+
22+
The compiler can also compress `.rct` files.
23+
24+
### Execute
25+
This will execute both raw compiled file (`.rct`) or compressed compiled ones (`.cct`):
26+
```
27+
$ cythan-cli execute cythan.cct 100
28+
```
29+
In this case 100 iterations of Cythan are computed and drawn to the terminal.
30+
31+
If you want the output be be written to a file: (You can use both `.rct` and `.cct`)
32+
```
33+
$ cythan-cli execute cythan.cct 100 executed.rct
34+
```
35+
36+
### Decompile
37+
This will convert `.cct` file into `.rct` ones.
38+
39+
In this example `cythan.cct` will be decompiled to `decompiled.rct`
40+
```
41+
$ cythan-cli decompile cythan.cct decompiled.rct
42+
```

src/compiler.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub fn compile(file: &str, out: &str) -> anyhow::Result<()> {
2+
let vec = cythan_compiler::Context::new().compute(&cythan_compiler::generate_tokens(
3+
&std::fs::read_to_string(file)?,
4+
));
5+
super::cythanenc::write_file_and_compress(out, &vec)
6+
}

src/cythanenc.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
fn compress(v: &Vec<u32>) -> Vec<u8> {
2+
v.iter()
3+
.map(|x| x.to_be_bytes())
4+
.fold(Vec::with_capacity(v.len() * 4), |mut a, b| {
5+
a.extend(b.iter());
6+
a
7+
})
8+
}
9+
10+
fn decompress(v: &Vec<u8>) -> Vec<u32> {
11+
(0..(v.len() / 4))
12+
.map(|i| {
13+
let i = i * 4;
14+
u32::from_be_bytes([v[i], v[i + 1], v[i + 2], v[i + 3]])
15+
})
16+
.collect()
17+
}
18+
19+
#[test]
20+
fn check_encode_validity() {
21+
let v: Vec<u32> = vec![
22+
12, 58, 23, 0, 2, 14, 25, 78, 52, 63, 60456, 4894, 91509, 4509090, 848554965, 645569511,
23+
489488, 845546154,
24+
];
25+
let o1 = compress(&v);
26+
let o2 = decompress(&o1);
27+
assert_eq!(v, o2);
28+
}
29+
30+
#[test]
31+
fn check_file_encode_decode() {
32+
let v: Vec<u32> = (0..100_000).collect();
33+
34+
std::fs::write(
35+
"raw.ctt",
36+
v.iter()
37+
.map(|x| x.to_string())
38+
.collect::<Vec<String>>()
39+
.join(" "),
40+
);
41+
42+
write_file_and_compress("compressed.cct", &v);
43+
let p = read_file_and_decompress("compressed.cct");
44+
assert_eq!(v, p.unwrap());
45+
}
46+
47+
use std::fs::File;
48+
use std::io::Read;
49+
use std::io::Write;
50+
51+
use compression::prelude::*;
52+
53+
pub fn read_file_and_decompress(filename: &str) -> anyhow::Result<Vec<u32>> {
54+
if filename.ends_with(".cct") {
55+
let mut f = File::open(&filename)?;
56+
let mut buffer = Vec::new();
57+
f.read_to_end(&mut buffer)?;
58+
Ok(decompress(
59+
&buffer
60+
.into_iter()
61+
.decode(&mut BZip2Decoder::new())
62+
.collect::<Result<Vec<u8>, _>>()?,
63+
))
64+
} else {
65+
Ok(std::fs::read_to_string(filename)?
66+
.split_whitespace()
67+
.flat_map(|x| x.parse::<u32>())
68+
.collect())
69+
}
70+
}
71+
72+
pub fn write_file_and_compress(filename: &str, data: &Vec<u32>) -> anyhow::Result<()> {
73+
if filename.ends_with(".cct") {
74+
let mut f = File::create(filename)?;
75+
f.write_all(
76+
&compress(data)
77+
.into_iter()
78+
.encode(&mut BZip2Encoder::new(9), Action::Finish)
79+
.collect::<Result<Vec<u8>, _>>()?,
80+
)?;
81+
Ok(())
82+
} else {
83+
std::fs::write(
84+
filename,
85+
data.iter()
86+
.map(|x| x.to_string())
87+
.fold(String::new(), |a, b| a + &b),
88+
)?;
89+
Ok(())
90+
}
91+
}

src/decompiler.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
pub fn decompile(file: &str, out: &str) -> anyhow::Result<()> {
2+
let i = super::cythanenc::read_file_and_decompress(file)?;
3+
4+
std::fs::write(
5+
out,
6+
&i.iter()
7+
.map(|x| x.to_string())
8+
.collect::<Vec<String>>()
9+
.join(" "),
10+
)?;
11+
Ok(())
12+
}

src/execute.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use cythan::{BasicCythan, Cythan};
2+
3+
pub fn execute(file: &str, iterations: usize, outfile: Option<String>) -> anyhow::Result<()> {
4+
let mut vec = BasicCythan::new(
5+
super::cythanenc::read_file_and_decompress(file)?
6+
.into_iter()
7+
.map(|x| x as usize)
8+
.collect(),
9+
);
10+
for _ in 0..iterations {
11+
vec.next();
12+
}
13+
if let Some(e) = outfile {
14+
super::cythanenc::write_file_and_compress(
15+
&e,
16+
&vec.cases.into_iter().map(|x| x as u32).collect(),
17+
)?;
18+
} else {
19+
println!("{:?}", vec.cases);
20+
};
21+
Ok(())
22+
}

src/main.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
mod compiler;
2+
mod cythanenc;
3+
mod decompiler;
4+
mod execute;
5+
6+
use clap::*;
7+
8+
fn main() {
9+
let matches = App::new("My Super Program")
10+
.version("1.0")
11+
.author("Kevin K. <kbknapp@gmail.com>")
12+
.about("Does awesome things")
13+
.subcommand(SubCommand::with_name("compile")
14+
.about("Compile a Cythan file | use .cct extension for Compressed Executable or .rct for raw format")
15+
.version("1.0")
16+
.author("Laurent Gaucheron <gaucheron.laurent@gmail.com>")
17+
.arg(Arg::with_name("INPUT")
18+
.required(true)
19+
.help("Set input file"))
20+
.arg(Arg::with_name("OUTPUT")
21+
.help("Set output file")))
22+
.subcommand(SubCommand::with_name("decompile")
23+
.about("Decompile a Cythan file | This require a compressed cythan file in .cct to work")
24+
.version("1.0")
25+
.author("Laurent Gaucheron <gaucheron.laurent@gmail.com>")
26+
.arg(Arg::with_name("INPUT")
27+
.required(true)
28+
.help("Set input file"))
29+
.arg(Arg::with_name("OUTPUT")
30+
.help("Set output file")))
31+
.subcommand(SubCommand::with_name("execute")
32+
.about("Execute compiled Cythan file | Support raw and compressed format (.rct and .cct)")
33+
.version("1.0")
34+
.author("Laurent Gaucheron <gaucheron.laurent@gmail.com>")
35+
.arg(Arg::with_name("INPUT")
36+
.required(true)
37+
.help("Set input file"))
38+
.arg(Arg::with_name("ITERATIONS")
39+
.required(true)
40+
.help("Number of iterations to compute"))
41+
.arg(Arg::with_name("OUTPUT")
42+
.help("Set output file if none will print to the console the result")))
43+
.get_matches();
44+
if let Some(matches) = matches.subcommand_matches("compile") {
45+
let output = matches.value_of("OUTPUT").unwrap_or("compiled.cct");
46+
let input = matches.value_of("INPUT").unwrap();
47+
let instant = std::time::Instant::now();
48+
compiler::compile(input, output).unwrap();
49+
println!(
50+
"Compiled `{}` to `{}` in {}",
51+
input,
52+
output,
53+
ms_to_str(instant.elapsed().as_millis())
54+
);
55+
} else if let Some(matches) = matches.subcommand_matches("execute") {
56+
let output = matches.value_of("OUTPUT");
57+
let input = matches.value_of("INPUT").unwrap();
58+
let iterations: usize = matches
59+
.value_of("ITERATIONS")
60+
.map(|x| x.parse().ok())
61+
.flatten()
62+
.expect("<ITERATIONS> must be a positive integer");
63+
64+
let instant = std::time::Instant::now();
65+
execute::execute(input, iterations, output.map(|x| x.to_owned())).unwrap();
66+
if let Some(e) = output {
67+
println!(
68+
"Executed `{}` and written the result to `{}` in {}",
69+
input,
70+
e,
71+
ms_to_str(instant.elapsed().as_millis())
72+
);
73+
} else {
74+
println!(
75+
"Executed `{}` in {}",
76+
input,
77+
ms_to_str(instant.elapsed().as_millis())
78+
);
79+
}
80+
} else if let Some(matches) = matches.subcommand_matches("decompile") {
81+
let output = matches.value_of("OUTPUT").unwrap_or("decompiled.rct");
82+
let input = matches.value_of("INPUT").unwrap();
83+
84+
let instant = std::time::Instant::now();
85+
decompiler::decompile(input, output);
86+
println!(
87+
"Decompiled `{}` to `{}` in {}",
88+
input,
89+
output,
90+
ms_to_str(instant.elapsed().as_millis())
91+
);
92+
} else {
93+
println!("Invalid use: cythan-cli compile <FILE> [OUTPUT]")
94+
}
95+
}
96+
97+
fn ms_to_str(ms: u128) -> String {
98+
if ms < 100 {
99+
format!("{}ms", ms)
100+
} else if ms < 60_000 {
101+
format!("{}s", (ms / 100) as f32 / 10.0)
102+
} else {
103+
format!(
104+
"{}m {}s",
105+
(ms / 60_000) as f32,
106+
((ms % 60_000) / 100) as f32 / 10.0
107+
)
108+
}
109+
}

0 commit comments

Comments
 (0)