Add the initial version yaml parser

This commit is contained in:
Lamdonn 2025-05-10 21:28:42 +08:00
parent cc3d350ecf
commit a15641c42e
11 changed files with 8930 additions and 2 deletions

View File

@ -2,7 +2,7 @@
![logo](/image/logo.png)
[![Version](https://img.shields.io/badge/version-0.3.3-blue.svg)](https://gitee.com/Lamdonn/varch)
[![Version](https://img.shields.io/badge/version-0.3.4-blue.svg)](https://gitee.com/Lamdonn/varch)
[![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE)
[![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com)
![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg)
@ -67,6 +67,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
| json | 01.00.00 | [link](/doc/json.en.md) | [path](./source/05_parser) | JSON file parsing generator
| txls | 01.01.00 | [link](/doc/txls.en.md) | [path](./source/05_parser) | TXLS file parsing generator
| xml | 01.00.00 | [link](/doc/xml.en.md) | [path](./source/05_parser) | XML file parsing generator
| yaml | 00.01.00 | [link](/doc/yaml.en.md) | [path](./source/05_parser) | YAML file parsing generator
| ramt | 01.00.00 | [link](/doc/ramt.en.md) | [path](./source/06_performance) | RAM test module
| romt | 01.00.00 | [link](/doc/romt.en.md) | [path](./source/06_performance) | ROM test module
| cant | 00.01.00 | [link](/doc/cant.en.md) | [path](./source/06_performance) | CAN test module

View File

@ -2,7 +2,7 @@
![logo](/image/logo.png)
[![Version](https://img.shields.io/badge/version-0.3.3-blue.svg)](https://gitee.com/Lamdonn/varch)
[![Version](https://img.shields.io/badge/version-0.3.4-blue.svg)](https://gitee.com/Lamdonn/varch)
[![License](https://img.shields.io/badge/license-GPL%202.0-brightgreen.svg)](LICENSE)
[![Author](https://img.shields.io/badge/author-Lamdonn%20%20%20%20%20-brightblue.svg)](Lamdonn@163.com)
![Supported Platforms](https://img.shields.io/badge/platform-Linux%20&%20MinGW-yellow.svg)
@ -67,6 +67,7 @@ varchwe-architecture意为我们的框架库是嵌入式C语言常用
| json | 01.00.00 | [link](/doc/json.md) | [path](./source/05_parser) | JSON文件解析生成器
| txls | 01.01.00 | [link](/doc/txls.md) | [path](./source/05_parser) | TXLS文件解析生成器
| xml | 01.00.00 | [link](/doc/xml.md) | [path](./source/05_parser) | XML文件解析生成器
| yaml | 00.01.00 | [link](/doc/yaml.md) | [path](./source/05_parser) | YAML文件解析生成器
| ramt | 01.00.00 | [link](/doc/ramt.md) | [path](./source/06_performance) | RAM测试模块
| romt | 01.00.00 | [link](/doc/romt.md) | [path](./source/06_performance) | ROM测试模块
| cant | 00.01.00 | [link](/doc/cant.md) | [path](./source/06_performance) | CAN测试模块

436
doc/yaml.en.md Normal file
View File

@ -0,0 +1,436 @@
## Introduction
### What is a YAML file?
YAML (YAML Ain't Markup Language) is a **human-readable data serialization format** that focuses on concisely expressing structured data. It is widely used in configuration files, data exchange, and the description of complex data structures (such as the configurations of tools like Kubernetes and Ansible), rather than for storing traditional spreadsheet-like tabular data.
### Characteristics of YAML files
- **High readability**:
It uses indentation and symbols (such as `-` and `:`) to represent hierarchical relationships, with a format similar to natural language, making it easier to read than JSON/XML.
```yaml
user:
name: Alice
age: 30
hobbies:
- reading
- hiking
```
- **Supports complex data structures**:
It can represent scalars (strings, numbers), lists, dictionaries, etc., and supports nesting and multi-line text (using `|` or `>`).
- **Cross-platform compatibility**:
It is a plain text format that can be parsed by all operating systems and programming languages.
- **Seamless integration with programming languages**:
Most languages (Python, Java, Go, etc.) provide native or third-party libraries (such as PyYAML) to support YAML parsing.
### Uses of YAML
1. **Configuration files** (core use)
- Software configuration (such as Docker Compose, GitLab CI/CD)
- Cloud-native tool configuration (Kubernetes manifests)
```yaml
# Kubernetes Deployment example
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
```
2. **Data serialization**
- Replacing JSON/XML to transfer complex data
- Structured data description for API requests/responses
3. **Data exchange**
- Transferring structured information between different systems (such as microservice configuration synchronization)
### How to create and edit YAML files?
1. **Text editors**
- VS Code (it is recommended to install the YAML plugin for syntax highlighting and validation)
- Sublime Text / Vim, etc.
2. **Specialized tools**
- **Online YAML Validator**: Validates the syntax validity.
- **yq**: A command-line tool (similar to jq for processing YAML).
### Precautions
1. **Indentation sensitivity**
- Spaces must be used (usually 2 or 4 spaces, and tabs are prohibited).
- Incorrect indentation will cause parsing to fail.
2. **Key-value pair format**
- A space is required after the colon: `key: value` (not `key:value`).
3. **Special character handling**
- When a string contains symbols such as `:` and `#`, it is recommended to use quotes:
```yaml
message: "Hello:World"
comment: "This is a # symbol"
```
4. **Multi-line text**
- Preserve line breaks: `|`
```yaml
description: |
This is a
multi-line
text.
```
- Fold line breaks: `>`
```yaml
summary: >
This will fold
into a single line.
```
5. **Data type marking**
- Force specifying the type:
```yaml
boolean: !!bool "true"
timestamp: !!timestamp 2023-07-20T15:30:00Z
```
### Common error examples
```yaml
# Error: Mixing spaces and tabs in indentation
user:
name: Bob
age: 25 # Inconsistent indentation
# Error: Missing space in key-value pair
key1:value1 # Should be changed to key1: value1
# Error: Unescaped special characters
message: Line 1
Line 2 # Missing multi-line text identifier
```
### C language version of the YAML library
The YAML library provided by varch is simple and easy to use, capable of performing most basic operations on YAML files, including loading and saving YAML, as well as adding, deleting, modifying, and querying operations.
## Interfaces
### Creating and deleting YAML objects
```c
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
```
Here, **yaml_t** is the YAML structure. The creation method generates an empty YAML object, and the deletion method deletes the specified YAML object.
### Loading YAML objects
```c
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
```
YAML objects can be loaded from string text or from files. If the loading is successful, a YAML object will be returned; otherwise, NULL will be returned.
The flag is some flag for the operation function, defined as follows:
```
#define YAML_F_NONE (0)
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
```
When the YAML object fails to load, you can call the `int yaml_error_info(int* line, int* column);` function to locate the error.
The error types include:
```
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* date error */
```
### Dumping YAML objects
```c
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
```
First, the **yaml_dumps** method dumps the YAML object into a string according to the format. `*len` is the length of the converted string. When `NULL` is passed, the length is not obtained. The return value is the converted string, which is allocated by the function and **needs to be `free`d after use**.
The **yaml_file_dump** method dumps the YAML object into a file based on the **yaml_dumps** method. `filename` is the file name, and the return value is the length of the dump. A negative value indicates a dump failure.
### Adding child objects to a YAML object
```c
#define yaml_seq_add_null(yaml)
#define yaml_seq_add_int(yaml, num)
#define yaml_seq_add_float(yaml, num)
#define yaml_seq_add_string(yaml, string)
#define yaml_seq_add_sequence(yaml, sequence)
#define yaml_seq_add_mapping(yaml, mapping)
#define yaml_map_add_null(yaml, key)
#define yaml_map_add_int(yaml, key, num)
#define yaml_map_add_float(yaml, key, num)
#define yaml_map_add_string(yaml, key, string)
#define yaml_map_add_sequence(yaml, key, sequence)
#define yaml_map_add_mapping(yaml, key, mapping)
```
The above methods add scalars to sequences and mappings respectively. In fact, they are implemented by applying `insert`-type methods:
```c
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
```
These `insert`-type methods are used to insert child objects of different types at specified positions in a `yaml` object. `key` is the key of the inserted object, and `index` is the insertion position. The return value is the `yaml` object after the insertion operation. If the insertion fails, `NULL` is returned. Specifically:
- `yaml_insert_null`: Inserts a null-type child object.
- `yaml_insert_bool`: Inserts a boolean-type child object, where `b` is the boolean value (`YAML_FALSE` or `YAML_TRUE`).
- `yaml_insert_int`: Inserts an integer-type child object, where `num` is the integer value.
- `yaml_insert_float`: Inserts a floating-point-type child object, where `num` is the floating-point value.
- `yaml_insert_string`: Inserts a string-type child object, where `string` is the string value.
- `yaml_insert_sequence`: Inserts a sequence-type child object, where `sequence` is the sequence object.
- `yaml_insert_mapping`: Inserts a mapping-type child object, where `mapping` is the mapping object.
- `yaml_insert_document`: Inserts a document-type child object, where `document` is the document object.
- `yaml_insert_reference`: Inserts a reference-type child object, where `anchor` is the reference anchor, and `doc` is the referenced document object.
### Removing child objects from a YAML object
```c
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
```
Removes the `index`-th child object with the specified key `key` from the `yaml` object. If the removal is successful, `YAML_E_OK` is returned; otherwise, the corresponding error code is returned.
### YAML object attribute operations
```c
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
```
- `yaml_type`: Gets the type of the `yaml` object. The return value is one of the `YAML_TYPE_*` series of macro definitions, used to determine whether the object is of type `NULL`, boolean, integer, floating-point, string, sequence, mapping, document, reference, or complex key.
- `yaml_size`: Gets the size of the `yaml` object. For sequence or mapping-type objects, it returns the number of elements; for other types of objects, the meaning of the return value may vary depending on the specific implementation.
### YAML object comparison and copying
```c
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
```
- `yaml_compare`: Compares two `yaml` objects. `flag` is the comparison flag, used to specify the comparison method. The return value is the comparison result, and its specific meaning is determined by the implementation. Usually, 0 indicates equality, and non-0 indicates inequality.
- `yaml_copy`: Copies a `yaml` object. `flag` is the copy flag, used to specify the copy method. The return value is the copied `yaml` object. If the copy fails, `NULL` is returned.
### YAML object key operations
```c
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
```
- `yaml_set_key`: Sets a simple key for the `yaml` object, where `key` is the string value of the key. The return value is the `yaml` object after setting the key.
- `yaml_set_key_complex`: Sets a complex key for the `yaml` object, where `key` is the `yaml` object of the complex key. The return value is the `yaml` object after setting the key.
- `yaml_key`: Gets the simple key of the `yaml` object. The return value is the string value of the key.
- `yaml_key_complex`: Gets the complex key of the `yaml` object. The return value is the `yaml` object of the complex key.
### YAML object value setting
```c
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
```
These methods are used to set the value of a `yaml` object, and the return value is the `yaml` object after setting the value. Specifically:
- `yaml_set_null`: Sets the value of the `yaml` object to the null type.
- `yaml_set_bool`: Sets the value of the `yaml` object to the boolean type, where `b` is the boolean value (`YAML_FALSE` or `YAML_TRUE`).
- `yaml_set_int`: Sets the value of the `yaml` object to the integer type, where `num` is the integer value.
- `yaml_set_float`: Sets the value of the `yaml` object to the floating-point type, where `num` is the floating-point value.
- `yaml_set_string`: Sets the value of the `yaml` object to the string type, where `string` is the string value.
- `yaml_set_date`: Sets the value of the `yaml` object to the date type, where `year` is the year, `month` is the month, and `day` is the date.
- `yaml_set_time`: Sets the value of the `yaml` object to the time type, where `hour` is the hour, `minute` is the minute, `second` is the second, and `msec` is the millisecond.
- `yaml_set_utc`: Sets the value of the `yaml` object to the UTC time offset type, where `hour` is the hour offset and `minute` is the minute offset.
- `yaml_set_sequence`: Sets the value of the `yaml` object to the sequence type, where `sequence` is the sequence object.
- `yaml_set_mapping`: Sets the value of the `yaml` object to the mapping type, where `mapping` is the mapping object.
- `yaml_set_document`: Sets the value of the `yaml` object to the document type, where `document` is the document object.
### YAML object value retrieval
```c
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
```
These methods are used to retrieve the value of a `yaml` object, and the return value is the retrieved value. Specifically:
- `yaml_value_bool`: Retrieves the boolean value of the `yaml` object.
- `yaml_value_int`: Retrieves the integer value of the `yaml` object.
- `yaml_value_float`: Retrieves the floating-point value of the `yaml` object.
- `yaml_value_string`: Retrieves the string value of the `yaml` object.
- `yaml_value_sequence`: Retrieves the sequence value of the `yaml` object and returns the sequence object.
- `yaml_value_mapping`: Retrieves the mapping value of the `yaml` object and returns the mapping object.
- `yaml_value_document`: Retrieves the document value of the `yaml` object and returns the document object.
### YAML object child element operations
```c
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
```
- `yaml_attach`: Attaches a `yaml` child object `attach` at the specified position `index` in the `yaml` object. The return value is the `yaml` object after the operation. If the operation fails, `NULL` is returned.
- `yaml_dettach`: Detaches a child object from the specified position `index` in the `yaml` object. The return value is the detached child object. If the operation fails, `NULL` is returned.
### YAML object indexing and child object retrieval
```c
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
```
- `yaml_get_index`: Gets the index of the `index`-th child object with the key `key` in the `yaml` object. The return value is the index of the child object. If not found, `YAML_INV_INDEX` is returned.
- `yaml_get_index_complex`: Gets the index of the child object with the complex key `key` in the `yaml` object. The return value is the index of the child object. If not found, `YAML_INV_INDEX` is returned.
- `yaml_get_child`: Gets the `index`-th child object with the key `key` in the `yaml` object. The return value is the `yaml` object of the child object. If not found, `NULL` is returned.
- `yaml_get_child_complex`: Gets the child object with the complex key `key` in the `yaml` object. The return value is the `yaml` object of the child object. If not found, `NULL` is returned.
### YAML object anchor and alias operations
```c
const char* yaml_get_alias(yaml_t yaml);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
unsigned int yaml_anchor_size(yaml_t yaml);
```
- `yaml_get_alias`: Gets the alias of the `yaml` object. The return value is the string value of the alias.
- `yaml_get_anchor`: Gets the anchor object at the specified index `index` in the `yaml` object. The return value is the `yaml` object of the anchor.
- `yaml_set_alias`: Sets an alias for the `yaml` object, where `alias` is the string value of the alias and `doc` is the associated document object. The return value is the `yaml` object after setting the alias.
- `yaml_set_anchor`: Sets an anchor for the `yaml` object, where `anchor` is the string value of the anchor and `doc` is the associated document object. The return value is the `yaml` object after setting the anchor.
- `yaml_anchor_size`: Gets the number of anchors in the `yaml` object. The return value is the number of anchors.
## Reference examples
### Generating a YAML file
```c
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
```
The dumped file **write.yaml**
```yaml
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```
### Loading a YAML file
Similarly, loading a YAML file
```c
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
```
The running result:
```
load success!
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```

432
doc/yaml.md Normal file
View File

@ -0,0 +1,432 @@
## 介绍
### 什么是YAML文件
YAMLYAML Ain't Markup Language是一种**人类可读的数据序列化格式**专注于简洁表达结构化数据。它广泛用于配置文件、数据交换和复杂数据结构的描述如Kubernetes、Ansible等工具的配置而非传统电子表格类的表格数据存储。
### YAML文件的特点
- **可读性高**
使用缩进和符号(如`-`、`:`表示层级关系类似自然语言的格式比JSON/XML更易阅读。
```yaml
user:
name: Alice
age: 30
hobbies:
- reading
- hiking
```
- **支持复杂数据结构**
可表示标量(字符串、数字)、列表、字典等类型,支持嵌套和多行文本(通过`|`或`>`)。
- **跨平台兼容**
纯文本格式,所有操作系统和编程语言均可解析。
- **与编程语言无缝集成**
大多数语言Python、Java、Go等提供原生或第三方库如PyYAML支持YAML解析。
### YAML的用途
1. **配置文件**(核心用途)
- 软件配置如Docker Compose、GitLab CI/CD
- 云原生工具配置Kubernetes manifests
```yaml
# Kubernetes Deployment示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
```
2. **数据序列化**
- 替代JSON/XML传输复杂数据
- API请求/响应的结构化数据描述
3. **数据交换**
- 不同系统间传递结构化信息(如微服务配置同步)
### 如何创建和编辑YAML文件
1. **文本编辑器**
- VS Code推荐安装YAML插件实现语法高亮和校验
- Sublime Text / Vim等
2. **专用工具**
- **Online YAML Validator**:校验语法有效性
- **yq**命令行工具类似jq用于处理YAML
### 注意事项
1. **缩进敏感**
- 必须使用空格通常2或4空格禁止使用Tab
- 缩进错误会导致解析失败
2. **键值对格式**
- 冒号后需有空格:`key: value`(而非`key:value`
3. **特殊字符处理**
- 字符串包含`:`、`#`等符号时建议使用引号:
```yaml
message: "Hello:World"
comment: "This is a # symbol"
```
4. **多行文本**
- 保留换行符:`|`
```yaml
description: |
This is a
multi-line
text.
```
- 折叠换行符:`>`
```yaml
summary: >
This will fold
into a single line.
```
5. **数据类型标记**
- 强制指定类型:
```yaml
boolean: !!bool "true"
timestamp: !!timestamp 2023-07-20T15:30:00Z
```
### 常见错误示例
```yaml
# 错误缩进混用空格和Tab
user:
name: Bob
age: 25 # 缩进不一致
# 错误:键值对缺少空格
key1:value1 # 应改为 key1: value1
# 错误:未转义特殊字符
message: Line 1
Line 2 # 缺少多行文本标识符
```
### C语言版YAML库
varch提供的YAML库简便易用能完成大部分对于YAML文件的基础操作包含对yaml的加载和保存增删改查等操作。
## 接口
### 创建和删除yaml对象
```c
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
```
其中**yaml_t**为yaml的结构体创建方法会生成一个空yaml对象。删除方法则删除指定的yaml对象。
### yaml对象加载
```c
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
```
yaml对象可以从字符串文本中加载也可以从文件中加载。加载成功则会返回一个yaml对象失败则返回NULL。
flag为操作函数的一些flag定义如下
```
#define YAML_F_NONE (0)
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
```
当yaml对象加载失败的时候可以调用`int yaml_error_info(int* line, int* column);`函数进行定位错误。
错误类型包含
```
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* date error */
```
### yaml对象转储
```c
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
```
首先**yaml_dumps**方法将yaml对象按格式转储为字符串。`*len`则是转换出来的字符串长度,传入`NULL`时候就是不获取长度。返回值则是转换出来的字符串,这个字符串是函数分配的,**在结束使用需要`free`掉**。
**yaml_file_dump**方法则是在**yaml_dumps**的基础上将yaml转储到文件当中`filename`传入文件名,返回值为转储的长度,负值表示转储失败。
### yaml添加子对象
```c
#define yaml_seq_add_null(yaml)
#define yaml_seq_add_int(yaml, num)
#define yaml_seq_add_float(yaml, num)
#define yaml_seq_add_string(yaml, string)
#define yaml_seq_add_sequence(yaml, sequence)
#define yaml_seq_add_mapping(yaml, mapping)
#define yaml_map_add_null(yaml, key)
#define yaml_map_add_int(yaml, key, num)
#define yaml_map_add_float(yaml, key, num)
#define yaml_map_add_string(yaml, key, string)
#define yaml_map_add_sequence(yaml, key, sequence)
#define yaml_map_add_mapping(yaml, key, mapping)
```
以上方法分别在序列中和在映射中添加标量,其实就是套用 `insert` 类方法实现的
```c
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
```
这些`insert`类方法用于在`yaml`对象的指定位置插入不同类型的子对象。`key`为插入对象的键,`index`为插入的位置。返回值为插入操作后的`yaml`对象,若插入失败则返回`NULL`。具体如下:
- `yaml_insert_null`:插入一个空类型的子对象。
- `yaml_insert_bool`:插入一个布尔类型的子对象,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。
- `yaml_insert_int`:插入一个整数类型的子对象,`num`为整数值。
- `yaml_insert_float`:插入一个浮点数类型的子对象,`num`为浮点数值。
- `yaml_insert_string`:插入一个字符串类型的子对象,`string`为字符串值。
- `yaml_insert_sequence`:插入一个序列类型的子对象,`sequence`为序列对象。
- `yaml_insert_mapping`:插入一个映射类型的子对象,`mapping`为映射对象。
- `yaml_insert_document`:插入一个文档类型的子对象,`document`为文档对象。
- `yaml_insert_reference`:插入一个引用类型的子对象,`anchor`为引用的锚点,`doc`为引用的文档对象。
### yaml移除子对象
```c
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
```
移除`yaml`对象中特定的键为`key`的第`index`个子对象。若移除成功,返回`YAML_E_OK`;若移除失败,返回相应的错误码。
### yaml对象属性操作
```c
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
```
- `yaml_type`:获取`yaml`对象的类型,返回值为`YAML_TYPE_*`系列宏定义的值,用于判断对象是`NULL`、布尔、整数、浮点数、字符串、序列、映射、文档、引用或复杂键等类型。
- `yaml_size`:获取`yaml`对象的大小。对于序列或映射类型的对象,返回其元素的数量;对于其他类型的对象,返回值的含义可能因具体实现而异。
### yaml对象比较和复制
```c
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
```
- `yaml_compare`:比较两个`yaml`对象。`flag`为比较的标志位,用于指定比较的方式。返回值为比较结果,具体含义由实现决定,通常 0 表示相等,非 0 表示不相等。
- `yaml_copy`:复制一个`yaml`对象。`flag`为复制的标志位,用于指定复制的方式。返回值为复制后的`yaml`对象,若复制失败则返回`NULL`。
### yaml对象键操作
```c
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
```
- `yaml_set_key`:为`yaml`对象设置一个简单键,`key`为键的字符串值。返回值为设置键后的`yaml`对象。
- `yaml_set_key_complex`:为`yaml`对象设置一个复杂键,`key`为复杂键的`yaml`对象。返回值为设置键后的`yaml`对象。
- `yaml_key`:获取`yaml`对象的简单键,返回值为键的字符串值。
- `yaml_key_complex`:获取`yaml`对象的复杂键,返回值为复杂键的`yaml`对象。
### yaml对象值设置
```c
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
```
这些方法用于设置`yaml`对象的值,返回值为设置值后的`yaml`对象。具体如下:
- `yaml_set_null`:将`yaml`对象的值设置为空类型。
- `yaml_set_bool`:将`yaml`对象的值设置为布尔类型,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。
- `yaml_set_int`:将`yaml`对象的值设置为整数类型,`num`为整数值。
- `yaml_set_float`:将`yaml`对象的值设置为浮点数类型,`num`为浮点数值。
- `yaml_set_string`:将`yaml`对象的值设置为字符串类型,`string`为字符串值。
- `yaml_set_date`:将`yaml`对象的值设置为日期类型,`year`为年份,`month`为月份,`day`为日期。
- `yaml_set_time`:将`yaml`对象的值设置为时间类型,`hour`为小时,`minute`为分钟,`second`为秒,`msec`为毫秒。
- `yaml_set_utc`:将`yaml`对象的值设置为 UTC 时间偏移类型,`hour`为小时偏移,`minute`为分钟偏移。
- `yaml_set_sequence`:将`yaml`对象的值设置为序列类型,`sequence`为序列对象。
- `yaml_set_mapping`:将`yaml`对象的值设置为映射类型,`mapping`为映射对象。
- `yaml_set_document`:将`yaml`对象的值设置为文档类型,`document`为文档对象。
### yaml对象值获取
```c
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
```
这些方法用于获取`yaml`对象的值,返回值为获取到的值。具体如下:
- `yaml_value_bool`:获取`yaml`对象的布尔值。
- `yaml_value_int`:获取`yaml`对象的整数值。
- `yaml_value_float`:获取`yaml`对象的浮点数值。
- `yaml_value_string`:获取`yaml`对象的字符串值。
- `yaml_value_sequence`:获取`yaml`对象的序列值,返回序列对象。
- `yaml_value_mapping`:获取`yaml`对象的映射值,返回映射对象。
- `yaml_value_document`:获取`yaml`对象的文档值,返回文档对象。
### yaml对象子元素操作
```c
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
```
- `yaml_attach`:在`yaml`对象的指定位置`index`附加一个`yaml`子对象`attach`。返回值为操作后的`yaml`对象,若操作失败则返回`NULL`。
- `yaml_dettach`:从`yaml`对象的指定位置`index`分离一个子对象。返回值为分离出的子对象,若操作失败则返回`NULL`。
### yaml对象索引和子对象获取
```c
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
```
- `yaml_get_index`:获取`yaml`对象中键为`key`的第`index`个子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。
- `yaml_get_index_complex`:获取`yaml`对象中复杂键为`key`的子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。
- `yaml_get_child`:获取`yaml`对象中键为`key`的第`index`个子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。
- `yaml_get_child_complex`:获取`yaml`对象中复杂键为`key`的子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。
### yaml对象锚点和别名操作
```c
const char* yaml_get_alias(yaml_t yaml);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
unsigned int yaml_anchor_size(yaml_t yaml);
```
- `yaml_get_alias`:获取`yaml`对象的别名,返回值为别名的字符串值。
- `yaml_get_anchor`:获取`yaml`对象中指定索引`index`的锚点对象,返回值为锚点的`yaml`对象。
- `yaml_set_alias`:为`yaml`对象设置别名,`alias`为别名的字符串值,`doc`为关联的文档对象。返回值为设置别名后的`yaml`对象。
- `yaml_set_anchor`:为`yaml`对象设置锚点,`anchor`为锚点的字符串值,`doc`为关联的文档对象。返回值为设置锚点后的`yaml`对象。
- `yaml_anchor_size`:获取`yaml`对象中锚点的数量。返回值为锚点的数量。
## 参考例子
### 生成yaml文件
```c
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
```
转储的文件 **write.yaml**
```yaml
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```
### 加载yaml文件
同样加载yaml文件
```c
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
```
运行结果:
```
load success!
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
```

View File

@ -1,3 +1,11 @@
version 0.3.4
date 2025.05.10
changes
1. Add the initial version yaml parser
---------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
version 0.3.3
date 2025.03.18
changes

7073
source/05_parser/yaml.c Normal file

File diff suppressed because it is too large Load Diff

278
source/05_parser/yaml.h Normal file
View File

@ -0,0 +1,278 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file yaml.h
* \unit yaml
* \brief This is a C language version of yaml streamlined parser
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#ifndef __yaml_H
#define __yaml_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdarg.h>
#include <limits.h>
#include <stdlib.h>
/* version infomation */
#define YAML_V_MAJOR 0
#define YAML_V_MINOR 1
#define YAML_V_PATCH 0
/* yaml type definition, hiding structural members, not for external use */
/**
* \brief Opaque handle to YAML node structure
*
* This typedef provides an opaque pointer to the internal YAML node structure.
* The actual structure definition is hidden from external code to maintain encapsulation.
*/
typedef struct YAML* yaml_t;
/* yaml normal types define */
/**
* \brief Enumeration of YAML data types
*
* These constants represent the different data types supported by the YAML parser.
* They are used to identify the type of value stored in a YAML node.
*/
#define YAML_TYPE_NULL (0) /* null type */
#define YAML_TYPE_BOOL (1) /* bool type */
#define YAML_TYPE_INT (2) /* number int type */
#define YAML_TYPE_FLOAT (3) /* number float type */
#define YAML_TYPE_STRING (4) /* string type */
#define YAML_TYPE_DATE (5) /* date type */
#define YAML_TYPE_SEQUENCE (6) /* sequence type */
#define YAML_TYPE_MAPPING (7) /* mapping type */
#define YAML_TYPE_DOCUMENT (8) /* document type */
#define YAML_TYPE_REFERENCE (9) /* reference type */
#define YAML_TYPE_COMPLEX_KEY (10) /* complex key */
/* bool define */
/**
* \brief Boolean values
*
* These constants represent boolean true and false values in the YAML parser.
*/
#define YAML_FALSE (0) /* bool false */
#define YAML_TRUE (1) /* bool true */
/* error type define */
/**
* \brief Error codes returned by the YAML parser
*
* These constants represent various error conditions that can occur during YAML parsing.
* They are used to indicate the nature of parsing errors to the calling code.
*/
#define YAML_E_OK (0) /* ok, no error */
#define YAML_E_INVALID (1) /* invalid, not a valid expected value */
#define YAML_E_END (2) /* many invalid characters appear at the end */
#define YAML_E_KEY (3) /* parsing key, invalid key content found */
#define YAML_E_VALUE (4) /* parsing value, invalid value content found */
#define YAML_E_MEMORY (5) /* memory allocation failed */
#define YAML_E_SQUARE (6) /* mising ']' */
#define YAML_E_CURLY (7) /* mising '}' */
#define YAML_E_TAB (8) /* incorrect indent depth */
#define YAML_E_MIX (9) /* mix type */
#define YAML_E_FLINE (10) /* the first line of value can only be a literal */
#define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */
#define YAML_E_LBREAK (12) /* line break */
#define YAML_E_NANCHOR (13) /* null anchor */
#define YAML_E_IANCHOR (14) /* invalid anchor */
#define YAML_E_RANCHOR (15) /* repeat anchor */
#define YAML_E_UANCHOR (16) /* undefine anchor */
#define YAML_E_TANCHOR (17) /* type error anchor */
#define YAML_E_DATE (18) /* date error */
#define YAML_E_TARTGET (19) /* target error */
/* dump flags define */
/**
* \brief Flags used to control YAML dumping and loading behavior
*
* These flags can be combined using bitwise OR to modify the behavior
* of functions that dump or load YAML documents.
*/
#define YAML_F_NONE (0) /* none flag */
#define YAML_F_DFLOW (0x01) /* dumps flow format */
#define YAML_F_LDOCS (0x02) /* load muti documents */
#define YAML_F_NOKEY (0x04) /* operate without key */
#define YAML_F_COMPLEX (0x08) /* operate with complex key */
#define YAML_F_ANCHOR (0x10) /* operate with anchor */
#define YAML_F_RECURSE (0x20) /* operate recurse */
#define YAML_F_REFERENCE (0x40) /* operate with reference */
/**
* \brief Represents an invalid index value
*
* This constant is used to indicate an invalid or non-existent index
* in functions that operate on indexed YAML structures.
*/
#define YAML_INV_INDEX ((unsigned int)(-1))
// Functions for creating and deleting YAML nodes.
// These functions are used to initialize and free the memory resources of YAML nodes.
yaml_t yaml_create(void);
void yaml_delete(yaml_t yaml);
// Function for getting YAML parsing error information.
// It is used to obtain error line number and column number during YAML parsing.
int yaml_error_info(int* line, int* column);
// Functions for getting the type and size of a YAML node.
// Used to determine the data type of a YAML node and get the number of child elements of a container node.
int yaml_type(yaml_t yaml);
unsigned int yaml_size(yaml_t yaml);
// Functions for comparing and copying YAML nodes.
// Used to check if two YAML nodes are equal and to create a copy of a YAML node.
int yaml_compare(yaml_t yaml, yaml_t cmp, int flag);
yaml_t yaml_copy(yaml_t yaml, int flag);
// Functions for setting the key of a YAML node.
// Used to set a simple key or a complex key for a YAML node.
yaml_t yaml_set_key(yaml_t yaml, const char* key);
yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key);
// Functions for getting the key of a YAML node.
// Used to retrieve the simple key or complex key of a YAML node.
const char* yaml_key(yaml_t yaml);
yaml_t yaml_key_complex(yaml_t yaml);
// Functions for setting the value of a YAML node.
// Used to set a YAML node to different types of values such as null, boolean, integer, float, string, date, time, sequence, mapping, document, etc.
yaml_t yaml_set_null(yaml_t yaml);
yaml_t yaml_set_bool(yaml_t yaml, int b);
yaml_t yaml_set_int(yaml_t yaml, int num);
yaml_t yaml_set_float(yaml_t yaml, double num);
yaml_t yaml_set_string(yaml_t yaml, const char* string);
yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day);
yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec);
yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute);
yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence);
yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping);
yaml_t yaml_set_document(yaml_t yaml, yaml_t document);
// Functions for getting the value of a YAML node.
// Used to retrieve the boolean value, integer value, float value, string value, sequence value, mapping value, document value, etc. of a YAML node.
int yaml_value_bool(yaml_t yaml);
int yaml_value_int(yaml_t yaml);
double yaml_value_float(yaml_t yaml);
const char* yaml_value_string(yaml_t yaml);
yaml_t yaml_value_sequence(yaml_t yaml);
yaml_t yaml_value_mapping(yaml_t yaml);
yaml_t yaml_value_document(yaml_t yaml);
// Functions for attaching and detaching YAML nodes.
// Used to attach a YAML node to another node or detach a child node from a node.
yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach);
yaml_t yaml_dettach(yaml_t yaml, unsigned int index);
// Functions for inserting values into a YAML node.
// Used to insert different types of values such as null, boolean, integer, float, string, sequence, mapping, document, reference, etc. into a YAML node.
yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b);
yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num);
yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num);
yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string);
yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence);
yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping);
yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document);
yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc);
// Function for removing a value from a YAML node.
// Used to remove a value from a YAML node according to the key and index.
int yaml_remove(yaml_t yaml, const char* key, unsigned int index);
// Functions for getting the index of a YAML node.
// Used to obtain the index of a value in a YAML node, supporting the index acquisition of simple keys and complex keys.
unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index);
unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key);
// Functions for getting child nodes of a YAML node.
// Used to get the child nodes of a YAML node according to the key and index, supporting the retrieval of child nodes of simple keys and complex keys.
yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index);
yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key);
// Functions for setting and getting the alias of a YAML node.
// Used to set an alias for a YAML node and retrieve the alias of a YAML node.
yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc);
const char* yaml_get_alias(yaml_t yaml);
// Functions for setting and getting the anchor of a YAML node.
// Used to set an anchor for a YAML node, retrieve the anchor, and get the size of the anchor.
yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc);
yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index);
unsigned int yaml_anchor_size(yaml_t yaml);
// YAML serialization functions.
// Used to serialize a YAML node into a string (yaml_dumps) or write it to a file (yaml_file_dump).
char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag);
int yaml_file_dump(yaml_t yaml, char* filename);
// YAML deserialization functions.
// Used to load YAML data from a string (yaml_loads) or a file (yaml_file_dump) and construct a YAML node.
yaml_t yaml_loads(const char* text, int flag);
yaml_t yaml_file_load(char* filename, int flag);
// Macros for judging the type of a YAML node.
// Used to determine whether a YAML node is of a specific type (null, bool, int, float, string, sequence, mapping, document).
#define yaml_isnull(yaml) (yaml_type(yaml)==YAML_TYPE_NULL)
#define yaml_isbool(yaml) (yaml_type(yaml)==YAML_TYPE_BOOL)
#define yaml_isint(yaml) (yaml_type(yaml)==YAML_TYPE_INT)
#define yaml_isfloat(yaml) (yaml_type(yaml)==YAML_TYPE_FLOAT)
#define yaml_isstring(yaml) (yaml_type(yaml)==YAML_TYPE_STRING)
#define yaml_issequence(yaml) (yaml_type(yaml)==YAML_TYPE_SEQUENCE)
#define yaml_ismapping(yaml) (yaml_type(yaml)==YAML_TYPE_MAPPING)
#define yaml_isdocument(yaml) (yaml_type(yaml)==YAML_TYPE_DOCUMENT)
// Macros for adding values to a YAML node.
// Used to quickly add different types of values to a YAML sequence or mapping node.
#define yaml_seq_add_null(yaml) yaml_insert_null((yaml), 0, yaml_size((yaml)))
#define yaml_seq_add_bool(yaml, b) yaml_insert_bool((yaml), 0, yaml_size((yaml)), (b))
#define yaml_seq_add_int(yaml, num) yaml_insert_int((yaml), 0, yaml_size((yaml)), (num))
#define yaml_seq_add_float(yaml, num) yaml_insert_float((yaml), 0, yaml_size((yaml)), (num))
#define yaml_seq_add_string(yaml, string) yaml_insert_string((yaml), 0, yaml_size((yaml)), (string))
#define yaml_seq_add_sequence(yaml, sequence) yaml_insert_sequence((yaml), 0, yaml_size((yaml)), (sequence))
#define yaml_seq_add_mapping(yaml, mapping) yaml_insert_mapping((yaml), 0, yaml_size((yaml)), (mapping))
#define yaml_map_add_null(yaml, key) yaml_insert_null((yaml), (key), yaml_size((yaml)))
#define yaml_map_add_bool(yaml, key, b) yaml_insert_bool((yaml), (key), yaml_size((yaml)), (b))
#define yaml_map_add_int(yaml, key, num) yaml_insert_int((yaml), (key), yaml_size((yaml)), (num))
#define yaml_map_add_float(yaml, key, num) yaml_insert_float((yaml), (key), yaml_size((yaml)), (num))
#define yaml_map_add_string(yaml, key, string) yaml_insert_string((yaml), (key), yaml_size((yaml)), (string))
#define yaml_map_add_sequence(yaml, key, sequence) yaml_insert_sequence((yaml), (key), yaml_size((yaml)), (sequence))
#define yaml_map_add_mapping(yaml, key, mapping) yaml_insert_mapping((yaml), (key), yaml_size((yaml)), (mapping))
#ifdef __cplusplus
}
#endif
#endif

108
test/file/read.yaml Normal file
View File

@ -0,0 +1,108 @@
mapping:
open: Yes
string null: string null
string null n: string null
n
string sigle: 'string sigle'
string sigle n: 'string
sigle'
string double: "string double"
string double n: "string
double"
string scalar: |
string
scalar
string scalar n: |2
string
scalar
string scalar -: |-
string
scalar
string scalar +: |+
string
scalar
string fold: >
string
fold
string fold n: >2
string
fold
string fold -: >-
string
fold
string fold +: >+
string
fold
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg
number dec: 123456
number hex: 0x123456
number float: 012.3456
number e: 1.23e-4
number nan: .nan
number inf: .inf
number inf n: -.inf
Anchors:
people: &people
name: people
age: 18
height: 178
cat: &cat
name: cat
age: 2
string: &string
This is string
sequence: &sequence
- 1
- 2
Zhang San: &default
color: blue
size: medium
property: *people
Inherit:
<<: *people
# shape: circle
square brackets: [1,2,3,4]
brace brackets: {a: 1, b: 2, c: 3}
bin: -0b1010_0111_0100_1010_1110
iso8601: 2025-03-29T23:59:43.89-05:00
date: 2025-03-29
time: 2025-03-29T23:59:43
utc: 2025-03-29T23:59:43+08:00
int_22: !!int 123456
muti:
a: "AABCDEFG"
?
- a
- b
: fruit

11
test/file/write.yaml Normal file
View File

@ -0,0 +1,11 @@
mapping:
version: 1.0.0
author: Lamdonn
license: GPL-2.0
sequence:
- file description
- This is a C language version of yaml streamlined parser
- Copyright (C) 2023 Lamdonn.
-
age: 18
weight: 75kg

View File

@ -9,6 +9,7 @@ TEST_LIST += txls
TEST_LIST += xml
TEST_LIST += csv
TEST_LIST += json
TEST_LIST += yaml
TEST_LIST += vector
TEST_LIST += list
TEST_LIST += str

579
test/test_yaml.c Normal file
View File

@ -0,0 +1,579 @@
#include <stdio.h>
#include <string.h>
#if defined(TEST_TARGET_yaml)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/yaml.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "yaml.h"
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
#define READ_FILE "test/file/read.yaml"
#define WRITE_FILE "test/file/write.yaml"
static void yaml_preview(yaml_t yaml)
{
char *s = yaml_dumps(yaml, 0, 0, YAML_F_NONE);
if (s)
{
printf("%s\r\n", s);
free(s);
}
}
static void test_create(void)
{
yaml_t yaml = yaml_create();
if (yaml)
{
printf("yaml_create success!!! %p\r\n", yaml);
}
else
{
printf("yaml_create fail!!!\r\n");
}
yaml_delete(yaml);
}
static void test_add(void)
{
yaml_t yaml = NULL, info = NULL, hobby = NULL, key = NULL, complex = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "Lamdonn", yaml);
info = yaml_map_add_mapping(yaml, "Zhangsan", NULL);
yaml_map_add_int(info, "age", 29);
yaml_map_add_float(info, "height", 175);
yaml_map_add_string(info, "email", "Zhangsan@163.com");
hobby = yaml_map_add_sequence(info, "hobby", NULL);
yaml_seq_add_string(hobby, "Playing basketball");
yaml_seq_add_string(hobby, "Playing football");
yaml_seq_add_string(hobby, "Playing mobile game");
yaml_t kk = yaml_seq_add_mapping(hobby, NULL);
yaml_map_add_int(kk, "year", 29);
yaml_map_add_float(kk, "month", 6);
info = yaml_map_add_mapping(yaml, "Lisi", NULL);
complex = yaml_map_add_int(info, "1", 29);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_insert(void)
{
yaml_t root = NULL, doc = NULL, t = NULL;
root = yaml_create();
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
doc = yaml_insert_document(root, yaml_size(root), NULL);
yaml_map_add_int(doc, "age", 26);
yaml_map_add_float(doc, "height", 178.5);
yaml_map_add_string(doc, "email", "Lamdonn@163.com");
yaml_preview(root);
yaml_delete(root);
}
static void test_remove(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_remove(info, "email", 0);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_set(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_null(info);
yaml_preview(yaml);
yaml_set_bool(info, YAML_TRUE);
yaml_preview(yaml);
yaml_set_int(info, 123);
yaml_preview(yaml);
yaml_set_float(info, 3.14159);
yaml_preview(yaml);
yaml_set_string(info, "Hello yaml");
yaml_preview(yaml);
yaml_set_date(info, 2025, 01, 01);
yaml_set_time(info, 16, 30, 03, 200);
yaml_set_utc(info, +8, 30);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_get(void)
{
yaml_t yaml = NULL, info = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_t bool_ = yaml_map_add_bool(info, "open source", YAML_TRUE);
printf("bool_: %d\r\n", yaml_value_bool(bool_));
yaml_t int_ = yaml_map_add_int(info, "age", 26);
printf("int_: %d\r\n", yaml_value_int(int_));
yaml_t float_ = yaml_map_add_float(info, "height", 178.5);
printf("float_: %f\r\n", yaml_value_float(float_));
yaml_t string_ = yaml_map_add_string(info, "email", "Lamdonn@163.com");
printf("string_: %s\r\n", yaml_value_string(string_));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_key(void)
{
yaml_t yaml = NULL, key = NULL;
yaml = yaml_create();
yaml_set_key(yaml, "StringKey");
printf("key: %s\r\n", yaml_key(yaml));
key = yaml_create();
yaml_map_add_int(key, "a", 12);
yaml_map_add_int(key, "b", 13);
yaml_set_key_complex(yaml, key);
printf("complex key: \r\n");
yaml_preview(yaml_key_complex(yaml));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_child(void)
{
yaml_t yaml = NULL, info = NULL, key = NULL, child = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
key = yaml_create();
yaml_map_add_int(key, "a", 12);
yaml_map_add_int(key, "b", 13);
yaml_set_key_complex(yaml_map_add_string(info, "0", "This is a complex key"), key);
child = yaml_get_child(info, "age", 0);
printf("child['age']: %d\r\n", yaml_value_int(child));
child = yaml_get_child(info, NULL, 1);
printf("child[1]: %f\r\n", yaml_value_float(child));
child = yaml_get_child_complex(info, key);
printf("child[?]: %s\r\n", yaml_value_string(child));
printf("child['email'] index = %u\r\n", yaml_get_index(info, "email", 0));
printf("child[?] index = %u\r\n", yaml_get_index_complex(info, key));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_alias(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "AuthorInfo", yaml);
printf("alias: %s\r\n", yaml_get_alias(info));
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_anchor(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
yaml_set_alias(info, "AuthorInfo", yaml);
for (int i = 0; i < yaml_anchor_size(yaml); i++)
{
yaml_t t = yaml_get_anchor(yaml, i);
printf("anchor[%d]: %s\r\n", i, yaml_get_alias(t));
}
temp = yaml_map_add_mapping(yaml, "Something", NULL);
yaml_set_anchor(temp, "AuthorInfo", yaml);
temp = yaml_map_add_mapping(yaml, "Extended", NULL);
yaml_insert_reference(temp, NULL, 0, "AuthorInfo", yaml);
yaml_preview(yaml);
yaml_delete(yaml);
}
static void test_copy(void)
{
yaml_t yaml = NULL, info = NULL, copy = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
printf("SOURCE yaml: \r\n");
yaml_preview(yaml);
printf("COPY yaml: \r\n");
copy = yaml_copy(yaml, YAML_F_RECURSE);
yaml_preview(copy);
yaml_delete(copy);
yaml_delete(copy);
}
static void test_compare(void)
{
yaml_t yaml = NULL, info = NULL, temp = NULL, copy = NULL;
yaml = yaml_create();
info = yaml_map_add_mapping(yaml, "Lamdonn", NULL);
yaml_map_add_int(info, "age", 26);
yaml_map_add_float(info, "height", 178.5);
yaml_map_add_string(info, "email", "Lamdonn@163.com");
copy = yaml_copy(yaml, YAML_F_RECURSE);
printf("yaml_compare %d\r\n", yaml_compare(yaml, copy, YAML_F_RECURSE));
yaml_delete(copy);
yaml_delete(copy);
}
static void test_dump(void)
{
yaml_t root, node, temp;
root = yaml_create();
yaml_set_mapping(root, NULL);
node = yaml_map_add_mapping(root, "mapping", NULL);
yaml_map_add_string(node, "version", "1.0.0");
yaml_map_add_string(node, "author", "Lamdonn");
yaml_map_add_string(node, "license", "GPL-2.0");
node = yaml_map_add_sequence(root, "sequence", NULL);
yaml_seq_add_string(node, "file description");
yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser");
yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn.");
temp = yaml_seq_add_mapping(node, NULL);
yaml_map_add_string(temp, "age", "18");
yaml_map_add_string(temp, "height", "178cm");
yaml_map_add_string(temp, "weight", "75kg");
yaml_remove(temp, 0, 1);
/* preview yaml */
yaml_preview(root);
/* dump yaml file */
yaml_file_dump(root, WRITE_FILE);
yaml_delete(root);
}
static void test_load(void)
{
yaml_t root = NULL, x = NULL;
root = yaml_file_load(READ_FILE, YAML_F_LDOCS);
if (!root)
{
int type = 0, line = 0, column = 0;
type = yaml_error_info(&line, &column);
printf("error at line %d column %d type %d.\r\n", line, column, type);
return;
}
printf("load success!\r\n");
yaml_preview(root);
yaml_delete(root);
}
static void test_base(void)
{
// test_dump();
test_load();
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: yaml [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" <create> Test create and delete functions\n"
" <add> Test add category functions\n"
" <insert> Test insert category functions\n"
" <remove> Test remove category functions\n"
" <set> Test set category functions\n"
" <get> Test get category functions\n"
" <key> Test key category functions\n"
" <child> Test child category functions\n"
" <alias> Test alias category functions\n"
" <anchor> Test anchor category functions\n"
" <copy> Test copy function\n"
" <compare> Test compare function\n"
" <dump> Test dump functions\n"
" <load> Test load functions\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::");
if (opt == -1) break;
switch (opt)
{
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("yaml version %d.%d.%d\r\n", YAML_V_MAJOR, YAML_V_MINOR, YAML_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_yaml)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
else if (!strcmp(execute, "add"))
{
test_add();
}
else if (!strcmp(execute, "insert"))
{
test_insert();
}
else if (!strcmp(execute, "remove"))
{
test_remove();
}
else if (!strcmp(execute, "set"))
{
test_set();
}
else if (!strcmp(execute, "get"))
{
test_get();
}
else if (!strcmp(execute, "key"))
{
test_key();
}
else if (!strcmp(execute, "child"))
{
test_child();
}
else if (!strcmp(execute, "alias"))
{
test_alias();
}
else if (!strcmp(execute, "anchor"))
{
test_anchor();
}
else if (!strcmp(execute, "copy"))
{
test_copy();
}
else if (!strcmp(execute, "compare"))
{
test_compare();
}
else if (!strcmp(execute, "dump"))
{
test_dump();
}
else if (!strcmp(execute, "load"))
{
test_load();
}
}
else
{
test_base();
}
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_yaml)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_yaml(void)
{
command_export("yaml", test);
}
init_export_app(test_yaml);
#endif