diff --git a/compiler/cl-interpret/src/builtin.rs b/compiler/cl-interpret/src/builtin.rs index 63249af..8d03883 100644 --- a/compiler/cl-interpret/src/builtin.rs +++ b/compiler/cl-interpret/src/builtin.rs @@ -178,6 +178,7 @@ pub const Builtins: &[Builtin] = &builtins![ fn len(list) @env { Ok(match list { ConValue::Empty => 0, + ConValue::Str(s) => s.chars().count() as _, ConValue::String(s) => s.chars().count() as _, ConValue::Ref(r) => { return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()]) @@ -203,6 +204,7 @@ pub const Builtins: &[Builtin] = &builtins![ fn chars(string) @env { Ok(match string { + ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()), ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()), ConValue::Ref(r) => { return chars(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()]) @@ -259,7 +261,15 @@ pub const Math: &[Builtin] = &builtins![ Ok(match (lhs, rhs) { (ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b), - (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(), + (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(), + (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(), + (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(), + (ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(), + (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() } + (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() } + (ConValue::Char(a), ConValue::Char(b)) => { + ConValue::String([a, b].into_iter().collect::()) + } _ => Err(Error::TypeError())? }) } @@ -375,6 +385,9 @@ pub const Math: &[Builtin] = &builtins![ (ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _, (ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _, (ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _, + (ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _, + (ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _, + (ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _, (ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _, _ => Err(error_format!("Incomparable values: {head}, {tail}"))? })) diff --git a/compiler/cl-interpret/src/convalue.rs b/compiler/cl-interpret/src/convalue.rs index dfd685f..f65d82c 100644 --- a/compiler/cl-interpret/src/convalue.rs +++ b/compiler/cl-interpret/src/convalue.rs @@ -51,8 +51,10 @@ pub enum ConValue { Bool(bool), /// A unicode character Char(char), - /// A string - String(Sym), + /// A string literal + Str(Sym), + /// A dynamic string + String(String), /// A reference Ref(usize), /// A reference to an array @@ -97,6 +99,7 @@ impl ConValue { ConValue::Float(_) => "f64", ConValue::Bool(_) => "bool", ConValue::Char(_) => "char", + ConValue::Str(_) => "str", ConValue::String(_) => "String", ConValue::Ref(_) => "Ref", ConValue::Slice(_, _) => "Slice", @@ -128,6 +131,11 @@ impl ConValue { Err(Error::TypeError())? }; match self { + ConValue::Str(string) => string + .chars() + .nth(index as _) + .map(ConValue::Char) + .ok_or(Error::OobIndex(index as usize, string.chars().count())), ConValue::String(string) => string .chars() .nth(index as _) @@ -216,6 +224,9 @@ macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$( (Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)), (Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)), (Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)), + (Self::Str(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)), + (Self::Str(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)), + (Self::String(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)), (Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)), _ => Err(Error::TypeError()) } @@ -235,7 +246,7 @@ macro from ($($T:ty => $v:expr),*$(,)?) { } impl From<&Sym> for ConValue { fn from(value: &Sym) -> Self { - ConValue::String(*value) + ConValue::Str(*value) } } from! { @@ -243,11 +254,11 @@ from! { f64 => ConValue::Float, bool => ConValue::Bool, char => ConValue::Char, - Sym => ConValue::String, - &str => ConValue::String, + Sym => ConValue::Str, + &str => ConValue::Str, Expr => ConValue::Quote, String => ConValue::String, - Rc => ConValue::String, + Rc => ConValue::Str, Function => ConValue::Function, Vec => ConValue::Tuple, &'static Builtin => ConValue::Builtin, @@ -282,10 +293,14 @@ ops! { (ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)), (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b), + (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + &*b).into(), + (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + &*b).into(), + (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + &*b).into(), (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(), + (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() } (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() } (ConValue::Char(a), ConValue::Char(b)) => { - ConValue::String([a, b].into_iter().collect::().into()) + ConValue::String([a, b].into_iter().collect::()) } _ => Err(Error::TypeError())? ] @@ -354,6 +369,7 @@ impl std::fmt::Display for ConValue { ConValue::Float(v) => v.fmt(f), ConValue::Bool(v) => v.fmt(f), ConValue::Char(v) => v.fmt(f), + ConValue::Str(v) => v.fmt(f), ConValue::String(v) => v.fmt(f), ConValue::Ref(v) => write!(f, "&<{}>", v), ConValue::Slice(id, len) => write!(f, "&<{id}>[{len}..]"), diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index af3168b..9003dc7 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -699,7 +699,7 @@ fn cast(env: &Environment, value: ConValue, ty: Sym) -> IResult { // TODO: This, better ConValue::Float(_) if ty.starts_with('f') => return Ok(value), ConValue::Float(f) => f as _, - _ if (*ty).eq("str") => return Ok(ConValue::String(format!("{value}").into())), + _ if (*ty).eq("str") => return Ok(ConValue::Str(format!("{value}").into())), _ => Err(Error::TypeError())?, }; Ok(match &*ty { @@ -958,7 +958,7 @@ impl Interpret for For { &ConValue::Slice(head, len) => Box::new((head..head + len).map(ConValue::Ref)), // In the production compiler this may be fully unrolled ConValue::Tuple(t) => Box::new(t.iter().cloned()), - ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)), + ConValue::Str(s) => Box::new(s.chars().map(ConValue::Char)), _ => Err(Error::TypeError())?, }; loop { diff --git a/compiler/cl-interpret/src/pattern.rs b/compiler/cl-interpret/src/pattern.rs index 907399a..34a1f90 100644 --- a/compiler/cl-interpret/src/pattern.rs +++ b/compiler/cl-interpret/src/pattern.rs @@ -139,6 +139,9 @@ pub fn append_sub( (Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => { (b == *a as _).then_some(()).ok_or(Error::NotAssignable()) } + (Pattern::Literal(Literal::String(a)), ConValue::Str(b)) => { + (*a == *b).then_some(()).ok_or(Error::NotAssignable()) + } (Pattern::Literal(Literal::String(a)), ConValue::String(b)) => { (*a == *b).then_some(()).ok_or(Error::NotAssignable()) } @@ -157,6 +160,9 @@ pub fn append_sub( (Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => { (b < *a as _).then_some(()).ok_or(Error::NotAssignable()) } + (Pattern::Literal(Literal::String(a)), ConValue::Str(b)) => { + (&*b < a).then_some(()).ok_or(Error::NotAssignable()) + } (Pattern::Literal(Literal::String(a)), ConValue::String(b)) => { (&*b < a).then_some(()).ok_or(Error::NotAssignable()) } @@ -207,10 +213,17 @@ pub fn append_sub( ( Pattern::Literal(Literal::String(a)), Pattern::Literal(Literal::String(c)), - ConValue::String(b), + ConValue::Str(b), ) => (a.as_str() <= b.to_ref() && b.to_ref() < c.as_str()) .then_some(()) .ok_or(Error::NotAssignable()), + ( + Pattern::Literal(Literal::String(a)), + Pattern::Literal(Literal::String(c)), + ConValue::String(b), + ) => (a.as_str() <= b.as_str() && b.as_str() < c.as_str()) + .then_some(()) + .ok_or(Error::NotAssignable()), _ => Err(Error::NotAssignable()), }, @@ -239,10 +252,17 @@ pub fn append_sub( ( Pattern::Literal(Literal::String(a)), Pattern::Literal(Literal::String(c)), - ConValue::String(b), + ConValue::Str(b), ) => (a.as_str() <= b.to_ref() && b.to_ref() <= c.as_str()) .then_some(()) .ok_or(Error::NotAssignable()), + ( + Pattern::Literal(Literal::String(a)), + Pattern::Literal(Literal::String(c)), + ConValue::String(b), + ) => (a.as_str() <= b.as_str() && b.as_str() <= c.as_str()) + .then_some(()) + .ok_or(Error::NotAssignable()), _ => Err(Error::NotAssignable()), }, diff --git a/compiler/cl-repl/src/cli.rs b/compiler/cl-repl/src/cli.rs index de2490d..36a8eb1 100644 --- a/compiler/cl-repl/src/cli.rs +++ b/compiler/cl-repl/src/cli.rs @@ -26,23 +26,29 @@ pub fn run(args: Args) -> Result<(), Box> { fn eval(string) @env { use cl_interpret::error::Error; - let string = match *string { - ConValue::String(string) => string, + let string = match string { + ConValue::Str(string) => string.to_ref(), + ConValue::String(string) => string.as_str(), ConValue::Ref(v) => { - let string = env.get_id(v).cloned().unwrap_or_default(); + let string = env.get_id(*v).cloned().unwrap_or_default(); return eval(env, &[string]) } _ => Err(Error::TypeError())? }; - match Parser::new("eval", Lexer::new(string.to_ref())).parse::() { - Err(e) => Ok(ConValue::String(format!("{e}").into())), + match Parser::new("eval", Lexer::new(string)).parse::() { + Err(e) => Ok(ConValue::Str(format!("{e}").into())), Ok(v) => v.interpret(env), } } /// Executes a file - fn import(ConValue::String(path)) @env { - load_file(env, &**path).or(Ok(ConValue::Empty)) + fn import(path) @env { + use cl_interpret::error::Error; + match path { + ConValue::Str(path) => load_file(env, &**path).or(Ok(ConValue::Empty)), + ConValue::String(path) => load_file(env, &**path).or(Ok(ConValue::Empty)), + _ => Err(Error::TypeError()) + } } fn putchar(ConValue::Char(c)) { @@ -51,12 +57,18 @@ pub fn run(args: Args) -> Result<(), Box> { } /// Gets a line of input from stdin - fn get_line(ConValue::String(prompt)) { - match repline::Repline::new("", prompt.to_ref(), "").read() { - Ok(line) => Ok(ConValue::String(line.into())), - Err(repline::Error::CtrlD(line)) => Ok(ConValue::String(line.into())), + fn get_line(prompt) { + use cl_interpret::error::Error; + let prompt = match prompt { + ConValue::Str(prompt) => prompt.to_ref(), + ConValue::String(prompt) => prompt.as_str(), + _ => Err(Error::TypeError())?, + }; + match repline::Repline::new("", prompt, "").read() { + Ok(line) => Ok(ConValue::String(line)), + Err(repline::Error::CtrlD(line)) => Ok(ConValue::String(line)), Err(repline::Error::CtrlC(_)) => Err(cl_interpret::error::Error::Break(ConValue::Empty)), - Err(e) => Ok(ConValue::String(e.to_string().into())), + Err(e) => Ok(ConValue::Str(e.to_string().into())), } } }); diff --git a/sample-code/fstring.cl b/sample-code/fstring.cl index c3d91f3..f820b8b 100644 --- a/sample-code/fstring.cl +++ b/sample-code/fstring.cl @@ -2,6 +2,7 @@ fn f(__fmt: str) -> str { let __out = ""; let __expr = ""; + let __label = ""; let __depth = 0; for __c in chars(__fmt) { match __c { @@ -14,19 +15,26 @@ fn f(__fmt: str) -> str { '}' => { __depth -= 1 if __depth <= 0 { - __out += fmt(eval(__expr)) - __expr = "" + __out = fmt(__out, __label, eval(__expr)); + (__expr, __label) = ("", ""); continue } }, - ':' => if __depth == 1 { - __out += __expr + ": " + ':' => if __depth == 1 && __label.len() == 0 { + __label = __expr + __c + continue + }, + '=' => if __depth == 1 && __label.len() == 0 { + __label = __expr + __c + continue }, _ => {} } - if __depth > 0 { - __expr += __c - } else __out += __c; + match (__depth, __label.len()) { + (0, _) => __out += __c, + (_, 0) => __expr += __c, + (_, _) => __label += __c, + } } __out }