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