This commit is contained in:
parent
c75dafac8e
commit
2c50b04108
9 changed files with 100 additions and 17 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -445,9 +445,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.93"
|
version = "2.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
|
checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -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
|
a history file with the `-H`/`--histfile` argument, history can be preserved
|
||||||
between sessions.
|
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
|
## Advanced features
|
||||||
|
|
||||||
Use the `-a`/`--show-ast` flag to print the AST generated from the expression.
|
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.
|
compiler.
|
||||||
The `--no-opt` flag disables the optimization step, which may affect the
|
The `--no-opt` flag disables the optimization step, which may affect the
|
||||||
bytecode but should not affect behavior.
|
bytecode but should not affect behavior.
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ false
|
||||||
true
|
true
|
||||||
>> 1 + 3i < 5
|
>> 1 + 3i < 5
|
||||||
Error: type_error: cannot compare 1.0+3.0i and 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
|
The boolean operators `and`, `or`, and `not` perform the expected operations
|
||||||
|
|
|
@ -11,6 +11,7 @@ message (which must be a string).
|
||||||
```talc
|
```talc
|
||||||
>> throw(:my_exception, "something bad happened!")
|
>> throw(:my_exception, "something bad happened!")
|
||||||
Error: 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:
|
The exception can then be caught in a try-catch block:
|
||||||
|
|
|
@ -57,6 +57,7 @@ you to write multiline programs.
|
||||||
45
|
45
|
||||||
>> y
|
>> y
|
||||||
Error: name_error: undefined global 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
|
Later we will see other expressions that create scopes, so it is important to
|
||||||
|
|
|
@ -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 clap::ColorChoice;
|
||||||
use rustyline::{
|
use rustyline::{
|
||||||
|
@ -22,6 +27,7 @@ lazy_static::lazy_static! {
|
||||||
pub static ref SYM_PREV1: Symbol = symbol!("_");
|
pub static ref SYM_PREV1: Symbol = symbol!("_");
|
||||||
pub static ref SYM_PREV2: Symbol = symbol!("__");
|
pub static ref SYM_PREV2: Symbol = symbol!("__");
|
||||||
pub static ref SYM_PREV3: Symbol = symbol!("___");
|
pub static ref SYM_PREV3: Symbol = symbol!("___");
|
||||||
|
pub static ref SYM__FMT: Symbol = symbol!("_fmt");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[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)
|
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(
|
fn exec_line(
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
line: &str,
|
line: &str,
|
||||||
|
@ -144,9 +176,7 @@ fn exec_line(
|
||||||
let prev1 = vm.get_global(*SYM_PREV1).unwrap().clone();
|
let prev1 = vm.get_global(*SYM_PREV1).unwrap().clone();
|
||||||
vm.set_global(*SYM_PREV2, prev1);
|
vm.set_global(*SYM_PREV2, prev1);
|
||||||
vm.set_global(*SYM_PREV1, v.clone());
|
vm.set_global(*SYM_PREV1, v.clone());
|
||||||
if v != Value::Nil {
|
print_result(vm, v, c);
|
||||||
println!("{v:#}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("{}Error:{} {e}", c.error, c.reset),
|
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_PREV1, Value::Nil);
|
||||||
vm.set_global(*SYM_PREV2, Value::Nil);
|
vm.set_global(*SYM_PREV2, Value::Nil);
|
||||||
vm.set_global(*SYM_PREV3, 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) {
|
let init_src = match read_init_file(args) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
|
|
@ -6,6 +6,32 @@ use super::{ParserError, Pos, Span};
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, ParserError>;
|
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)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum TokenKind {
|
pub enum TokenKind {
|
||||||
Eof,
|
Eof,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use num::{complex::Complex64, BigInt};
|
||||||
use crate::exception::{throw, Exception};
|
use crate::exception::{throw, Exception};
|
||||||
use crate::lstring::{LStr, LString};
|
use crate::lstring::{LStr, LString};
|
||||||
use crate::number::{Int, Range, Ratio};
|
use crate::number::{Int, Range, Ratio};
|
||||||
|
use crate::parser::KEYWORDS;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
||||||
|
|
||||||
|
@ -254,7 +255,7 @@ impl Value {
|
||||||
}
|
}
|
||||||
Self::Symbol(s) => {
|
Self::Symbol(s) => {
|
||||||
let name = s.name();
|
let name = s.name();
|
||||||
if name.is_identifier() {
|
if name.is_identifier() && !KEYWORDS.contains(&name.as_bytes()) {
|
||||||
w.push_lstr(name);
|
w.push_lstr(name);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -438,15 +438,7 @@ fn format_arg(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2, "fmt")]
|
pub fn fmt_inner(fstr: &LStr, fargs: &[Value]) -> Result<LString> {
|
||||||
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 mut res = LString::new();
|
let mut res = LString::new();
|
||||||
let mut faidx = 0;
|
let mut faidx = 0;
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
@ -493,8 +485,21 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
let code = &fstr[start..i];
|
let code = &fstr[start..i];
|
||||||
i += 1;
|
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())
|
Ok(res.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue