commit before easter break
This commit is contained in:
4
orchid-std/src/std/mod.rs
Normal file
4
orchid-std/src/std/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod number;
|
||||
pub mod string;
|
||||
|
||||
pub mod std_system;
|
||||
3
orchid-std/src/std/number/mod.rs
Normal file
3
orchid-std/src/std/number/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod num_atom;
|
||||
pub mod num_lexer;
|
||||
pub mod num_lib;
|
||||
93
orchid-std/src/std/number/num_atom.rs
Normal file
93
orchid-std/src/std/number/num_atom.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::FmtUnit;
|
||||
use orchid_base::number::Numeric;
|
||||
use orchid_extension::atom::{
|
||||
AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, ToAtom, TypAtom,
|
||||
};
|
||||
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::system::SysCtx;
|
||||
use ordered_float::NotNan;
|
||||
use rust_decimal::prelude::Zero;
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct Int(pub i64);
|
||||
impl Atomic for Int {
|
||||
type Variant = ThinVariant;
|
||||
type Data = Self;
|
||||
}
|
||||
impl ThinAtom for Int {
|
||||
async fn print(&self, _: SysCtx) -> FmtUnit { self.0.to_string().into() }
|
||||
}
|
||||
impl TryFromExpr for Int {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
TypAtom::<Int>::try_from_expr(expr).await.map(|t| t.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct Float(pub NotNan<f64>);
|
||||
impl Atomic for Float {
|
||||
type Variant = ThinVariant;
|
||||
type Data = Self;
|
||||
}
|
||||
impl ThinAtom for Float {
|
||||
async fn print(&self, _: SysCtx) -> FmtUnit { self.0.to_string().into() }
|
||||
}
|
||||
impl TryFromExpr for Float {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
Ok(Self(Num::try_from_expr(expr).await?.0.to_f64()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Num(pub Numeric);
|
||||
impl TryFromExpr for Num {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
let e = match Int::try_from_expr(expr.clone()).await {
|
||||
Ok(t) => return Ok(Num(Numeric::Int(t.0))),
|
||||
Err(e) => e,
|
||||
};
|
||||
match TypAtom::<Float>::try_from_expr(expr).await {
|
||||
Ok(t) => Ok(Num(Numeric::Float(t.0))),
|
||||
Err(e2) => Err(e + e2),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToAtom for Num {
|
||||
fn to_atom_factory(self) -> AtomFactory {
|
||||
match self.0 {
|
||||
Numeric::Float(f) => Float(f).factory(),
|
||||
Numeric::Int(i) => Int(i).factory(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A homogenous fixed length number array that forces all of its elements into
|
||||
/// the weakest element type. This describes the argument casting behaviour of
|
||||
/// most numeric operations.
|
||||
pub enum HomoArray<const N: usize> {
|
||||
Int([i64; N]),
|
||||
Float([NotNan<f64>; N]),
|
||||
}
|
||||
impl<const N: usize> HomoArray<N> {
|
||||
pub fn new(n: [Numeric; N]) -> Self {
|
||||
let mut ints = [0i64; N];
|
||||
for i in 0..N {
|
||||
if let Numeric::Int(val) = n[i] {
|
||||
ints[i] = val
|
||||
} else {
|
||||
let mut floats = [NotNan::zero(); N];
|
||||
for (i, int) in ints.iter().take(i).enumerate() {
|
||||
floats[i] = NotNan::new(*int as f64).expect("i64 cannot convert to f64 NaN");
|
||||
}
|
||||
for j in i..N {
|
||||
floats[j] = n[j].to_f64();
|
||||
}
|
||||
return Self::Float(floats);
|
||||
}
|
||||
}
|
||||
Self::Int(ints)
|
||||
}
|
||||
}
|
||||
24
orchid-std/src/std/number/num_lexer.rs
Normal file
24
orchid-std/src/std/number/num_lexer.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::number::{num_to_err, parse_num};
|
||||
use orchid_extension::atom::ToAtom;
|
||||
use orchid_extension::lexer::{LexContext, Lexer};
|
||||
use orchid_extension::tree::{GenTok, GenTokTree};
|
||||
|
||||
use super::num_atom::Num;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct NumLexer;
|
||||
impl Lexer for NumLexer {
|
||||
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['0'..='9'];
|
||||
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
|
||||
let ends_at = all.find(|c: char| !c.is_ascii_hexdigit() && !"xX._pP".contains(c));
|
||||
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
|
||||
let fac = match parse_num(chars) {
|
||||
Ok(numeric) => Num(numeric).to_atom_factory(),
|
||||
Err(e) => return Err(num_to_err(e, ctx.pos(all), ctx.i).await.into()),
|
||||
};
|
||||
Ok((tail, GenTok::X(fac).at(ctx.pos(all)..ctx.pos(tail))))
|
||||
}
|
||||
}
|
||||
34
orchid-std/src/std/number/num_lib.rs
Normal file
34
orchid-std/src/std/number/num_lib.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use orchid_base::number::Numeric;
|
||||
use orchid_extension::tree::{GenItem, fun, prefix};
|
||||
use ordered_float::NotNan;
|
||||
|
||||
use super::num_atom::{Float, HomoArray, Int, Num};
|
||||
|
||||
pub fn gen_num_lib() -> Vec<GenItem> {
|
||||
prefix("std::number", [
|
||||
fun(true, "add", |a: Num, b: Num| async move {
|
||||
Num(match HomoArray::new([a.0, b.0]) {
|
||||
HomoArray::Int([a, b]) => Numeric::Int(a + b),
|
||||
HomoArray::Float([a, b]) => Numeric::Float(a + b),
|
||||
})
|
||||
}),
|
||||
fun(true, "neg", |a: Num| async move {
|
||||
Num(match a.0 {
|
||||
Numeric::Int(i) => Numeric::Int(-i),
|
||||
Numeric::Float(f) => Numeric::Float(-f),
|
||||
})
|
||||
}),
|
||||
fun(true, "mul", |a: Num, b: Num| async move {
|
||||
Num(match HomoArray::new([a.0, b.0]) {
|
||||
HomoArray::Int([a, b]) => Numeric::Int(a * b),
|
||||
HomoArray::Float([a, b]) => Numeric::Float(a * b),
|
||||
})
|
||||
}),
|
||||
fun(true, "idiv", |a: Int, b: Int| async move { Int(a.0 / b.0) }),
|
||||
fun(true, "imod", |a: Int, b: Int| async move { Int(a.0 % b.0) }),
|
||||
fun(true, "fdiv", |a: Float, b: Float| async move { Float(a.0 / b.0) }),
|
||||
fun(true, "fmod", |a: Float, b: Float| async move {
|
||||
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0)
|
||||
}),
|
||||
])
|
||||
}
|
||||
41
orchid-std/src/std/std_system.rs
Normal file
41
orchid-std/src/std/std_system.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use never::Never;
|
||||
use orchid_base::reqnot::Receipt;
|
||||
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
|
||||
use orchid_extension::entrypoint::ExtReq;
|
||||
use orchid_extension::fs::DeclFs;
|
||||
use orchid_extension::lexer::LexerObj;
|
||||
use orchid_extension::parser::ParserObj;
|
||||
use orchid_extension::system::{System, SystemCard};
|
||||
use orchid_extension::system_ctor::SystemCtor;
|
||||
use orchid_extension::tree::{GenItem, merge_trivial};
|
||||
|
||||
use super::number::num_lib::gen_num_lib;
|
||||
use super::string::str_atom::{IntStrAtom, StrAtom};
|
||||
use super::string::str_lib::gen_str_lib;
|
||||
use crate::std::number::num_lexer::NumLexer;
|
||||
use crate::std::string::str_lexer::StringLexer;
|
||||
use crate::{Float, Int};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StdSystem;
|
||||
impl SystemCtor for StdSystem {
|
||||
type Deps = ();
|
||||
type Instance = Self;
|
||||
const NAME: &'static str = "orchid::std";
|
||||
const VERSION: f64 = 0.00_01;
|
||||
fn inst() -> Option<Self::Instance> { Some(Self) }
|
||||
}
|
||||
impl SystemCard for StdSystem {
|
||||
type Ctor = Self;
|
||||
type Req = Never;
|
||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
|
||||
[Some(Int::dynfo()), Some(Float::dynfo()), Some(StrAtom::dynfo()), Some(IntStrAtom::dynfo())]
|
||||
}
|
||||
}
|
||||
impl System for StdSystem {
|
||||
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
|
||||
fn lexers() -> Vec<LexerObj> { vec![&StringLexer, &NumLexer] }
|
||||
fn parsers() -> Vec<ParserObj> { vec![] }
|
||||
fn vfs() -> DeclFs { DeclFs::Mod(&[]) }
|
||||
fn env() -> Vec<GenItem> { merge_trivial([gen_num_lib(), gen_str_lib()]) }
|
||||
}
|
||||
3
orchid-std/src/std/string/mod.rs
Normal file
3
orchid-std/src/std/string/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod str_atom;
|
||||
pub mod str_lexer;
|
||||
pub mod str_lib;
|
||||
115
orchid-std/src/std/string/str_atom.rs
Normal file
115
orchid-std/src/std/string/str_atom.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_std::io::Write;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::{Encode, Request};
|
||||
use orchid_base::error::{OrcRes, mk_errv};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom};
|
||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use orchid_extension::conv::TryFromExpr;
|
||||
use orchid_extension::expr::Expr;
|
||||
use orchid_extension::system::SysCtx;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Coding)]
|
||||
pub struct StringGetVal;
|
||||
impl Request for StringGetVal {
|
||||
type Response = Rc<String>;
|
||||
}
|
||||
impl AtomMethod for StringGetVal {
|
||||
const NAME: &str = "std::string_get_val";
|
||||
}
|
||||
impl Supports<StringGetVal> for StrAtom {
|
||||
async fn handle(&self, _: SysCtx, _: StringGetVal) -> <StringGetVal as Request>::Response {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrAtom(Rc<String>);
|
||||
impl Atomic for StrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = ();
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StringGetVal>() }
|
||||
}
|
||||
impl StrAtom {
|
||||
pub fn new(str: Rc<String>) -> Self { Self(str) }
|
||||
}
|
||||
impl Deref for StrAtom {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
impl OwnedAtom for StrAtom {
|
||||
type Refs = ();
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn serialize(&self, _: SysCtx, sink: Pin<&mut (impl Write + ?Sized)>) -> Self::Refs {
|
||||
self.deref().encode(sink).await
|
||||
}
|
||||
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
format!("{:?}", &*self.0).into()
|
||||
}
|
||||
async fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self {
|
||||
Self::new(Rc::new(ctx.read::<String>().await))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IntStrAtom(Tok<String>);
|
||||
impl Atomic for IntStrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = orchid_api::TStr;
|
||||
}
|
||||
impl From<Tok<String>> for IntStrAtom {
|
||||
fn from(value: Tok<String>) -> Self { Self(value) }
|
||||
}
|
||||
impl OwnedAtom for IntStrAtom {
|
||||
type Refs = ();
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) }
|
||||
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
format!("{:?}i", *self.0).into()
|
||||
}
|
||||
async fn serialize(&self, _: SysCtx, write: Pin<&mut (impl Write + ?Sized)>) {
|
||||
self.0.encode(write).await
|
||||
}
|
||||
async fn deserialize(mut ctx: impl DeserializeCtx, _: ()) -> Self {
|
||||
let s = ctx.decode::<String>().await;
|
||||
Self(ctx.sys().i().i(&s).await)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OrcString<'a> {
|
||||
kind: OrcStringKind<'a>,
|
||||
ctx: SysCtx,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum OrcStringKind<'a> {
|
||||
Val(TypAtom<'a, StrAtom>),
|
||||
Int(TypAtom<'a, IntStrAtom>),
|
||||
}
|
||||
impl OrcString<'_> {
|
||||
pub async fn get_string(&self) -> Rc<String> {
|
||||
match &self.kind {
|
||||
OrcStringKind::Int(tok) => self.ctx.i().ex(**tok).await.rc(),
|
||||
OrcStringKind::Val(atom) => atom.request(StringGetVal).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExpr for OrcString<'static> {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<OrcString<'static>> {
|
||||
if let Ok(v) = TypAtom::<StrAtom>::try_from_expr(expr.clone()).await {
|
||||
return Ok(OrcString { ctx: expr.ctx(), kind: OrcStringKind::Val(v) });
|
||||
}
|
||||
let ctx = expr.ctx();
|
||||
match TypAtom::<IntStrAtom>::try_from_expr(expr).await {
|
||||
Ok(t) => Ok(OrcString { ctx: t.data.ctx(), kind: OrcStringKind::Int(t) }),
|
||||
Err(e) => Err(mk_errv(ctx.i().i("A string was expected").await, "", e.pos_iter())),
|
||||
}
|
||||
}
|
||||
}
|
||||
152
orchid-std/src/std/string/str_lexer.rs
Normal file
152
orchid-std/src/std/string/str_lexer.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use itertools::Itertools;
|
||||
use orchid_base::error::{OrcErr, OrcRes, mk_err, mk_errv};
|
||||
use orchid_base::interner::Interner;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::sym;
|
||||
use orchid_base::tree::wrap_tokv;
|
||||
use orchid_extension::atom::AtomicFeatures;
|
||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||
use orchid_extension::tree::{GenTok, GenTokTree};
|
||||
|
||||
use super::str_atom::IntStrAtom;
|
||||
|
||||
/// Reasons why [parse_string] might fail. See [StringError]
|
||||
#[derive(Clone)]
|
||||
enum StringErrorKind {
|
||||
/// A unicode escape sequence wasn't followed by 4 hex digits
|
||||
NotHex,
|
||||
/// A unicode escape sequence contained an unassigned code point
|
||||
BadCodePoint,
|
||||
/// An unrecognized escape sequence was found
|
||||
BadEscSeq,
|
||||
}
|
||||
|
||||
/// Error produced by [parse_string]
|
||||
#[derive(Clone)]
|
||||
struct StringError {
|
||||
/// Character where the error occured
|
||||
pos: u32,
|
||||
/// Reason for the error
|
||||
kind: StringErrorKind,
|
||||
}
|
||||
|
||||
impl StringError {
|
||||
/// Convert into project error for reporting
|
||||
pub async fn into_proj(self, pos: u32, i: &Interner) -> OrcErr {
|
||||
let start = pos + self.pos;
|
||||
mk_err(
|
||||
i.i("Failed to parse string").await,
|
||||
match self.kind {
|
||||
StringErrorKind::NotHex => "Expected a hex digit",
|
||||
StringErrorKind::BadCodePoint => "The specified number is not a Unicode code point",
|
||||
StringErrorKind::BadEscSeq => "Unrecognized escape sequence",
|
||||
},
|
||||
[Pos::Range(start..start + 1).into()],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Process escape sequences in a string literal
|
||||
fn parse_string(str: &str) -> Result<String, StringError> {
|
||||
let mut target = String::new();
|
||||
let mut iter = str.char_indices().map(|(i, c)| (i as u32, c));
|
||||
while let Some((_, c)) = iter.next() {
|
||||
if c != '\\' {
|
||||
target.push(c);
|
||||
continue;
|
||||
}
|
||||
let (mut pos, code) = iter.next().expect("lexer would have continued");
|
||||
let next = match code {
|
||||
c @ ('\\' | '"' | '\'' | '$') => c,
|
||||
'b' => '\x08',
|
||||
'f' => '\x0f',
|
||||
'n' => '\n',
|
||||
'r' => '\r',
|
||||
't' => '\t',
|
||||
'\n' => 'skipws: loop {
|
||||
match iter.next() {
|
||||
None => return Ok(target),
|
||||
Some((_, c)) =>
|
||||
if !c.is_whitespace() {
|
||||
break 'skipws c;
|
||||
},
|
||||
}
|
||||
},
|
||||
'u' => {
|
||||
let acc = ((0..4).rev())
|
||||
.map(|radical| {
|
||||
let (j, c) = (iter.next()).ok_or(StringError { pos, kind: StringErrorKind::NotHex })?;
|
||||
pos = j;
|
||||
let b = u32::from_str_radix(&String::from(c), 16)
|
||||
.map_err(|_| StringError { pos, kind: StringErrorKind::NotHex })?;
|
||||
Ok(16u32.pow(radical) + b)
|
||||
})
|
||||
.fold_ok(0, u32::wrapping_add)?;
|
||||
char::from_u32(acc).ok_or(StringError { pos, kind: StringErrorKind::BadCodePoint })?
|
||||
},
|
||||
_ => return Err(StringError { pos, kind: StringErrorKind::BadEscSeq }),
|
||||
};
|
||||
target.push(next);
|
||||
}
|
||||
Ok(target)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StringLexer;
|
||||
impl Lexer for StringLexer {
|
||||
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
|
||||
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
|
||||
let Some(mut tail) = all.strip_prefix('"') else {
|
||||
return Err(err_not_applicable(ctx.i).await.into());
|
||||
};
|
||||
let mut ret = None;
|
||||
let mut cur = String::new();
|
||||
let mut errors = vec![];
|
||||
async fn str_to_gen<'a>(
|
||||
str: &mut String,
|
||||
tail: &str,
|
||||
err: &mut Vec<OrcErr>,
|
||||
ctx: &'a LexContext<'a>,
|
||||
) -> GenTokTree<'a> {
|
||||
let str_val_res = parse_string(&str.split_off(0));
|
||||
if let Err(e) = &str_val_res {
|
||||
err.push(e.clone().into_proj(ctx.pos(tail) - str.len() as u32, ctx.i).await);
|
||||
}
|
||||
let str_val = str_val_res.unwrap_or_default();
|
||||
GenTok::X(IntStrAtom::from(ctx.i.i(&*str_val).await).factory())
|
||||
.at(ctx.tok_ran(str.len() as u32, tail)) as GenTokTree<'a>
|
||||
}
|
||||
let add_frag = |prev: Option<GenTokTree<'a>>, new: GenTokTree<'a>| async {
|
||||
let Some(prev) = prev else { return new };
|
||||
let concat_fn = GenTok::Reference(sym!(std::string::concat; ctx.i).await)
|
||||
.at(prev.range.start..prev.range.start);
|
||||
wrap_tokv([concat_fn, prev, new])
|
||||
};
|
||||
loop {
|
||||
if let Some(rest) = tail.strip_prefix('"') {
|
||||
return Ok((rest, add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await));
|
||||
} else if let Some(rest) = tail.strip_prefix('$') {
|
||||
ret = Some(add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await);
|
||||
let (new_tail, tree) = ctx.recurse(rest).await?;
|
||||
tail = new_tail;
|
||||
ret = Some(add_frag(ret, tree).await);
|
||||
} else if tail.starts_with('\\') {
|
||||
// parse_string will deal with it, we just have to skip the next char
|
||||
tail = &tail[2..];
|
||||
} else {
|
||||
let mut ch = tail.chars();
|
||||
if let Some(c) = ch.next() {
|
||||
cur.push(c);
|
||||
tail = ch.as_str();
|
||||
} else {
|
||||
let range = ctx.pos(all)..ctx.pos("");
|
||||
return Err(mk_errv(
|
||||
ctx.i.i("No string end").await,
|
||||
"String never terminated with \"",
|
||||
[Pos::Range(range.clone()).into()],
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
orchid-std/src/std/string/str_lib.rs
Normal file
15
orchid-std/src/std/string/str_lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use orchid_extension::tree::{GenItem, comments, fun, prefix};
|
||||
|
||||
use super::str_atom::StrAtom;
|
||||
use crate::OrcString;
|
||||
|
||||
pub fn gen_str_lib() -> Vec<GenItem> {
|
||||
prefix("std::string", [comments(
|
||||
["Concatenate two strings"],
|
||||
fun(true, "concat", |left: OrcString<'static>, right: OrcString<'static>| async move {
|
||||
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
|
||||
}),
|
||||
)])
|
||||
}
|
||||
Reference in New Issue
Block a user