use crate::core::*;
use crate::core::to_sml;
use crate::value::*;
use std::collections::BTreeMap;
use std::os::raw::{c_char, c_int};
use std::ptr;
fn cstr(s: &str) -> *mut c_char {
let c = std::ffi::CString::new(s).unwrap_or_default();
c.into_raw()
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_parse(text: *const c_char) -> *mut c_char {
if text.is_null() {
return ptr::null_mut();
}
let t = unsafe { std::ffi::CStr::from_ptr(text) }.to_string_lossy().into_owned();
match parse(&t) {
Ok(v) => cstr(&jsonify(&v)),
Err(_) => ptr::null_mut(),
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_dump(json: *const c_char) -> *mut c_char {
if json.is_null() {
return ptr::null_mut();
}
let j = unsafe { std::ffi::CStr::from_ptr(json) }.to_string_lossy().into_owned();
match json_to_value(&j) {
Some(v) => cstr(&to_sml(&v)),
None => ptr::null_mut(),
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_free_str(p: *mut c_char) {
if !p.is_null() {
drop(unsafe { std::ffi::CString::from_raw(p) });
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_version() -> *const c_char {
concat!("sml ", env!("CARGO_PKG_VERSION"), "\0").as_ptr() as *const c_char
}
fn parse_opts_json(opts: &str) -> Result<(Vec<Feature>, Vec<(String, String)>, Vec<Version>), String> {
let mut features: Vec<Feature> = Vec::new();
let mut env: Vec<(String, String)> = Vec::new();
let mut allow: Vec<Version> = Vec::new();
if opts.trim().is_empty() {
return Ok((features, env, allow));
}
let b = opts.as_bytes();
let mut i = 0usize;
let len = b.len();
while i < len && b[i] != b'{' { i += 1; }
if i >= len { return Err("opts 不是 JSON object".into()); }
i += 1; loop {
while i < len && (b[i] == b' ' || b[i] == b'\t' || b[i] == b'\n' || b[i] == b'\r' || b[i] == b',') { i += 1; }
if i >= len || b[i] == b'}' { break; }
if b[i] != b'"' { return Err("opts key 须为字符串".into()); }
i += 1;
let ks = i;
while i < len && b[i] != b'"' { i += 1; }
let key = std::str::from_utf8(&b[ks..i]).map_err(|_| "opts key 非法 UTF-8".to_string())?.to_string();
i += 1; while i < len && (b[i] == b' ' || b[i] == b':' || b[i] == b'\t') { i += 1; }
match key.as_str() {
"features" | "allow" => {
if i >= len || b[i] != b'[' { return Err(format!("opts.{key} 须为数组")); }
i += 1;
loop {
while i < len && (b[i] == b' ' || b[i] == b'\t' || b[i] == b'\n' || b[i] == b'\r' || b[i] == b',') { i += 1; }
if i < len && b[i] == b']' { i += 1; break; }
if i >= len || b[i] != b'"' { return Err(format!("opts.{key} 元素须为字符串")); }
i += 1;
let vs = i;
while i < len && b[i] != b'"' { i += 1; }
let val = std::str::from_utf8(&b[vs..i]).map_err(|_| "opts 值非法 UTF-8".to_string())?.to_string();
i += 1;
if key == "features" {
features.push(Feature::from_name(&val).ok_or_else(|| format!("未知特性 {val}"))?);
} else {
allow.push(Version::from_word(&val).ok_or_else(|| format!("未知版本 {val}"))?);
}
}
}
"env" => {
if i >= len || b[i] != b'{' { return Err("opts.env 须为 object".into()); }
i += 1;
loop {
while i < len && (b[i] == b' ' || b[i] == b'\t' || b[i] == b'\n' || b[i] == b'\r' || b[i] == b',') { i += 1; }
if i < len && b[i] == b'}' { i += 1; break; }
if i >= len || b[i] != b'"' { return Err("opts.env key 须为字符串".into()); }
i += 1;
let ks = i;
while i < len && b[i] != b'"' { i += 1; }
let ek = std::str::from_utf8(&b[ks..i]).map_err(|_| "opts.env key 非法".to_string())?.to_string();
i += 1;
while i < len && (b[i] == b' ' || b[i] == b':' || b[i] == b'\t') { i += 1; }
if i >= len || b[i] != b'"' { return Err("opts.env value 须为字符串".into()); }
i += 1;
let vs = i;
while i < len && b[i] != b'"' { i += 1; }
let ev = std::str::from_utf8(&b[vs..i]).map_err(|_| "opts.env value 非法".to_string())?.to_string();
i += 1;
env.push((ek, ev));
}
}
_ => {
let mut depth = 0i32;
loop {
if i >= len { break; }
match b[i] {
b'"' => { i += 1; while i < len && b[i] != b'"' { if b[i] == b'\\' { i += 2; } else { i += 1; } } i += 1; }
b'{' | b'[' => { depth += 1; i += 1; }
b'}' | b']' => { depth -= 1; i += 1; if depth <= 0 { break; } }
_ => { i += 1; }
}
}
}
}
}
Ok((features, env, allow))
}
#[allow(unused_unsafe)]
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_parse_ex(text: *const c_char, opts: *const c_char) -> *mut c_char {
if text.is_null() {
return ptr::null_mut();
}
let t = unsafe { std::ffi::CStr::from_ptr(text) }.to_string_lossy().into_owned();
let opts_str = if opts.is_null() {
String::new()
} else {
unsafe { std::ffi::CStr::from_ptr(opts) }.to_string_lossy().into_owned()
};
let (feats, env, allow) = match parse_opts_json(&opts_str) {
Ok(x) => x,
Err(_) => return ptr::null_mut(),
};
let prev: Vec<(String, Option<String>)> = env
.iter()
.map(|(k, _)| (k.clone(), std::env::var(k).ok()))
.collect();
for (k, v) in &env {
unsafe { std::env::set_var(k, v) };
}
let result = (|| {
let mut allowed = FeatureSet::all();
for f in &feats {
allowed = allowed.with(*f);
}
let val = parse_with_features(&t, allowed).map(|(v, _)| v)?;
if !allow.is_empty() {
let declared = strip_version(&t).ok().and_then(|(_, d)| d);
if let Some(d) = declared {
if !allow.contains(&d) {
return Err(format!("文档声明版本 {} 不在 allow 范围", d.name()));
}
}
}
Ok(jsonify(&val))
})();
for (k, v) in &prev {
match v {
Some(old) => unsafe { std::env::set_var(k, old) },
None => unsafe { std::env::remove_var(k) },
}
}
match result {
Ok(s) => cstr(&s),
Err(_) => ptr::null_mut(),
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_parse_file(path: *const c_char) -> *mut c_char {
if path.is_null() {
return ptr::null_mut();
}
let p = unsafe { std::ffi::CStr::from_ptr(path) }.to_string_lossy().into_owned();
match parse_file(&p) {
Ok(v) => cstr(&jsonify(&v)),
Err(_) => ptr::null_mut(),
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_features() -> *mut c_char {
let names: Vec<&str> = FEATURES.iter().map(|(n, _)| *n).collect();
let body = names
.iter()
.map(|n| format!("\"{}\"", n))
.collect::<Vec<_>>()
.join(",");
cstr(&format!("[{}]", body))
}
use std::os::raw::{c_uint, c_ulonglong};
#[repr(C)]
#[derive(Clone, Copy)]
pub enum CSmlErrc {
Ok = 0,
Syntax = 1,
FeatureDisabled = 2,
VersionMismatch = 3,
Contract = 4,
IncludeLoop = 5,
Io = 6,
Utf8 = 7,
Internal = 8,
}
#[repr(C)]
pub struct CSmlError {
pub code: c_int,
pub line: c_int,
pub column: c_int,
pub position: usize,
pub source: [c_char; 128],
pub text: [c_char; 256],
}
impl CSmlError {
unsafe fn fill(out: *mut CSmlError, code: CSmlErrc, msg: &str, source: &str) {
if out.is_null() {
return;
}
let e = &mut *out;
e.code = code as c_int;
e.line = 0;
e.column = 0;
e.position = 0;
e.source = [0; 128];
e.text = [0; 256];
copy_cstr(&mut e.source, source);
copy_cstr(&mut e.text, msg);
if let Some(l) = extract_line(msg) {
e.line = l;
}
}
}
fn copy_cstr(dst: &mut [c_char], s: &str) {
if dst.is_empty() {
return;
}
let bytes = s.as_bytes();
let n = bytes.len().min(dst.len() - 1);
for i in 0..n {
dst[i] = bytes[i] as c_char;
}
dst[n] = 0;
}
fn extract_line(msg: &str) -> Option<c_int> {
for pat in ["第 ", "line "] {
if let Some(idx) = msg.find(pat) {
let rest = &msg[idx + pat.len()..];
let digits: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
if let Ok(n) = digits.parse::<i32>() {
if n > 0 {
return Some(n);
}
}
}
}
None
}
#[repr(transparent)]
pub struct CSmlValue(Value);
fn classify(err: &str) -> CSmlErrc {
if err.contains("include") && (err.contains("循环") || err.contains("loop")) {
CSmlErrc::IncludeLoop
} else if err.contains("特性") || err.contains("feature") {
CSmlErrc::FeatureDisabled
} else if err.contains("版本") || err.contains("version") {
CSmlErrc::VersionMismatch
} else if err.contains("契约") || err.contains("contract") {
CSmlErrc::Contract
} else if err.contains("读取失败") || err.contains("IO") {
CSmlErrc::Io
} else {
CSmlErrc::Syntax
}
}
fn feature_set_from_flags(flags: c_uint) -> FeatureSet {
if flags == 0 {
return FeatureSet::baseline();
}
let mut s = FeatureSet::none();
for (i, (_, f)) in FEATURES.iter().enumerate() {
if i >= 32 {
break;
}
if flags & (1u32 << i) != 0 {
s = s.with(*f);
}
}
s
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_loads(
text: *const c_char,
flags: c_uint,
err: *mut CSmlError,
) -> *mut CSmlValue {
if text.is_null() {
CSmlError::fill(err, CSmlErrc::Internal, "sml_loads: text is NULL", "<string>");
return ptr::null_mut();
}
let t = std::ffi::CStr::from_ptr(text).to_string_lossy().into_owned();
let allowed = feature_set_from_flags(flags);
match parse_with_features(&t, allowed) {
Ok((v, _)) => Box::into_raw(Box::new(CSmlValue(v))),
Err(e) => {
CSmlError::fill(err, classify(&e), &e, "<string>");
ptr::null_mut()
}
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_load_file(
path: *const c_char,
flags: c_uint,
err: *mut CSmlError,
) -> *mut CSmlValue {
if path.is_null() {
CSmlError::fill(err, CSmlErrc::Internal, "sml_load_file: path is NULL", "<file>");
return ptr::null_mut();
}
let p = std::ffi::CStr::from_ptr(path).to_string_lossy().into_owned();
let _ = flags; match parse_file(&p) {
Ok(v) => Box::into_raw(Box::new(CSmlValue(v))),
Err(e) => {
CSmlError::fill(err, classify(&e), &e, &p);
ptr::null_mut()
}
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_free(v: *mut CSmlValue) {
if !v.is_null() {
drop(Box::from_raw(v));
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_typeof(v: *const CSmlValue) -> c_int {
if v.is_null() {
return -1;
}
let inner = &(*(v as *const Value));
match inner {
Value::Null => 0,
Value::Bool(_) => 1,
Value::Int(_) => 2,
Value::Float(_) => 3,
Value::Str(_) => 4,
Value::Array(_) => 5,
Value::Object(_) => 6,
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_get(
v: *const CSmlValue,
key: *const c_char,
) -> *const CSmlValue {
if v.is_null() || key.is_null() {
return ptr::null();
}
let inner = &(*(v as *const Value));
let k = std::ffi::CStr::from_ptr(key).to_string_lossy();
match inner {
Value::Object(m) => m
.get(k.as_ref())
.map(|x| x as *const Value as *const CSmlValue)
.unwrap_or(ptr::null()),
_ => ptr::null(),
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_get_path(
v: *const CSmlValue,
path: *const c_char,
) -> *const CSmlValue {
if v.is_null() || path.is_null() {
return ptr::null();
}
let p = std::ffi::CStr::from_ptr(path).to_string_lossy();
let mut cur: *const CSmlValue = v;
for seg in p.split('.') {
if seg.is_empty() {
continue;
}
let c_seg = match std::ffi::CString::new(seg) {
Ok(c) => c,
Err(_) => return ptr::null(),
};
let next = sml_get(cur, c_seg.as_ptr());
if next.is_null() {
return ptr::null();
}
cur = next;
}
cur
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_at(v: *const CSmlValue, idx: usize) -> *const CSmlValue {
if v.is_null() {
return ptr::null();
}
let inner = &(*(v as *const Value));
match inner {
Value::Array(a) => a
.get(idx)
.map(|x| x as *const Value as *const CSmlValue)
.unwrap_or(ptr::null()),
_ => ptr::null(),
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_size(v: *const CSmlValue) -> usize {
if v.is_null() {
return 0;
}
match &(*(v as *const Value)) {
Value::Array(a) => a.len(),
Value::Object(m) => m.len(),
_ => 0,
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_str_copy(
v: *const CSmlValue,
buf: *mut c_char,
buflen: usize,
) -> usize {
if v.is_null() {
return 0;
}
let s = match &(*(v as *const Value)) {
Value::Str(s) => s.as_str(),
_ => return 0,
};
let need = s.len();
if buf.is_null() || buflen == 0 {
return need;
}
let n = need.min(buflen - 1);
let src = s.as_bytes();
for i in 0..n {
*buf.add(i) = src[i] as c_char;
}
*buf.add(n) = 0;
need
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_str_dup(v: *const CSmlValue) -> *mut c_char {
if v.is_null() {
return ptr::null_mut();
}
match &(*(v as *const Value)) {
Value::Str(s) => cstr(s),
_ => ptr::null_mut(),
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_int_value(v: *const CSmlValue) -> i64 {
if v.is_null() {
return 0;
}
match &(*(v as *const Value)) {
Value::Int(i) => *i,
Value::Float(f) => *f as i64,
_ => 0,
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_real_value(v: *const CSmlValue) -> f64 {
if v.is_null() {
return 0.0;
}
match &(*(v as *const Value)) {
Value::Float(f) => *f,
Value::Int(i) => *i as f64,
_ => 0.0,
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_bool_value(v: *const CSmlValue) -> c_int {
if v.is_null() {
return 0;
}
match &(*(v as *const Value)) {
Value::Bool(b) => {
if *b {
1
} else {
0
}
}
_ => 0,
}
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_str_in(
v: *const CSmlValue,
path: *const c_char,
) -> *mut c_char {
let node = sml_get_path(v, path);
if node.is_null() {
return ptr::null_mut();
}
sml_str_dup(node)
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_int_in(
v: *const CSmlValue,
path: *const c_char,
ok: *mut c_int,
) -> i64 {
let node = sml_get_path(v, path);
if node.is_null() {
if !ok.is_null() {
*ok = 0;
}
return 0;
}
let is_int = sml_typeof(node) == 2;
if !ok.is_null() {
*ok = if is_int { 1 } else { 0 };
}
sml_int_value(node)
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_bool_in(
v: *const CSmlValue,
path: *const c_char,
ok: *mut c_int,
) -> c_int {
let node = sml_get_path(v, path);
if node.is_null() {
if !ok.is_null() {
*ok = 0;
}
return 0;
}
let is_bool = sml_typeof(node) == 1;
if !ok.is_null() {
*ok = if is_bool { 1 } else { 0 };
}
sml_bool_value(node)
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub unsafe extern "C" fn sml_dumps(v: *const CSmlValue, _flags: c_uint) -> *mut c_char {
if v.is_null() {
return ptr::null_mut();
}
cstr(&to_sml(&(*(v as *const Value))))
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_feature_name(bit: c_uint) -> *const c_char {
let s: &'static str = match bit {
0 => "bareword-string\0",
1 => "include\0",
2 => "env\0",
3 => "contract\0",
4 => "fragment\0",
5 => "top-level-array\0",
6 => "namespace\0",
7 => "implicit-ns\0",
8 => "multi-include\0",
9 => "glob-include\0",
10 => "regex-include\0",
11 => "ext-rewrite\0",
_ => return ptr::null(),
};
s.as_ptr() as *const c_char
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_features_mask() -> c_uint {
let mut m = 0u32;
for (i, _) in FEATURES.iter().enumerate() {
if i >= 32 {
break;
}
m |= 1u32 << i;
}
m
}
#[cfg_attr(edge2024, unsafe(no_mangle))]
#[cfg_attr(not(edge2024), no_mangle)]
pub extern "C" fn sml_version_str() -> *mut c_char {
cstr(env!("CARGO_PKG_VERSION"))
}
#[allow(dead_code)]
type _CUnsignedLongLong = c_ulonglong;
pub(crate) fn jsonify(v: &Value) -> String {
fn esc(s: &str) -> String {
s.replace('\\', "\\\\").replace('"', "\\\"")
}
match v {
Value::Null => "null".into(),
Value::Bool(b) => b.to_string(),
Value::Int(i) => i.to_string(),
Value::Float(f) => f.to_string(),
Value::Str(s) => format!("\"{}\"", esc(s)),
Value::Array(a) => {
let parts: Vec<String> = a.iter().map(jsonify).collect();
format!("[{}]", parts.join(","))
}
Value::Object(m) => {
let parts: Vec<String> = m
.iter()
.map(|(k, val)| format!("\"{}\":{}", esc(k), jsonify(val)))
.collect();
format!("{{{}}}", parts.join(","))
}
}
}
pub(crate) fn json_to_value(s: &str) -> Option<Value> {
let bytes = s.as_bytes();
let mut i = 0;
let _n = bytes.len();
let mut skip_ws = |b: &[u8], i: &mut usize| {
while *i < b.len() && matches!(b[*i], b' ' | b'\t' | b'\n' | b'\r') {
*i += 1;
}
};
let mut parse_str = |b: &[u8], i: &mut usize| -> Option<String> {
skip_ws(b, i);
if *i >= b.len() || b[*i] != b'"' {
return None;
}
*i += 1;
let mut out = String::new();
while *i < b.len() {
let c = b[*i];
if c == b'"' {
*i += 1;
return Some(out);
}
if c == b'\\' && *i + 1 < b.len() {
*i += 1;
let e = b[*i];
out.push(match e {
b'n' => '\n',
b't' => '\t',
b'r' => '\r',
b'"' => '"',
b'\\' => '\\',
_ => e as char,
});
} else {
out.push(c as char);
}
*i += 1;
}
None
};
fn parse_val_impl(
b: &[u8],
i: &mut usize,
s: &str,
parse_str: &dyn Fn(&[u8], &mut usize) -> Option<String>,
) -> Option<Value> {
let mut skip_ws = |b: &[u8], i: &mut usize| {
while *i < b.len() && matches!(b[*i], b' ' | b'\t' | b'\n' | b'\r') {
*i += 1;
}
};
skip_ws(b, i);
if *i >= b.len() {
return None;
}
match b[*i] {
b'{' => {
*i += 1;
let mut m = BTreeMap::new();
skip_ws(b, i);
if *i < b.len() && b[*i] == b'}' {
*i += 1;
return Some(Value::Object(m));
}
loop {
skip_ws(b, i);
let k = parse_str(b, i)?;
skip_ws(b, i);
if *i < b.len() && b[*i] == b':' {
*i += 1;
}
let v = parse_val_impl(b, i, s, parse_str)?;
m.insert(k, v);
skip_ws(b, i);
if *i < b.len() && b[*i] == b',' {
*i += 1;
} else if *i < b.len() && b[*i] == b'}' {
*i += 1;
break;
}
}
Some(Value::Object(m))
}
b'[' => {
*i += 1;
let mut a = Vec::new();
skip_ws(b, i);
if *i < b.len() && b[*i] == b']' {
*i += 1;
return Some(Value::Array(a));
}
loop {
a.push(parse_val_impl(b, i, s, parse_str)?);
skip_ws(b, i);
if *i < b.len() && b[*i] == b',' {
*i += 1;
} else if *i < b.len() && b[*i] == b']' {
*i += 1;
break;
}
}
Some(Value::Array(a))
}
b'"' => parse_str(b, i).map(Value::Str),
b't' => {
if s[*i..].starts_with("true") {
*i += 4;
Some(Value::Bool(true))
} else {
None
}
}
b'f' => {
if s[*i..].starts_with("false") {
*i += 5;
Some(Value::Bool(false))
} else {
None
}
}
b'n' => {
if s[*i..].starts_with("null") {
*i += 4;
Some(Value::Null)
} else {
None
}
}
_ => {
let start = *i;
while *i < b.len()
&& (b[*i].is_ascii_digit()
|| matches!(b[*i], b'-' | b'+' | b'.' | b'e' | b'E'))
{
*i += 1;
}
let tok = s[start..*i].to_string();
if let Ok(iv) = tok.parse::<i64>() {
Some(Value::Int(iv))
} else if let Ok(fv) = tok.parse::<f64>() {
Some(Value::Float(fv))
} else {
None
}
}
}
}
parse_val_impl(bytes, &mut i, s, &parse_str)
}