///| MoonBit YAML Library
/// A YAML parsing and stringifying library for MoonBit
///
/// Ported from Deno std/yaml (js-yaml v3.13.1):
/// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
/// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
/// Copyright 2018-2025 the Deno authors. MIT license.
///
/// This library supports:
/// - YAML 1.2.x (latest) and common YAML 1.1 features
/// - Parsing YAML strings to MoonBit values
/// - Stringifying MoonBit values to YAML
/// - Multiple schema types (failsafe, json, core, default)
/// - Flow and block styles
/// - Custom parsing options
///
/// # Example
///
/// ```moonbit
/// // Parse simple YAML
/// let data = parse("name: Alice\nage: 30")!
///
/// // Stringify to YAML
/// let yaml_obj = YamlValue::Object({
/// let map = Map::new()
/// map["name"] = YamlValue::String("Bob")
/// map["age"] = YamlValue::Int(25)
/// map
/// })
///
/// let yaml_text = stringify(yaml_obj)
/// ```
// Re-export core types from the converted modules
///|
/// Represents any YAML value
pub enum YamlValue {
Null
Bool(Bool)
Int(Int)
Float(Double)
String(String)
Array(Array[YamlValue])
Object(Map[String, YamlValue])
} derive(Eq, Show)
// Utility functions
// JSON conversion functions are commented out due to constructor permission issues
// These are optional utility functions and not core to YAML functionality
// ///|
// /// Convert a simple JSON value to YamlValue
// /// This is a basic conversion for demonstration purposes
// pub fn from_json(json : Json) -> YamlValue {
// match json {
// Json::Null => Null
// Json::True => Bool(true)
// Json::False => Bool(false)
// Json::Number(n) => {
// // Try to convert to int if it's a whole number
// let d = n
// if d.floor() == d && d >= -2147483648.0 && d <= 2147483647.0 {
// Int(d.to_int())
// } else {
// Float(d)
// }
// }
// Json::String(s) => String(s)
// Json::Array(arr) => {
// let yaml_arr = []
// for item in arr {
// yaml_arr.push(from_json(item))
// }
// Array(yaml_arr)
// }
// Json::Object(obj) => {
// let yaml_obj = Map::new()
// obj.each(fn(k, v) { yaml_obj[k] = from_json(v) })
// Object(yaml_obj)
// }
// }
// }
// ///|
// /// Convert YamlValue to JSON
// /// This is a basic conversion for demonstration purposes
// pub fn to_json(yaml : YamlValue) -> Json {
// match yaml {
// Null => Json::Null
// Bool(true) => Json::True
// Bool(false) => Json::False
// Int(i) => Json::Number(i.to_double())
// Float(f) =>
// if f != f || f == 1.0 / 0.0 || f == -1.0 / 0.0 {
// Json::Null // JSON doesn't support NaN/Infinity
// } else {
// Json::Number(f)
// }
// String(s) => Json::String(s)
// Array(arr) => {
// let json_arr = []
// for item in arr {
// json_arr.push(to_json(item))
// }
// Json::Array(json_arr)
// }
// Object(obj) => {
// let json_obj = {}
// obj.each(fn(k, v) { json_obj[k] = to_json(v) })
// Json::Object(json_obj)
// }
// }
// }
///|
/// Create a YAML object from key-value pairs
pub fn yaml_object(pairs : Array[(String, YamlValue)]) -> YamlValue {
let obj = Map::new()
for i = 0; i < pairs.length(); i = i + 1 {
let (key, value) = pairs[i]
obj[key] = value
}
Object(obj)
}
///|
/// Create a YAML array from values
pub fn yaml_array(values : Array[YamlValue]) -> YamlValue {
Array(values)
}
// Directly export needed utility functions
///|
pub fn is_object(value : YamlValue) -> Bool {
match value {
Object(_) => true
_ => false
}
}
///|
pub fn is_plain_object(value : YamlValue) -> Bool {
match value {
Object(_) => true
_ => false
}
}
///|
pub fn is_negative_zero(f : Double) -> Bool {
f == 0.0 && 1.0 / f < 0.0
}
// Character utilities are already declared in chars.mbt
// Schema functions are already declared in schema.mbt
// Type functions are already declared in builtin_types.mbt
// Main parsing functions are already declared in parser.mbt and stringify.mbt
// Helper string methods
///|
/// Repeat string n times
fn String::repeat(self : String, n : Int) -> String {
if n <= 0 {
""
} else {
let mut result = ""
for i = 0; i < n; i = i + 1 {
result = result + self
}
result
}
}
///|
/// Get minimum of two integers
fn Int::min(self : Int, other : Int) -> Int {
if self < other {
self
} else {
other
}
}
///|
/// Array slice method
fn[T] Array::slice(
self : Array[T],
start? : Int = 0,
end? : Int = -1,
) -> Array[T] {
let actual_end = if end == -1 { self.length() } else { end }
let result = []
for i = start; i < actual_end && i < self.length(); i = i + 1 {
result.push(self[i])
}
result
}
///|
/// Option or method
fn[T] Option::or(self : T?, default : T) -> T {
match self {
Some(value) => value
None => default
}
}
///|
/// Trim whitespace from both ends
fn String::trim(self : String) -> String {
// Simple implementation - trim spaces and newlines
let mut start = 0
let mut end = self.length()
while start < end &&
(
self[start] == ' ' ||
self[start] == '\t' ||
self[start] == '\n' ||
self[start] == '\r'
) {
start = start + 1
}
while end > start &&
(
self[end - 1] == ' ' ||
self[end - 1] == '\t' ||
self[end - 1] == '\n' ||
self[end - 1] == '\r'
) {
end = end - 1
}
if start == 0 && end == self.length() {
self
} else {
self.substring(start~, end~)
}
}
///|
/// Split string by delimiter
fn String::split(self : String, delimiter : String) -> Array[String] {
let result = []
let mut current = ""
let mut i = 0
while i < self.length() {
if i + delimiter.length() <= self.length() {
let substr = self.substring(start=i, end=i + delimiter.length())
if substr == delimiter {
result.push(current)
current = ""
i = i + delimiter.length()
continue
}
}
current = current + Char::from_int(self[i]).to_string()
i = i + 1
}
result.push(current)
result
}
///|
/// Check if string starts with prefix
fn String::starts_with(self : String, prefix : String) -> Bool {
if prefix.length() > self.length() {
false
} else {
self.substring(start=0, end=prefix.length()) == prefix
}
}
///|
/// Check if string contains character
fn String::contains_char(self : String, ch : Char) -> Bool {
let ch_code = Char::to_int(ch)
for i = 0; i < self.length(); i = i + 1 {
if self[i] == ch_code {
return true
}
}
false
}
///|
/// Join array elements with separator
fn join(self : Array[String], separator : String) -> String {
if self.is_empty() {
""
} else if self.length() == 1 {
self[0]
} else {
let mut result = self[0]
for i = 1; i < self.length(); i = i + 1 {
result = result + separator + self[i]
}
result
}
}