feat: compressing files, converting files

and error message handling
This commit is contained in:
Artemy 2022-08-17 14:21:21 +03:00
parent 30150961a9
commit 1da96d7704
4 changed files with 185 additions and 33 deletions

View file

@ -21,5 +21,7 @@ serde = { version = "1.0", features = ["derive"] }
json5 = "0.4.1" json5 = "0.4.1"
serde_json = "1.0" serde_json = "1.0"
serde_yaml = "0.9" serde_yaml = "0.9"
rmp-serde = "1.1.0"
clap = { version = "3.2", features = ["derive"] } clap = { version = "3.2", features = ["derive"] }
colored = "2" colored = "2"

View file

@ -74,5 +74,4 @@
// "21! == 51 090 942 171 709 440 000: ", // "21! == 51 090 942 171 709 440 000: ",
// { _eq: [{ fact: [21] }, 51090942171709440000] }, // { _eq: [{ fact: [21] }, 51090942171709440000] },
// ], some json and yaml troubles with number `51090942171709440000` // ], some json and yaml troubles with number `51090942171709440000`
{ calc: ["operand", "+", "operand"] }
] ]

View file

@ -3,10 +3,13 @@ use colored::*;
use json5; use json5;
use serde_json::{json, Map, Value}; use serde_json::{json, Map, Value};
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::File;
use std::io::{self, Write}; use std::io::{self, Write};
use std::{thread, time}; use std::path::Path;
use std::{fs, thread, time};
pub struct Interpreter { pub struct Interpreter {
input: String, commands: Vec<Value>,
vars: HashMap<String, Var>, vars: HashMap<String, Var>,
pos: usize, pos: usize,
scope: usize, scope: usize,
@ -14,32 +17,159 @@ pub struct Interpreter {
} }
impl Interpreter { impl Interpreter {
pub fn new(input: String) -> Self { pub fn new(file_path: String) -> Self {
match Path::new(&file_path)
.extension()
.and_then(OsStr::to_str)
.expect("The file must have the extension (.yaml, .json, .json5, .onla or .conla)")
{
"yaml" => {
let file_input = fs::read_to_string(&file_path).expect("File reading error");
let obj: serde_json::Value =
serde_yaml::from_str(&file_input).unwrap_or_else(|x| {
match x.location() {
Some(location) => {
eprintln!(
"{file_path}:{}:{} --> {x}",
location.column(),
location.line()
);
}
None => {
eprintln!("{}", x);
}
}
std::process::exit(1);
});
let commands = obj.as_array().unwrap_or_else(|| {
obj.get("main")
.expect("Each program must contain a `{main: [..commands]}` object or be a command array ([..commands])")
.as_array()
.expect("The program must be an array of commands")
});
Self { Self {
input, commands: commands.clone(),
vars: HashMap::new(), vars: HashMap::new(),
pos: 1, pos: 1,
scope: 0, scope: 0,
scopes: Vec::new(), scopes: Vec::new(),
} }
} }
"conla" => {
pub fn run(&mut self) { let file_input = File::open(file_path).expect("File reading error");
let obj: serde_json::Value = json5::from_str::<Value>(&self.input).unwrap_or_else(|_| { let obj: serde_json::Value = rmp_serde::from_read(file_input)
serde_yaml::from_str(&self.input) .expect(".conla file (MessagePack) is invalid! ");
.expect("Your file format is invalid! (supported: json, json5 or yaml)") let commands = obj.as_array().unwrap_or_else(|| {
obj.get("main")
.expect("Each program must contain a `{main: [..commands]}` object or be a command array ([..commands])")
.as_array()
.expect("The program must be an array of commands")
}); });
let arr = obj.as_array().unwrap_or_else(|| { Self {
commands: commands.clone(),
vars: HashMap::new(),
pos: 1,
scope: 0,
scopes: Vec::new(),
}
}
_ => {
let file_input = fs::read_to_string(&file_path).expect("File reading error");
let obj: serde_json::Value =
json5::from_str::<Value>(&file_input).unwrap_or_else(|x| {
eprintln!("{file_path}{x}");
std::process::exit(1);
});
let commands = obj.as_array().unwrap_or_else(|| {
obj.get("main") obj.get("main")
.expect("Each program must contain a `{main: [..commands]}` object or be a command array ([..commands])") .expect("Each program must contain a `{main: [..commands]}` object or be a command array ([..commands])")
.as_array() .as_array()
.expect("The program must be an array of commands") .expect("The program must be an array of commands")
}); });
Self {
commands: commands.clone(),
vars: HashMap::new(),
pos: 1,
scope: 0,
scopes: Vec::new(),
}
}
}
}
pub fn compress(&mut self, output_path: String) {
let mut output = File::create(output_path).expect("Failed to create output file");
output
.write_all(
&rmp_serde::encode::to_vec(&self.commands)
.expect("Error when compressing onlang to .conla"),
)
.expect("Error when writing to file");
println!("Compressed");
}
pub fn convert(&self, format: String, output_path: String) {
match format.as_str() {
"yaml" => {
self.convert_to_yaml(output_path);
}
"json" => {
self.convert_to_json(output_path);
}
"json5" => {
self.convert_to_json5(output_path);
}
_ => {
self.error("The conversion format is not supported");
}
}
}
fn convert_to_yaml(&self, output_path: String) {
let mut output = File::create(output_path).expect("Failed to create output file");
write!(
output,
"{}",
serde_yaml::to_string(&self.commands).expect("Error when convert to yaml")
)
.expect("Error when writing to file");
println!("Converted");
}
fn convert_to_json(&self, output_path: String) {
let mut output = File::create(output_path).expect("Failed to create output file");
write!(
output,
"{}",
serde_json::to_string(&self.commands).expect("Error when convert to json")
)
.expect("Error when writing to file");
println!("Converted");
}
fn convert_to_json5(&self, output_path: String) {
let mut output = File::create(output_path).expect("Failed to create output file");
write!(
output,
"{}",
json5::to_string(&self.commands).expect("Error when convert to json5")
)
.expect("Error when writing to file");
println!("Converted");
}
pub fn run(&mut self) {
self.scopes.push(Vec::new()); self.scopes.push(Vec::new());
for command in arr { let length = self.commands.len();
for i in 0..length {
let command = &self.commands[i].clone();
self.eval_node(command); self.eval_node(command);
self.pos += 1; self.pos = i;
} }
} }

View file

@ -1,40 +1,61 @@
use clap::Parser; use clap::Parser;
use std::fs;
use std::time::Instant; use std::time::Instant;
mod types; mod types;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
struct Args { struct Args {
file: String, path: String,
#[clap(short, long)] #[clap(short, long)]
verbose: bool, verbose: bool,
#[clap(long)]
compress: bool,
#[clap(long)]
convert: Option<String>,
#[clap(short, long)]
out: Option<String>,
} }
mod interpreter; mod interpreter;
use interpreter::Interpreter; use interpreter::Interpreter;
fn main() { fn main() {
#[cfg(not(debug_assertions))] // #[cfg(not(debug_assertions))]
std::panic::set_hook(Box::new(|info| { std::panic::set_hook(Box::new(|info| {
eprint!( eprint!(
"{msg}", "{}",
msg = match info.payload().downcast_ref::<String>() { match info.payload().downcast_ref::<String>() {
None => "Program panicked without a message!", None => "Program panicked without a message!",
Some(x) => x, Some(x) => x,
} }
); )
})); }));
let start = Instant::now(); let start = Instant::now();
let args = Args::parse(); let args = Args::parse();
if args.verbose == true { if args.verbose == true {
println!("Running: {}\n", args.file); println!("Running: {}\n", args.path);
} }
let file_input = fs::read_to_string(args.file).expect("File reading error");
let mut onint = Interpreter::new(file_input);
let mut onint = Interpreter::new(args.path);
match args.out {
Some(output_path) => {
if args.compress {
onint.compress(output_path);
} else if let Some(format) = args.convert {
onint.convert(format, output_path);
} else {
eprintln!("The file conversion format is not specified, use the flag: --compress")
}
}
None => {
onint.run(); onint.run();
}
}
if args.verbose == true { if args.verbose == true {
println!("\nElapsed: {:?}", start.elapsed()); println!("\nElapsed: {:?}", start.elapsed());