Refactor CLI
This commit is contained in:
parent
358436c470
commit
e5742f6294
@ -2,7 +2,7 @@ use std::io::{stdout, Write};
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{fs::read_to_string, path::PathBuf};
|
use std::{fs::read_to_string, path::PathBuf};
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use dust_lang::{compile, lex, run, write_token_list};
|
use dust_lang::{compile, lex, run, write_token_list};
|
||||||
use log::{Level, LevelFilter};
|
use log::{Level, LevelFilter};
|
||||||
@ -13,72 +13,63 @@ use log::{Level, LevelFilter};
|
|||||||
author = env!("CARGO_PKG_AUTHORS"),
|
author = env!("CARGO_PKG_AUTHORS"),
|
||||||
about = env!("CARGO_PKG_DESCRIPTION"),
|
about = env!("CARGO_PKG_DESCRIPTION"),
|
||||||
)]
|
)]
|
||||||
#[command(args_conflicts_with_subcommands = true)]
|
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[command(flatten)]
|
|
||||||
global_arguments: GlobalArguments,
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
mode: Option<CliMode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
enum CliMode {
|
|
||||||
/// Run the source code (default)
|
|
||||||
#[command(short_flag = 'r')]
|
|
||||||
Run {
|
|
||||||
#[command(flatten)]
|
|
||||||
global_arguments: GlobalArguments,
|
|
||||||
|
|
||||||
/// Do not print the program's output value
|
|
||||||
#[arg(short, long)]
|
|
||||||
no_output: bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Compile a chunk and show the disassembly
|
|
||||||
#[command(short_flag = 'd')]
|
|
||||||
Disassemble {
|
|
||||||
#[command(flatten)]
|
|
||||||
global_arguments: GlobalArguments,
|
|
||||||
|
|
||||||
/// Style the disassembly output
|
|
||||||
#[arg(short, long, default_value = "true")]
|
|
||||||
style: bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Create and display tokens from the source code
|
|
||||||
#[command(short_flag = 't')]
|
|
||||||
Tokenize {
|
|
||||||
#[command(flatten)]
|
|
||||||
global_arguments: GlobalArguments,
|
|
||||||
|
|
||||||
/// Style the disassembly output
|
|
||||||
#[arg(short, long, default_value = "true")]
|
|
||||||
style: bool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
|
||||||
struct GlobalArguments {
|
|
||||||
/// Log level, overrides the DUST_LOG environment variable
|
/// Log level, overrides the DUST_LOG environment variable
|
||||||
///
|
///
|
||||||
/// Possible values: trace, debug, info, warn, error
|
/// Possible values: trace, debug, info, warn, error
|
||||||
#[arg(short, long, value_name = "LOG_LEVEL")]
|
#[arg(short, long, global = true, value_name = "LOG_LEVEL")]
|
||||||
log: Option<LevelFilter>,
|
log: Option<LevelFilter>,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
mode: ModeFlags,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
source: Source,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
#[group(required = true, multiple = false)]
|
||||||
|
struct ModeFlags {
|
||||||
|
/// Run the source code (default)
|
||||||
|
#[arg(short, long)]
|
||||||
|
run: bool,
|
||||||
|
|
||||||
|
#[arg(long, requires("run"))]
|
||||||
|
/// Do not print the run result
|
||||||
|
no_output: bool,
|
||||||
|
|
||||||
|
/// Compile a chunk and show the disassembly
|
||||||
|
#[arg(short, long)]
|
||||||
|
disassemble: bool,
|
||||||
|
|
||||||
|
/// Lex and display tokens from the source code
|
||||||
|
#[arg(short, long)]
|
||||||
|
tokenize: bool,
|
||||||
|
|
||||||
|
/// Style disassembly or tokenization output
|
||||||
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
default_value = "true",
|
||||||
|
requires("disassemble"),
|
||||||
|
requires("tokenize")
|
||||||
|
)]
|
||||||
|
style: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
#[group(required = true, multiple = false)]
|
||||||
|
struct Source {
|
||||||
/// Source code
|
/// Source code
|
||||||
///
|
#[arg(short, long, value_name = "SOURCE")]
|
||||||
/// Conflicts with the file argument
|
|
||||||
#[arg(short, long, value_name = "SOURCE", conflicts_with = "file")]
|
|
||||||
command: Option<String>,
|
command: Option<String>,
|
||||||
|
|
||||||
/// Path to a source code file
|
/// Path to a source code file
|
||||||
#[arg(required_unless_present = "command")]
|
|
||||||
file: Option<PathBuf>,
|
file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_log_and_get_source(arguments: GlobalArguments, start_time: Instant) -> String {
|
fn main() {
|
||||||
let GlobalArguments { command, file, log } = arguments;
|
let start_time = Instant::now();
|
||||||
let mut logger = env_logger::builder();
|
let mut logger = env_logger::builder();
|
||||||
|
|
||||||
logger.format(move |buf, record| {
|
logger.format(move |buf, record| {
|
||||||
@ -95,43 +86,32 @@ fn set_log_and_get_source(arguments: GlobalArguments, start_time: Instant) -> St
|
|||||||
writeln!(buf, "{display}")
|
writeln!(buf, "{display}")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let Cli {
|
||||||
|
log,
|
||||||
|
source: Source { command, file },
|
||||||
|
mode,
|
||||||
|
} = Cli::parse();
|
||||||
|
|
||||||
if let Some(level) = log {
|
if let Some(level) = log {
|
||||||
logger.filter_level(level).init();
|
logger.filter_level(level).init();
|
||||||
} else {
|
} else {
|
||||||
logger.parse_env("DUST_LOG").init();
|
logger.parse_env("DUST_LOG").init();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(source) = command {
|
let source = if let Some(source) = command {
|
||||||
source
|
source
|
||||||
} else {
|
} else {
|
||||||
let path = file.expect("Path is required when command is not provided");
|
let path = file.expect("Path is required when command is not provided");
|
||||||
|
|
||||||
read_to_string(path).expect("Failed to read file")
|
read_to_string(path).expect("Failed to read file")
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
if mode.run {
|
||||||
let start_time = Instant::now();
|
|
||||||
let Cli {
|
|
||||||
global_arguments,
|
|
||||||
mode,
|
|
||||||
} = Cli::parse();
|
|
||||||
let mode = mode.unwrap_or(CliMode::Run {
|
|
||||||
global_arguments,
|
|
||||||
no_output: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if let CliMode::Run {
|
|
||||||
global_arguments,
|
|
||||||
no_output,
|
|
||||||
} = mode
|
|
||||||
{
|
|
||||||
let source = set_log_and_get_source(global_arguments, start_time);
|
|
||||||
let run_result = run(&source);
|
let run_result = run(&source);
|
||||||
|
|
||||||
match run_result {
|
match run_result {
|
||||||
Ok(Some(value)) => {
|
Ok(Some(value)) => {
|
||||||
if !no_output {
|
if !mode.no_output {
|
||||||
println!("{}", value)
|
println!("{}", value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,12 +124,7 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let CliMode::Disassemble {
|
if mode.disassemble {
|
||||||
global_arguments,
|
|
||||||
style,
|
|
||||||
} = mode
|
|
||||||
{
|
|
||||||
let source = set_log_and_get_source(global_arguments, start_time);
|
|
||||||
let chunk = match compile(&source) {
|
let chunk = match compile(&source) {
|
||||||
Ok(chunk) => chunk,
|
Ok(chunk) => chunk,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@ -160,7 +135,7 @@ fn main() {
|
|||||||
};
|
};
|
||||||
let disassembly = chunk
|
let disassembly = chunk
|
||||||
.disassembler()
|
.disassembler()
|
||||||
.style(style)
|
.style(mode.style)
|
||||||
.source(&source)
|
.source(&source)
|
||||||
.disassemble();
|
.disassemble();
|
||||||
|
|
||||||
@ -169,12 +144,7 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let CliMode::Tokenize {
|
if mode.tokenize {
|
||||||
global_arguments,
|
|
||||||
style,
|
|
||||||
} = mode
|
|
||||||
{
|
|
||||||
let source = set_log_and_get_source(global_arguments, start_time);
|
|
||||||
let tokens = match lex(&source) {
|
let tokens = match lex(&source) {
|
||||||
Ok(tokens) => tokens,
|
Ok(tokens) => tokens,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@ -185,7 +155,7 @@ fn main() {
|
|||||||
};
|
};
|
||||||
let mut stdout = stdout().lock();
|
let mut stdout = stdout().lock();
|
||||||
|
|
||||||
write_token_list(&tokens, style, &mut stdout)
|
write_token_list(&tokens, mode.style, &mut stdout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user