add string interpolation
This commit is contained in:
parent
82ac6744ca
commit
cda217f6ef
|
@ -1,4 +1,5 @@
|
||||||
use crate::parser::Ast;
|
use crate::parser::Ast;
|
||||||
|
use crate::parser::StringPart;
|
||||||
use crate::spans::Spanned;
|
use crate::spans::Spanned;
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use chumsky::prelude::SimpleSpan;
|
use chumsky::prelude::SimpleSpan;
|
||||||
|
@ -6,6 +7,7 @@ use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::string;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
|
@ -53,6 +55,10 @@ pub enum Op {
|
||||||
Decrement,
|
Decrement,
|
||||||
Truncate,
|
Truncate,
|
||||||
MatchDepth,
|
MatchDepth,
|
||||||
|
Panic,
|
||||||
|
EmptyString,
|
||||||
|
ConcatStrings,
|
||||||
|
Stringify,
|
||||||
|
|
||||||
Call,
|
Call,
|
||||||
|
|
||||||
|
@ -156,6 +162,10 @@ impl std::fmt::Display for Op {
|
||||||
Truncate => "truncate",
|
Truncate => "truncate",
|
||||||
Duplicate => "duplicate",
|
Duplicate => "duplicate",
|
||||||
MatchDepth => "match_depth",
|
MatchDepth => "match_depth",
|
||||||
|
Panic => "panic",
|
||||||
|
EmptyString => "empty_string",
|
||||||
|
ConcatStrings => "concat_strings",
|
||||||
|
Stringify => "stringify",
|
||||||
|
|
||||||
Eq => "eq",
|
Eq => "eq",
|
||||||
Add => "add",
|
Add => "add",
|
||||||
|
@ -199,7 +209,8 @@ impl Chunk {
|
||||||
Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||||
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch
|
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch
|
||||||
| TypeOf | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq
|
| TypeOf | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq
|
||||||
| Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not => {
|
| Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic
|
||||||
|
| EmptyString | ConcatStrings | Stringify => {
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -1094,10 +1105,40 @@ impl Compiler {
|
||||||
self.emit_op(Op::JumpBack);
|
self.emit_op(Op::JumpBack);
|
||||||
self.emit_byte(self.len() - self.loop_idx());
|
self.emit_byte(self.len() - self.loop_idx());
|
||||||
}
|
}
|
||||||
Interpolated(..)
|
Panic(msg) => {
|
||||||
| Arguments(..)
|
self.visit(msg);
|
||||||
|
self.emit_op(Op::Panic);
|
||||||
|
}
|
||||||
|
Interpolated(parts) => {
|
||||||
|
self.emit_op(Op::EmptyString);
|
||||||
|
self.stack_depth += 1;
|
||||||
|
for part in parts {
|
||||||
|
let str = &part.0;
|
||||||
|
match str {
|
||||||
|
StringPart::Inline(_) => unreachable!(),
|
||||||
|
StringPart::Data(str) => {
|
||||||
|
let allocated = Value::String(Rc::new(str.clone()));
|
||||||
|
self.emit_constant(allocated);
|
||||||
|
self.emit_op(Op::ConcatStrings);
|
||||||
|
self.stack_depth -= 1;
|
||||||
|
}
|
||||||
|
StringPart::Word(word) => {
|
||||||
|
self.emit_op(Op::PushBinding);
|
||||||
|
let biter = self.bindings.iter().rev();
|
||||||
|
for binding in biter {
|
||||||
|
if binding.name == word.as_str() {
|
||||||
|
self.emit_byte(binding.stack_pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.emit_op(Op::Stringify);
|
||||||
|
self.emit_op(Op::ConcatStrings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Arguments(..)
|
||||||
| Placeholder
|
| Placeholder
|
||||||
| Panic(..)
|
|
||||||
| Do(..)
|
| Do(..)
|
||||||
| Splat(..)
|
| Splat(..)
|
||||||
| InterpolatedPattern(..)
|
| InterpolatedPattern(..)
|
||||||
|
@ -1119,7 +1160,7 @@ impl Compiler {
|
||||||
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
||||||
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple
|
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple
|
||||||
| LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At
|
| LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At
|
||||||
| Not => {
|
| Not | Panic | EmptyString | ConcatStrings | Stringify => {
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
|
|
@ -65,7 +65,7 @@ pub fn run(src: &'static str) {
|
||||||
let result = vm.run();
|
let result = vm.run();
|
||||||
let output = match result {
|
let output = match result {
|
||||||
Ok(val) => val.show(&compiler.chunk),
|
Ok(val) => val.show(&compiler.chunk),
|
||||||
Err(panic) => format!("{:?}", panic),
|
Err(panic) => format!("Ludus panicked! {panic}"),
|
||||||
};
|
};
|
||||||
vm.print_stack();
|
vm.print_stack();
|
||||||
println!("{output}");
|
println!("{output}");
|
||||||
|
@ -74,7 +74,10 @@ pub fn run(src: &'static str) {
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
env::set_var("RUST_BACKTRACE", "1");
|
env::set_var("RUST_BACKTRACE", "1");
|
||||||
let src = "
|
let src = "
|
||||||
or (true, true, 42)
|
let x = 42
|
||||||
|
let y = :foo
|
||||||
|
let z = \"thing\"
|
||||||
|
\"{x} {y} {z}\"
|
||||||
";
|
";
|
||||||
run(src);
|
run(src);
|
||||||
}
|
}
|
||||||
|
|
56
src/value.rs
56
src/value.rs
|
@ -51,6 +51,7 @@ impl std::fmt::Display for Value {
|
||||||
False => write!(f, "false"),
|
False => write!(f, "false"),
|
||||||
Keyword(idx) => write!(f, ":{idx}"),
|
Keyword(idx) => write!(f, ":{idx}"),
|
||||||
Interned(idx) => write!(f, "\"@{idx}\""),
|
Interned(idx) => write!(f, "\"@{idx}\""),
|
||||||
|
String(str) => write!(f, "\"{str}\""),
|
||||||
Number(n) => write!(f, "{n}"),
|
Number(n) => write!(f, "{n}"),
|
||||||
Tuple(members) => write!(
|
Tuple(members) => write!(
|
||||||
f,
|
f,
|
||||||
|
@ -98,6 +99,10 @@ impl Value {
|
||||||
let str_str = ctx.strings[*i];
|
let str_str = ctx.strings[*i];
|
||||||
format!("\"{str_str}\"")
|
format!("\"{str_str}\"")
|
||||||
}
|
}
|
||||||
|
String(str) => {
|
||||||
|
let str_str = str.to_string();
|
||||||
|
format!("\"{str_str}\"")
|
||||||
|
}
|
||||||
Keyword(i) => {
|
Keyword(i) => {
|
||||||
let kw_str = ctx.keywords[*i];
|
let kw_str = ctx.keywords[*i];
|
||||||
format!(":{kw_str}")
|
format!(":{kw_str}")
|
||||||
|
@ -122,13 +127,62 @@ impl Value {
|
||||||
.join(", ");
|
.join(", ");
|
||||||
format!("#{{{members}}}")
|
format!("#{{{members}}}")
|
||||||
}
|
}
|
||||||
String(s) => s.as_ref().clone(),
|
|
||||||
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show(ctx)),
|
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show(ctx)),
|
||||||
Fn(lfn) => format!("fn {}", lfn.get().unwrap().name),
|
Fn(lfn) => format!("fn {}", lfn.get().unwrap().name),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stringify(&self, ctx: &Chunk) -> String {
|
||||||
|
use Value::*;
|
||||||
|
match &self {
|
||||||
|
Nil => "nil".to_string(),
|
||||||
|
True => "true".to_string(),
|
||||||
|
False => "false".to_string(),
|
||||||
|
Number(n) => format!("{n}"),
|
||||||
|
Interned(i) => {
|
||||||
|
let str_str = ctx.strings[*i];
|
||||||
|
str_str.to_string()
|
||||||
|
}
|
||||||
|
Keyword(i) => {
|
||||||
|
let kw_str = ctx.keywords[*i];
|
||||||
|
format!(":{kw_str}")
|
||||||
|
}
|
||||||
|
Tuple(t) => {
|
||||||
|
let members = t
|
||||||
|
.iter()
|
||||||
|
.map(|e| e.stringify(ctx))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
members.to_string()
|
||||||
|
}
|
||||||
|
List(l) => {
|
||||||
|
let members = l
|
||||||
|
.iter()
|
||||||
|
.map(|e| e.stringify(ctx))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
members.to_string()
|
||||||
|
}
|
||||||
|
Dict(d) => {
|
||||||
|
let members = d
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
let key_show = Value::Keyword(*k).show(ctx);
|
||||||
|
let value_show = v.show(ctx);
|
||||||
|
format!("{key_show} {value_show}")
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
members.to_string()
|
||||||
|
}
|
||||||
|
String(s) => s.as_ref().clone(),
|
||||||
|
Box(x) => x.as_ref().borrow().stringify(ctx),
|
||||||
|
Fn(lfn) => lfn.get().unwrap().name.to_string(),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn type_of(&self) -> &'static str {
|
pub fn type_of(&self) -> &'static str {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match self {
|
match self {
|
||||||
|
|
50
src/vm.rs
50
src/vm.rs
|
@ -6,6 +6,7 @@ use chumsky::prelude::SimpleSpan;
|
||||||
use imbl::{HashMap, Vector};
|
use imbl::{HashMap, Vector};
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::fmt;
|
||||||
use std::mem::swap;
|
use std::mem::swap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -18,7 +19,19 @@ use std::rc::Rc;
|
||||||
// pub trace: Vec<Trace>,
|
// pub trace: Vec<Trace>,
|
||||||
// pub extra: String,
|
// pub extra: String,
|
||||||
// }
|
// }
|
||||||
pub struct Panic(&'static str);
|
pub enum Panic {
|
||||||
|
Str(&'static str),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Panic {
|
||||||
|
fn fmt(self: &Panic, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Panic::Str(msg) => write!(f, "{msg}"),
|
||||||
|
Panic::String(msg) => write!(f, "{msg}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Trace {
|
pub struct Trace {
|
||||||
|
@ -102,7 +115,11 @@ impl<'a> Vm<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panic(&mut self, msg: &'static str) {
|
pub fn panic(&mut self, msg: &'static str) {
|
||||||
self.result = Some(Err(Panic(msg)));
|
self.result = Some(Err(Panic::Str(msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn panic_with(&mut self, msg: String) {
|
||||||
|
self.result = Some(Err(Panic::String(msg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(&mut self) {
|
pub fn interpret(&mut self) {
|
||||||
|
@ -554,6 +571,35 @@ impl<'a> Vm<'a> {
|
||||||
self.push(negated);
|
self.push(negated);
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
}
|
}
|
||||||
|
Panic => {
|
||||||
|
let msg = self.pop().show(self.chunk);
|
||||||
|
self.panic_with(msg);
|
||||||
|
}
|
||||||
|
EmptyString => {
|
||||||
|
self.push(Value::String(Rc::new("".to_string())));
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
ConcatStrings => {
|
||||||
|
let second = self.pop();
|
||||||
|
let first = self.pop();
|
||||||
|
let combined = match (first, second) {
|
||||||
|
(Value::String(first), Value::String(second)) => {
|
||||||
|
let mut newstr = first.as_ref().clone();
|
||||||
|
newstr.push_str(second.as_str());
|
||||||
|
Value::String(Rc::new(newstr))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
self.push(combined);
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
|
Stringify => {
|
||||||
|
let to_stringify = self.pop();
|
||||||
|
let the_string = to_stringify.stringify(self.chunk);
|
||||||
|
let stringified = Value::String(Rc::new(the_string));
|
||||||
|
self.push(stringified);
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
Call => todo!(),
|
Call => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user