From e78129aea9afdb2d59abba45675e74b988c69efb Mon Sep 17 00:00:00 2001 From: Лач Date: Sat, 06 Jun 2020 09:31:26 +0000 Subject: [PATCH] chore: rename executable to jrsonnet --- --- /dev/null +++ b/cmds/jrsonnet/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "jrsonnet" +version = "0.1.0" +authors = ["Лач "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +jsonnet-evaluator = { path = "../../crates/jsonnet-evaluator" } +jsonnet-parser = { path = "../../crates/jsonnet-parser" } +annotate-snippets = "0.8.0" + +[dependencies.clap] +version = "3.0.0-beta.1" +default-features = false +features = ["std", "derive"] --- /dev/null +++ b/cmds/jrsonnet/src/location.rs @@ -0,0 +1,99 @@ +#[derive(Clone, PartialEq, Debug)] +pub struct CodeLocation { + pub line: usize, + pub column: usize, + + pub line_start_offset: usize, + pub line_end_offset: usize, +} + +pub fn offset_to_location(file: &str, offsets: &[usize]) -> Vec { + if offsets.is_empty() { + return vec![]; + } + let mut line = 1; + let mut column = 0; + let max_offset = *offsets.iter().max().unwrap(); + + let mut offset_map = offsets + .iter() + .enumerate() + .map(|(pos, offset)| (*offset, pos)) + .collect::>(); + offset_map.sort_by_key(|v| v.0); + offset_map.reverse(); + + let mut out = vec![ + CodeLocation { + column: 0, + line: 0, + line_start_offset: 0, + line_end_offset: 0 + }; + offsets.len() + ]; + let mut with_no_known_line_ending = vec![]; + let mut this_line_offset = 0; + for (pos, ch) in file.chars().enumerate() { + column += 1; + match offset_map.last() { + Some(x) if x.0 == pos => { + let out_idx = x.1; + with_no_known_line_ending.push(out_idx); + out[out_idx].line = line; + out[out_idx].column = column; + out[out_idx].line_start_offset = this_line_offset + 1; + offset_map.pop(); + } + _ => {} + } + if ch == '\n' { + line += 1; + column = 0; + + for idx in with_no_known_line_ending.drain(..) { + out[idx].line_end_offset = pos; + } + this_line_offset = pos; + + if pos == max_offset + 1 { + break; + } + } + } + let file_end = file.chars().count(); + for idx in with_no_known_line_ending { + out[idx].line_end_offset = file_end; + } + + out +} + +#[cfg(test)] +pub mod tests { + use super::{offset_to_location, CodeLocation}; + + #[test] + fn test() { + assert_eq!( + offset_to_location( + "hello world\n_______________________________________________________", + &[0, 14] + ), + vec![ + CodeLocation { + line: 1, + column: 1, + line_start_offset: 0, + line_end_offset: 11 + }, + CodeLocation { + line: 2, + column: 3, + line_start_offset: 11, + line_end_offset: 67 + } + ] + ) + } +} --- /dev/null +++ b/cmds/jrsonnet/src/main.rs @@ -0,0 +1,269 @@ +pub mod location; + +use clap::Clap; +use jsonnet_evaluator::{EvaluationState, LocError, StackTrace, Val}; +use location::{offset_to_location, CodeLocation}; +use std::env::current_dir; +use std::{path::PathBuf, str::FromStr}; + +enum Format { + None, + Json, + Yaml, +} + +impl FromStr for Format { + type Err = &'static str; + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Format::None, + "json" => Format::Json, + "yaml" => Format::Yaml, + _ => return Err("no such format"), + }) + } +} + +#[derive(PartialEq)] +enum TraceFormat { + CppJsonnet, + GoJsonnet, + Custom, +} +impl FromStr for TraceFormat { + type Err = &'static str; + fn from_str(s: &str) -> Result { + Ok(match s { + "cpp" => TraceFormat::CppJsonnet, + "go" => TraceFormat::GoJsonnet, + "default" => TraceFormat::Custom, + _ => return Err("no such format"), + }) + } +} + +#[derive(Clap)] +#[clap(version = "0.1.0", author = "Lach ")] +struct Opts { + #[clap(long, about = "Disable global std variable")] + no_stdlib: bool, + #[clap(long, about = "Add external string")] + ext_str: Option>, + #[clap(long, about = "Add external string from code")] + ext_code: Option>, + #[clap(long, about = "Add TLA")] + tla_str: Option>, + #[clap(long, about = "Add TLA from code")] + tla_code: Option>, + #[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")] + format: Format, + #[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")] + trace_format: TraceFormat, + + #[clap( + long, + short = "s", + default_value = "200", + about = "Number of allowed stack frames" + )] + max_stack: usize, + #[clap( + long, + short = "t", + default_value = "20", + about = "Max length of stack trace before cropping" + )] + max_trace: usize, + + #[clap(about = "File to compile", index = 1)] + input: String, +} + +fn main() { + let opts: Opts = Opts::parse(); + let evaluator = jsonnet_evaluator::EvaluationState::default(); + if !opts.no_stdlib { + evaluator.add_stdlib(); + } + let mut input = current_dir().unwrap(); + input.push(opts.input.clone()); + let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap(); + if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) { + print_syntax_error(e, &input, &code_string); + std::process::exit(2); + } + let result = evaluator.evaluate_file(&input); + match result { + Ok(v) => { + let v = match opts.format { + Format::Json => { + if opts.no_stdlib { + evaluator.add_stdlib(); + } + evaluator.add_global("__tmp__to_json__".to_owned(), v); + let v = evaluator + .parse_evaluate_raw("std.manifestJsonEx(__tmp__to_json__, \" \")"); + match v { + Ok(v) => v, + Err(err) => { + print_error(&err, evaluator, &opts); + std::process::exit(2); + } + } + } + Format::Yaml => { + if opts.no_stdlib { + evaluator.add_stdlib(); + } + evaluator.add_global("__tmp__to_yaml__".to_owned(), v); + let v = evaluator + .parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \" \")"); + match v { + Ok(v) => v, + Err(err) => { + print_error(&err, evaluator, &opts); + std::process::exit(2); + } + } + } + _ => v, + }; + match v { + Val::Str(s) => println!("{}", s), + Val::Num(n) => println!("{}", n), + _v => eprintln!( + "jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?" + ), + } + } + Err(err) => { + print_error(&err, evaluator, &opts); + } + } +} + +fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) { + println!("Error: {:?}", err.0); + print_trace(&(err.1), evaluator, &opts); +} + +fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) { + use annotate_snippets::{ + display_list::{DisplayList, FormatOptions}, + snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, + }; + //&("Expected: ".to_owned() + error.expected) + let origin = file.to_str().unwrap(); + let error_message = format!("Expected: {}", error.expected); + let snippet = Snippet { + opt: FormatOptions { + color: true, + ..Default::default() + }, + title: Some(Annotation { + label: Some(&error_message), + id: None, + annotation_type: AnnotationType::Error, + }), + footer: vec![], + slices: vec![Slice { + source: &code, + line_start: 1, + origin: Some(origin), + fold: false, + annotations: vec![SourceAnnotation { + label: "At this position", + annotation_type: AnnotationType::Error, + range: (error.location.offset, error.location.offset + 1), + }], + }], + }; + + let dl = DisplayList::from(snippet); + println!("{}", dl); +} + +fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) { + use annotate_snippets::{ + display_list::{DisplayList, FormatOptions}, + snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, + }; + for item in trace.0.iter() { + let desc = &item.1; + if (item.0).1.is_none() { + continue; + } + let source = (item.0).1.clone().unwrap(); + let code = evaluator.get_source(&source.0); + if code.is_none() { + continue; + } + let code = code.unwrap(); + let start_end = offset_to_location(&code, &[source.1, source.2]); + if opts.trace_format == TraceFormat::Custom { + let source_fragment: String = code + .chars() + .skip(start_end[0].line_start_offset) + .take(start_end[1].line_end_offset - start_end[0].line_start_offset) + .collect(); + let snippet = Snippet { + opt: FormatOptions { + color: true, + ..Default::default() + }, + title: Some(Annotation { + label: Some(&item.1), + id: None, + annotation_type: AnnotationType::Error, + }), + footer: vec![], + slices: vec![Slice { + source: &source_fragment, + line_start: start_end[0].line, + origin: Some(&source.0.to_str().unwrap()), + fold: false, + annotations: vec![SourceAnnotation { + label: desc, + annotation_type: AnnotationType::Error, + range: ( + source.1 - start_end[0].line_start_offset, + source.2 - start_end[0].line_start_offset, + ), + }], + }], + }; + + let dl = DisplayList::from(snippet); + println!("{}", dl); + } else { + print_jsonnet_pair( + source.0.to_str().unwrap(), + &start_end[0], + &start_end[1], + opts.trace_format == TraceFormat::GoJsonnet, + ); + } + } +} + +fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) { + if is_go { + print!(" "); + } else { + print!(" "); + } + print!("{}:", file); + if start.line == end.line { + // IDK why, but this is the behavior original jsonnet cpp impl shows + if start.column == end.column || !is_go && start.column + 1 == end.column { + println!("{}:{}", start.line, end.column) + } else { + println!("{}:{}-{}", start.line, start.column, end.column); + } + } else { + println!( + "({}:{})-({}:{})", + start.line, end.column, start.line, end.column + ); + } +} --- a/cmds/jsonnet/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "jsonnet" -version = "0.1.0" -authors = ["Лач "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -jsonnet-evaluator = { path = "../../crates/jsonnet-evaluator" } -jsonnet-parser = { path = "../../crates/jsonnet-parser" } -annotate-snippets = "0.8.0" - -[dependencies.clap] -version = "3.0.0-beta.1" -default-features = false -features = ["std", "derive"] --- a/cmds/jsonnet/src/location.rs +++ /dev/null @@ -1,99 +0,0 @@ -#[derive(Clone, PartialEq, Debug)] -pub struct CodeLocation { - pub line: usize, - pub column: usize, - - pub line_start_offset: usize, - pub line_end_offset: usize, -} - -pub fn offset_to_location(file: &str, offsets: &[usize]) -> Vec { - if offsets.is_empty() { - return vec![]; - } - let mut line = 1; - let mut column = 0; - let max_offset = *offsets.iter().max().unwrap(); - - let mut offset_map = offsets - .iter() - .enumerate() - .map(|(pos, offset)| (*offset, pos)) - .collect::>(); - offset_map.sort_by_key(|v| v.0); - offset_map.reverse(); - - let mut out = vec![ - CodeLocation { - column: 0, - line: 0, - line_start_offset: 0, - line_end_offset: 0 - }; - offsets.len() - ]; - let mut with_no_known_line_ending = vec![]; - let mut this_line_offset = 0; - for (pos, ch) in file.chars().enumerate() { - column += 1; - match offset_map.last() { - Some(x) if x.0 == pos => { - let out_idx = x.1; - with_no_known_line_ending.push(out_idx); - out[out_idx].line = line; - out[out_idx].column = column; - out[out_idx].line_start_offset = this_line_offset + 1; - offset_map.pop(); - } - _ => {} - } - if ch == '\n' { - line += 1; - column = 0; - - for idx in with_no_known_line_ending.drain(..) { - out[idx].line_end_offset = pos; - } - this_line_offset = pos; - - if pos == max_offset + 1 { - break; - } - } - } - let file_end = file.chars().count(); - for idx in with_no_known_line_ending { - out[idx].line_end_offset = file_end; - } - - out -} - -#[cfg(test)] -pub mod tests { - use super::{offset_to_location, CodeLocation}; - - #[test] - fn test() { - assert_eq!( - offset_to_location( - "hello world\n_______________________________________________________", - &[0, 14] - ), - vec![ - CodeLocation { - line: 1, - column: 1, - line_start_offset: 0, - line_end_offset: 11 - }, - CodeLocation { - line: 2, - column: 3, - line_start_offset: 11, - line_end_offset: 67 - } - ] - ) - } -} --- a/cmds/jsonnet/src/main.rs +++ /dev/null @@ -1,269 +0,0 @@ -pub mod location; - -use clap::Clap; -use jsonnet_evaluator::{EvaluationState, LocError, StackTrace, Val}; -use location::{offset_to_location, CodeLocation}; -use std::env::current_dir; -use std::{path::PathBuf, str::FromStr}; - -enum Format { - None, - Json, - Yaml, -} - -impl FromStr for Format { - type Err = &'static str; - fn from_str(s: &str) -> Result { - Ok(match s { - "none" => Format::None, - "json" => Format::Json, - "yaml" => Format::Yaml, - _ => return Err("no such format"), - }) - } -} - -#[derive(PartialEq)] -enum TraceFormat { - CppJsonnet, - GoJsonnet, - Custom, -} -impl FromStr for TraceFormat { - type Err = &'static str; - fn from_str(s: &str) -> Result { - Ok(match s { - "cpp" => TraceFormat::CppJsonnet, - "go" => TraceFormat::GoJsonnet, - "default" => TraceFormat::Custom, - _ => return Err("no such format"), - }) - } -} - -#[derive(Clap)] -#[clap(version = "0.1.0", author = "Lach ")] -struct Opts { - #[clap(long, about = "Disable global std variable")] - no_stdlib: bool, - #[clap(long, about = "Add external string")] - ext_str: Option>, - #[clap(long, about = "Add external string from code")] - ext_code: Option>, - #[clap(long, about = "Add TLA")] - tla_str: Option>, - #[clap(long, about = "Add TLA from code")] - tla_code: Option>, - #[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")] - format: Format, - #[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")] - trace_format: TraceFormat, - - #[clap( - long, - short = "s", - default_value = "200", - about = "Number of allowed stack frames" - )] - max_stack: usize, - #[clap( - long, - short = "t", - default_value = "20", - about = "Max length of stack trace before cropping" - )] - max_trace: usize, - - #[clap(about = "File to compile", index = 1)] - input: String, -} - -fn main() { - let opts: Opts = Opts::parse(); - let evaluator = jsonnet_evaluator::EvaluationState::default(); - if !opts.no_stdlib { - evaluator.add_stdlib(); - } - let mut input = current_dir().unwrap(); - input.push(opts.input.clone()); - let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap(); - if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) { - print_syntax_error(e, &input, &code_string); - std::process::exit(2); - } - let result = evaluator.evaluate_file(&input); - match result { - Ok(v) => { - let v = match opts.format { - Format::Json => { - if opts.no_stdlib { - evaluator.add_stdlib(); - } - evaluator.add_global("__tmp__to_json__".to_owned(), v); - let v = evaluator - .parse_evaluate_raw("std.manifestJsonEx(__tmp__to_json__, \" \")"); - match v { - Ok(v) => v, - Err(err) => { - print_error(&err, evaluator, &opts); - std::process::exit(2); - } - } - } - Format::Yaml => { - if opts.no_stdlib { - evaluator.add_stdlib(); - } - evaluator.add_global("__tmp__to_yaml__".to_owned(), v); - let v = evaluator - .parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \" \")"); - match v { - Ok(v) => v, - Err(err) => { - print_error(&err, evaluator, &opts); - std::process::exit(2); - } - } - } - _ => v, - }; - match v { - Val::Str(s) => println!("{}", s), - Val::Num(n) => println!("{}", n), - _v => eprintln!( - "jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?" - ), - } - } - Err(err) => { - print_error(&err, evaluator, &opts); - } - } -} - -fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) { - println!("Error: {:?}", err.0); - print_trace(&(err.1), evaluator, &opts); -} - -fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) { - use annotate_snippets::{ - display_list::{DisplayList, FormatOptions}, - snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, - }; - //&("Expected: ".to_owned() + error.expected) - let origin = file.to_str().unwrap(); - let error_message = format!("Expected: {}", error.expected); - let snippet = Snippet { - opt: FormatOptions { - color: true, - ..Default::default() - }, - title: Some(Annotation { - label: Some(&error_message), - id: None, - annotation_type: AnnotationType::Error, - }), - footer: vec![], - slices: vec![Slice { - source: &code, - line_start: 1, - origin: Some(origin), - fold: false, - annotations: vec![SourceAnnotation { - label: "At this position", - annotation_type: AnnotationType::Error, - range: (error.location.offset, error.location.offset + 1), - }], - }], - }; - - let dl = DisplayList::from(snippet); - println!("{}", dl); -} - -fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) { - use annotate_snippets::{ - display_list::{DisplayList, FormatOptions}, - snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, - }; - for item in trace.0.iter() { - let desc = &item.1; - if (item.0).1.is_none() { - continue; - } - let source = (item.0).1.clone().unwrap(); - let code = evaluator.get_source(&source.0); - if code.is_none() { - continue; - } - let code = code.unwrap(); - let start_end = offset_to_location(&code, &[source.1, source.2]); - if opts.trace_format == TraceFormat::Custom { - let source_fragment: String = code - .chars() - .skip(start_end[0].line_start_offset) - .take(start_end[1].line_end_offset - start_end[0].line_start_offset) - .collect(); - let snippet = Snippet { - opt: FormatOptions { - color: true, - ..Default::default() - }, - title: Some(Annotation { - label: Some(&item.1), - id: None, - annotation_type: AnnotationType::Error, - }), - footer: vec![], - slices: vec![Slice { - source: &source_fragment, - line_start: start_end[0].line, - origin: Some(&source.0.to_str().unwrap()), - fold: false, - annotations: vec![SourceAnnotation { - label: desc, - annotation_type: AnnotationType::Error, - range: ( - source.1 - start_end[0].line_start_offset, - source.2 - start_end[0].line_start_offset, - ), - }], - }], - }; - - let dl = DisplayList::from(snippet); - println!("{}", dl); - } else { - print_jsonnet_pair( - source.0.to_str().unwrap(), - &start_end[0], - &start_end[1], - opts.trace_format == TraceFormat::GoJsonnet, - ); - } - } -} - -fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) { - if is_go { - print!(" "); - } else { - print!(" "); - } - print!("{}:", file); - if start.line == end.line { - // IDK why, but this is the behavior original jsonnet cpp impl shows - if start.column == end.column || !is_go && start.column + 1 == end.column { - println!("{}:{}", start.line, end.column) - } else { - println!("{}:{}-{}", start.line, start.column, end.column); - } - } else { - println!( - "({}:{})-({}:{})", - start.line, end.column, start.line, end.column - ); - } -} -- gitstuff