conlang: Move all cl-libs into the compiler directory
This commit is contained in:
14
compiler/cl-repl/src/ansi.rs
Normal file
14
compiler/cl-repl/src/ansi.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
//! ANSI escape sequences
|
||||
|
||||
pub const RED: &str = "\x1b[31m";
|
||||
pub const GREEN: &str = "\x1b[32m"; // the color of type checker mode
|
||||
pub const CYAN: &str = "\x1b[36m";
|
||||
pub const BRIGHT_GREEN: &str = "\x1b[92m";
|
||||
pub const BRIGHT_BLUE: &str = "\x1b[94m";
|
||||
pub const BRIGHT_MAGENTA: &str = "\x1b[95m";
|
||||
pub const BRIGHT_CYAN: &str = "\x1b[96m";
|
||||
pub const RESET: &str = "\x1b[0m";
|
||||
pub const OUTPUT: &str = "\x1b[38;5;117m";
|
||||
|
||||
pub const CLEAR_LINES: &str = "\x1b[G\x1b[J";
|
||||
pub const CLEAR_ALL: &str = "\x1b[H\x1b[2J";
|
||||
51
compiler/cl-repl/src/args.rs
Normal file
51
compiler/cl-repl/src/args.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
//! Handles argument parsing (currently using the [argh] crate)
|
||||
|
||||
use argh::FromArgs;
|
||||
use std::{io::IsTerminal, path::PathBuf, str::FromStr};
|
||||
|
||||
/// The Conlang prototype debug interface
|
||||
#[derive(Clone, Debug, FromArgs, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Args {
|
||||
/// the main source file
|
||||
#[argh(positional)]
|
||||
pub file: Option<PathBuf>,
|
||||
|
||||
/// files to include
|
||||
#[argh(option, short = 'I')]
|
||||
pub include: Vec<PathBuf>,
|
||||
|
||||
/// the CLI operating mode (`f`mt | `l`ex | `r`un)
|
||||
#[argh(option, short = 'm', default = "Default::default()")]
|
||||
pub mode: Mode,
|
||||
|
||||
/// whether to start the repl (`true` or `false`)
|
||||
#[argh(option, short = 'r', default = "is_terminal()")]
|
||||
pub repl: bool,
|
||||
}
|
||||
|
||||
/// gets whether stdin AND stdout are a terminal, for pipelining
|
||||
pub fn is_terminal() -> bool {
|
||||
std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
|
||||
}
|
||||
|
||||
/// The CLI's operating mode
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Mode {
|
||||
#[default]
|
||||
Menu,
|
||||
Lex,
|
||||
Fmt,
|
||||
Run,
|
||||
}
|
||||
|
||||
impl FromStr for Mode {
|
||||
type Err = &'static str;
|
||||
fn from_str(s: &str) -> Result<Self, &'static str> {
|
||||
Ok(match s {
|
||||
"f" | "fmt" | "p" | "pretty" => Mode::Fmt,
|
||||
"l" | "lex" | "tokenize" | "token" => Mode::Lex,
|
||||
"r" | "run" => Mode::Run,
|
||||
_ => Err("Recognized modes are: 'r' \"run\", 'f' \"fmt\", 'l' \"lex\"")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
5
compiler/cl-repl/src/bin/conlang.rs
Normal file
5
compiler/cl-repl/src/bin/conlang.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use cl_repl::cli::run;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
run(argh::from_env())
|
||||
}
|
||||
89
compiler/cl-repl/src/cli.rs
Normal file
89
compiler/cl-repl/src/cli.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
//! Implement's the command line interface
|
||||
use crate::{
|
||||
args::{Args, Mode},
|
||||
ctx::Context,
|
||||
menu,
|
||||
tools::print_token,
|
||||
};
|
||||
use cl_interpret::{env::Environment, interpret::Interpret, temp_type_impl::ConValue};
|
||||
use cl_lexer::Lexer;
|
||||
use cl_parser::Parser;
|
||||
use std::{error::Error, path::Path};
|
||||
|
||||
/// Run the command line interface
|
||||
pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
||||
let Args { file, include, mode, repl } = args;
|
||||
|
||||
let mut env = Environment::new();
|
||||
for path in include {
|
||||
load_file(&mut env, path)?;
|
||||
}
|
||||
|
||||
if repl {
|
||||
if let Some(file) = file {
|
||||
load_file(&mut env, file)?;
|
||||
}
|
||||
let mut ctx = Context::with_env(env);
|
||||
match mode {
|
||||
Mode::Menu => menu::main_menu(&mut ctx)?,
|
||||
Mode::Lex => menu::lex(&mut ctx)?,
|
||||
Mode::Fmt => menu::fmt(&mut ctx)?,
|
||||
Mode::Run => menu::run(&mut ctx)?,
|
||||
}
|
||||
} else {
|
||||
let code = match &file {
|
||||
Some(file) => std::fs::read_to_string(file)?,
|
||||
None => std::io::read_to_string(std::io::stdin())?,
|
||||
};
|
||||
|
||||
match mode {
|
||||
Mode::Lex => lex_code(&code, file),
|
||||
Mode::Fmt => fmt_code(&code),
|
||||
Mode::Run | Mode::Menu => run_code(&code, &mut env),
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_file(
|
||||
env: &mut Environment,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<ConValue, Box<dyn Error>> {
|
||||
let file = std::fs::read_to_string(path)?;
|
||||
let code = Parser::new(Lexer::new(&file)).file()?;
|
||||
Ok(env.eval(&code)?)
|
||||
}
|
||||
|
||||
fn lex_code(code: &str, path: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Error>> {
|
||||
for token in Lexer::new(code) {
|
||||
if let Some(path) = &path {
|
||||
print!("{}:", path.as_ref().display());
|
||||
}
|
||||
match token {
|
||||
Ok(token) => print_token(&token),
|
||||
Err(e) => println!("{e}"),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> {
|
||||
let code = Parser::new(Lexer::new(code)).file()?;
|
||||
println!("{code}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
|
||||
let code = Parser::new(Lexer::new(code)).file()?;
|
||||
match code.interpret(env)? {
|
||||
ConValue::Empty => {}
|
||||
ret => println!("{ret}"),
|
||||
}
|
||||
if env.get("main").is_ok() {
|
||||
match env.call("main", &[])? {
|
||||
ConValue::Empty => {}
|
||||
ret => println!("{ret}"),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
26
compiler/cl-repl/src/ctx.rs
Normal file
26
compiler/cl-repl/src/ctx.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use cl_interpret::{
|
||||
env::Environment, error::IResult, interpret::Interpret, temp_type_impl::ConValue,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Context {
|
||||
pub env: Environment,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Self {
|
||||
Self { env: Environment::new() }
|
||||
}
|
||||
pub fn with_env(env: Environment) -> Self {
|
||||
Self { env }
|
||||
}
|
||||
pub fn run(&mut self, code: &impl Interpret) -> IResult<ConValue> {
|
||||
code.interpret(&mut self.env)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Context {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
11
compiler/cl-repl/src/lib.rs
Normal file
11
compiler/cl-repl/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
//! The Conlang REPL, based on [repline]
|
||||
//!
|
||||
//! Uses [argh] for argument parsing.
|
||||
#![warn(clippy::all)]
|
||||
|
||||
pub mod ansi;
|
||||
pub mod args;
|
||||
pub mod cli;
|
||||
pub mod ctx;
|
||||
pub mod menu;
|
||||
pub mod tools;
|
||||
76
compiler/cl-repl/src/menu.rs
Normal file
76
compiler/cl-repl/src/menu.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use crate::{ansi, ctx};
|
||||
use cl_lexer::Lexer;
|
||||
use cl_parser::Parser;
|
||||
use repline::{error::ReplResult, prebaked::*};
|
||||
|
||||
fn clear() {
|
||||
println!("{}", ansi::CLEAR_ALL);
|
||||
banner()
|
||||
}
|
||||
|
||||
pub fn banner() {
|
||||
println!("--- conlang v{} 💪🦈 ---\n", env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
|
||||
/// Presents a selection interface to the user
|
||||
pub fn main_menu(ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
banner();
|
||||
read_and(ansi::GREEN, "mu>", " ?>", |line| {
|
||||
match line.trim() {
|
||||
"clear" => clear(),
|
||||
"l" | "lex" => lex(ctx)?,
|
||||
"f" | "fmt" => fmt(ctx)?,
|
||||
"r" | "run" => run(ctx)?,
|
||||
"q" | "quit" => return Ok(Response::Break),
|
||||
"h" | "help" => println!(
|
||||
"Valid commands
|
||||
lex (l): Spin up a lexer, and lex some lines
|
||||
fmt (f): Format the input
|
||||
run (r): Enter the REPL, and evaluate some statements
|
||||
help (h): Print this list
|
||||
quit (q): Exit the program"
|
||||
),
|
||||
_ => Err("Unknown command. Type \"help\" for help")?,
|
||||
}
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
read_and(ansi::CYAN, "cl>", " ?>", |line| {
|
||||
let code = Parser::new(Lexer::new(line)).stmt()?;
|
||||
|
||||
print!("{}", ansi::OUTPUT);
|
||||
match ctx.run(&code) {
|
||||
Ok(v) => println!("{}{v}", ansi::RESET),
|
||||
Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
|
||||
}
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lex(_ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
read_and(ansi::BRIGHT_BLUE, "lx>", " ?>", |line| {
|
||||
for token in Lexer::new(line) {
|
||||
match token {
|
||||
Ok(token) => crate::tools::print_token(&token),
|
||||
Err(e) => eprintln!("! > {}{e}{}", ansi::RED, ansi::RESET),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| {
|
||||
let mut p = Parser::new(Lexer::new(line));
|
||||
|
||||
match p.stmt() {
|
||||
Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
|
||||
Err(e) => Err(e)?,
|
||||
}
|
||||
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
11
compiler/cl-repl/src/tools.rs
Normal file
11
compiler/cl-repl/src/tools.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use cl_token::Token;
|
||||
/// Prints a token in the particular way [cl-repl](crate) does
|
||||
pub fn print_token(t: &Token) {
|
||||
println!(
|
||||
"{:02}:{:02}: {:#19} │{}│",
|
||||
t.line(),
|
||||
t.col(),
|
||||
t.ty(),
|
||||
t.data(),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user