repl format
All checks were successful
docs / test (push) Successful in 9s

This commit is contained in:
trimill 2025-01-02 22:31:19 -05:00
parent c75dafac8e
commit 2c50b04108
9 changed files with 100 additions and 17 deletions

4
Cargo.lock generated
View file

@ -445,9 +445,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
version = "2.0.93"
version = "2.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
dependencies = [
"proc-macro2",
"quote",

View file

@ -27,6 +27,17 @@ Use the up and down arrows to cycle through previous entries. By specifiying
a history file with the `-H`/`--histfile` argument, history can be preserved
between sessions.
## Output formatting
The `_fmt` global variable can be used to control output formatting. `_fmt` is
a table where each key is a type and the corresponding value is the format
string used to display that type. For example, setting `_fmt.int = "0x{:X}"`
will cause the REPL to print integers in hexadecimal with a `0x` prefix.
Values may also be `false` to disable printing entirely. Any other value will
print using the default format. Initially, the value of `_fmt` is
`{ :nil=false }`.
## Advanced features
Use the `-a`/`--show-ast` flag to print the AST generated from the expression.
@ -34,3 +45,4 @@ Use the `-d`/`--disasm` flag to show the disassembled bytecode generated by the
compiler.
The `--no-opt` flag disables the optimization step, which may affect the
bytecode but should not affect behavior.

View file

@ -93,6 +93,7 @@ false
true
>> 1 + 3i < 5
Error: type_error: cannot compare 1.0+3.0i and 5
in <repl> at 4
```
The boolean operators `and`, `or`, and `not` perform the expected operations

View file

@ -11,6 +11,7 @@ message (which must be a string).
```talc
>> throw(:my_exception, "something bad happened!")
Error: my_exception: something bad happened!
in <repl> at 1
```
The exception can then be caught in a try-catch block:

View file

@ -57,6 +57,7 @@ you to write multiline programs.
45
>> y
Error: name_error: undefined global y
in <repl> at 4
```
Later we will see other expressions that create scopes, so it is important to

View file

@ -1,4 +1,9 @@
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
use std::{
cell::RefCell,
io::{stdout, IsTerminal, Write},
process::ExitCode,
rc::Rc,
};
use clap::ColorChoice;
use rustyline::{
@ -22,6 +27,7 @@ lazy_static::lazy_static! {
pub static ref SYM_PREV1: Symbol = symbol!("_");
pub static ref SYM_PREV2: Symbol = symbol!("__");
pub static ref SYM_PREV3: Symbol = symbol!("___");
pub static ref SYM__FMT: Symbol = symbol!("_fmt");
}
#[derive(Clone, Copy, Default)]
@ -98,6 +104,32 @@ fn read_init_file(args: &Args) -> Result<Option<String>, std::io::Error> {
std::fs::read_to_string(file.to_os_str()).map(Some)
}
fn print_result(vm: &Vm, result: Value, c: &ReplColors) {
let ty = result.get_type();
let Some(Value::Table(g_fmt)) = vm.get_global(*SYM__FMT) else {
println!("{result:#}");
return
};
let g_fmt_ref = g_fmt.borrow();
let fmt = g_fmt_ref.get(&ty.into()).unwrap_or(&Value::Nil);
match fmt {
Value::String(fstr) => {
let res = talc_std::format::fmt_inner(fstr, &[result]);
match res {
Ok(s) => {
stdout().write_all(s.as_bytes()).expect("output failed");
stdout().write_all(b"\n").expect("output failed");
}
Err(e) => {
eprintln!("{}Error(fmt):{} {e}", c.error, c.reset);
}
}
}
Value::Bool(false) => (),
_ => println!("{result:#}"),
}
}
fn exec_line(
vm: &mut Vm,
line: &str,
@ -144,9 +176,7 @@ fn exec_line(
let prev1 = vm.get_global(*SYM_PREV1).unwrap().clone();
vm.set_global(*SYM_PREV2, prev1);
vm.set_global(*SYM_PREV1, v.clone());
if v != Value::Nil {
println!("{v:#}");
}
print_result(vm, v, c);
}
Err(e) => eprintln!("{}Error:{} {e}", c.error, c.reset),
}
@ -178,6 +208,12 @@ pub fn repl(args: &Args) -> ExitCode {
vm.set_global(*SYM_PREV1, Value::Nil);
vm.set_global(*SYM_PREV2, Value::Nil);
vm.set_global(*SYM_PREV3, Value::Nil);
vm.set_global(
*SYM__FMT,
Value::new_table(|table| {
table.insert(symbol!(nil).into(), Value::Bool(false));
}),
);
let init_src = match read_init_file(args) {
Ok(s) => s,

View file

@ -6,6 +6,32 @@ use super::{ParserError, Pos, Span};
type Result<T> = std::result::Result<T, ParserError>;
pub static KEYWORDS: &[&[u8]] = &[
b"and",
b"break",
b"catch",
b"continue",
b"do",
b"elif",
b"else",
b"end",
b"false",
b"fn",
b"for",
b"global",
b"if",
b"in",
b"nil",
b"not",
b"or",
b"return",
b"then",
b"true",
b"try",
b"var",
b"while",
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenKind {
Eof,

View file

@ -8,6 +8,7 @@ use num::{complex::Complex64, BigInt};
use crate::exception::{throw, Exception};
use crate::lstring::{LStr, LString};
use crate::number::{Int, Range, Ratio};
use crate::parser::KEYWORDS;
use crate::prelude::*;
use crate::symbol::{Symbol, SYM_HASH_ERROR};
@ -254,7 +255,7 @@ impl Value {
}
Self::Symbol(s) => {
let name = s.name();
if name.is_identifier() {
if name.is_identifier() && !KEYWORDS.contains(&name.as_bytes()) {
w.push_lstr(name);
Ok(())
} else {

View file

@ -438,15 +438,7 @@ fn format_arg(
Ok(())
}
#[native_func(2, "fmt")]
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, fstr, fargs] = unpack_args!(args);
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
throw!(
*SYM_TYPE_ERROR,
"format expected string and list, got {fstr:#} and {fargs:#}"
)
};
pub fn fmt_inner(fstr: &LStr, fargs: &[Value]) -> Result<LString> {
let mut res = LString::new();
let mut faidx = 0;
let mut i = 0;
@ -493,8 +485,21 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
let code = &fstr[start..i];
i += 1;
format_arg(&fargs.borrow(), &mut faidx, code, &mut res)?;
format_arg(fargs, &mut faidx, code, &mut res)?;
}
Ok(res)
}
#[native_func(2, "fmt")]
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, fstr, fargs] = unpack_args!(args);
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
throw!(
*SYM_TYPE_ERROR,
"format expected string and list, got {fstr:#} and {fargs:#}"
)
};
let res = fmt_inner(fstr, &fargs.borrow())?;
Ok(res.into())
}