#dsl #html #async #serde-derive #error-message #line-numbers

munyo

一种旨在成为最有效手写数据的编程语言

17 个版本 (5 个重大更改)

新功能 0.8.0 2024 年 8 月 7 日
0.7.0 2024 年 8 月 4 日
0.6.1 2024 年 7 月 29 日
0.5.0 2024 年 7 月 24 日
0.3.1 2023 年 11 月 29 日

#571解析器实现

Download history 2/week @ 2024-07-03 116/week @ 2024-07-17 362/week @ 2024-07-24 141/week @ 2024-07-31

每月 619 次下载

MIT/Apache 许可

135KB
3.5K SLoC

Munyo

crates.io link Doc link

Munyo

Munyo 是一种旨在成为最有效手写数据的编程语言。当发生错误时,您还可以看到清晰的错误消息以及行号。

让我们看看如何在 Munyo 中高效地编写数据。

以下是竞技宝可梦对战队伍配置的数据。 完整示例

|| <- This is the syntax for comments.
|| In the competitive Pokémon world, rankings are announced once a month.
>>>Season
2024 6 || The season of June 2024
	>>>Team
	1 || The #1 ranked team
		>>>Pokemon
		Koraidon Fire AssaultVest H204A+196B4C-0D12S92 FlameCharge FlareBlitz DrainPunch Uturn
		FlutterMane Fairy ChoiceSpecs H148A-(0)B100C188D4S+68 MoonBlast ShadowBall DrainingKiss PerishSong | ability Protosynthesis 
			|| The followings are some variations of the customization of this 
			|| Pokémon(not necessary, just for illustration purposes)
			>Item
			BoostEnergy
			FocusSash
			>Terastal
			Normal
			Ground
			Water
		|| A team contains 6 Pokémons...
	2 ||...
	|| Players ranked 200 or higher tend to publish their Pokémon compositions in their blogs voluntarily.
2024 5
	1
	||...

Munyo 中每行都必须有类型名

typename arg1 arg2...

上面的行有些不准确。如果您想正确学习语法,请阅读 lang_spec.txt

您可以通过设置默认类型名来省略类型名

|| Set the default typename 'Season'
>>>Season
2024 6
|| ↑ This line becomes 'Season 2024 6'

以下是对应的 Rust 数据结构,用于捕获该行。

#[derive(serde::Deserialize)]
enum Top {
    Season(usize, usize, Vec<Second>),
}

↑ Munyo 的基本用法是与 'serde' 一起使用。要使用 serde 解析 Munyo,要反序列化的数据结构必须是 'enum'。该枚举必须实现 'serde::Deserialize'。此枚举在 'derive' 部分实现了它。

	Season(usize, usize, Vec<Second>)
||  ↑typename ↑20246     ↑ the container of the data for child lines

↑ 第一个参数(2024)被消耗到第一个 'usize'。第二个(6)是第二个。如果一个行有子行,最后一个项必须是枚举的 Vec,该枚举捕获子行。

因为参数没有名称,您需要将它们转换为合适的数据结构。

首先,让我们解析 Munyo 的源文本

let r: Vec<Top> = munyo::from_str(.../* the sample text */)?;

↑ 您可以使用 'munyo::from_str' 使用 serde 反序列化 Munyo。'Top' 实现 'serde::Deserialize',因此您可以使用 'munyo::from_str' 与类型声明 'Vec'。

let r: Vec<Season> = r.into_iter().map(top_to_season).collect();

// the full-fledged data structure
struct Season {
    year: usize,
    month: usize,
    teams: Vec<Team>,
}

fn top_to_season(top: Top) -> Season {
    match top {
        Top::Season(year, month, vec) => Season {
            year,
            month,
            teams: vec.into_iter().map(second_to_team).collect(),
        },
    }
}

您需要使用单分支的 'match' 来处理它。

编写子项时需要缩进。

>>>Season
2024 6 
	>>>Team
	1 || #1 ranked team
	|| ↑ Indentation means the line is a child of the one less indented line.

缩进字符必须是 TAB(ASCII 码 9)。您可能需要更改文本编辑器的设置。

#[derive(Debug, serde::Deserialize)]
enum Second {
    Team(usize, Vec<Third>),
}

struct Team {
    rank: usize,
    pokemons: Vec<Pokemon>,
}

fn second_to_team(second: Second) -> Team {
    match second {
        Second::Team(rank, vec) => Team {
            rank,
            pokemons: vec.into_iter().map(third_to_pokemon).collect(),
        },
    }
}

这是第三级,描述宝可梦

		>>>Pokemon
		Koraidon Fire AssaultVest H204A+196B4C-0D12S92 FlameCharge FlareBlitz DrainPunch Uturn
#[derive(serde::Deserialize)]
enum Third {
    Pokemon(
        PokeName,
        PokeType,
        PokeItem,
        PokeValues,
        PokeMove,
        PokeMove,
        PokeMove,
        PokeMove,
        Param,
        Vec<Fourth>,
    ),
}

struct Pokemon {
    name: PokeName,
    poke_type: PokeType,
    item: PokeItem,
    custom: PokeValues,
    moves: Vec<PokeMove>,
    ability: Option<Ability>,
    other_items: Vec<PokeItem>,
    other_terastals: Vec<PokeType>,
}

#[derive(serde::Deserialize)]
enum PokeName {
    Koraidon,
    FlutterMane,
}

#[derive(serde::Deserialize)]
enum PokeType {
    Fire,
    Fairy,
    Normal,
    Ground,
    Water,
}
//...

宝可梦行 'Pokemon' 由宝可梦名称、宝可梦类型、宝可梦道具等组成。这些项也定义为 'enum'。

如果您写入不在枚举变体中的项,Munyo 将输出类似错误消息的内容

9: unknown variant `Koraido`, expected `Koraidon` or `FlutterMane`
            Koraido Fire AssaultVest H204A+196B4C-0D12S92 FlameCharge FlareBlitz DrainPunch Uturn

当发生错误时,Munyo 总是输出行号和行。在这种情况下,serde 也正确地找到了原因。

宝可梦自定义具有传统表示法

H204A+196B4C-0D12S92

要解析此内容,您需要实现解析器。Munyo 无法为您完成此操作。我的建议是 pest

我的解析器实现

#[derive(Parser)]
#[grammar_inline = r###"
alpha = {
	"H" | "A" | "B" | "C" |"D"| "S"
}

sign = {
	"+" | "-"
}

number_char = _{
	'0'..'9'
}

number = {
	number_char+
}

bracketed_number ={
	"(" ~ number ~ ")"
}

chunk = {
	alpha ~ sign? ~ (number | bracketed_number)
}

poke_custom ={
	SOI ~ chunk+ ~ EOI
}

"###]

当解析器实现返回错误信息时,Munyo会输出行号和该行的文本。

10: 260 is bigger than 252
        	FlutterMane Fairy ChoiceSpecs H148A-(0)B100C260D4S+68 MoonBlast ShadowBall DrainingKiss PerishSong | ability Protosynthesis 

252是宝可梦参数自定义的最大值。

实现自定义解析器和输出有用的错误信息对于最有效的数据语言至关重要。

该语言的目标是将文本数据中的冗余降到最低。另一方面,支持代码并不简单,但如您所见,它并不复杂,我认为。

宝可梦有技能,但有些宝可梦只有一个技能。您无需为它们写下它。

如果您需要可选参数,可以使用'param'。

typename arg1 arg2...| param_name arg | param_name2 arg2...

↑ 这是Munyo中参数的语法。

数据已经使用了它。

FlutterMane Fairy... | ability Protosynthesis 

支持代码如下。

#[derive(serde::Deserialize)]
enum Third {
    Pokemon(
        PokeName,
        PokeType,
        PokeItem,
        PokeValues,
        PokeMove,
        PokeMove,
        PokeMove,
        PokeMove,
        Param,  // <- Structs are for parameters
        Vec<Fourth>,
    ),
}

#[derive(serde::Deserialize)]
struct Param {
	// field names are used as param-names
    ability: Option<Ability>,
}

#[derive(serde::Deserialize)]
enum Ability {
    Protosynthesis,
}

结构体的名称可以是任何名称。它不影响Munyo。在这种情况下,名称是'Param'。

结构体必须是 'serde::Deserialize',字段名称用作参数名称。在这种情况下,它是'ability'。

FlutterMane Fairy... | ability Protosynthesis 
                       || ↑ the field name

Koraidon Fire... 
				 || ↑ No 'ability' parameter for this Pokemon

它可以是Option,这意味着它是可选的。'Koraidon'没有'ability',正如您所看到的。

它只有一个可选参数,这意味着参数名称'ability'可以省略。

FlutterMane Fairy ChoiceSpecs H148A-(0)B100C188D4S+68 MoonBlast... Protosynthesis
|| ↑ Attach only the ability name at the last if the Pokemon need it.

我创建了省略版本。 版本1 简单但错误信息中没有行号,因为错误信息是在转换过程中返回的,没有行号信息。 版本2 实现了一个简单的自定义数据结构来输出行号。当解析过程中返回错误时,Munyo自动附加行号。如果您想查看它们。

如果您需要更高效的语法,您可以编写一个自定义解析器,该解析器可以在一行中获取任意数量的参数,并且可以自动检测参数的类型。如果您想查看,请参阅另一个 示例

宝可梦基本上有四个技能。我天真地实现了它。

enum Third {
    Pokemon(
        PokeName,
        PokeType,
        PokeItem,
        PokeValues,
        PokeMove, // <- four moves
        PokeMove,
        PokeMove,
        PokeMove, // <-
        Param,  
        Vec<Fourth>,
    ),
}

这更健壮,但如果您想使物品具有多个子项,基本需要使用子项(或创建自定义解析器)。

第四个缩进级别是它的示例,尽管它们对于这个宝可梦数据不是必需的。

		FlutterMane Fairy ChoiceSpecs H148A-(0)B100C188D4S+68 MoonBlast ShadowBall DrainingKiss PerishSong | ability Protosynthesis 
			>Item
			BoostEnergy
			FocusSash
			>Terastal
			Normal
			Ground
			Water

虽然 '>>>' 定义了缩进级别的类型名,但 '>' 定义了当前级别的类型名。

Foo
	>>>TripledType
	A
		>SingledType
		B
		>
		Canceled
		>SingledType2
		C
	StillAffected
		HereIsNotCurrentLevel		
	>>>Triple2
	D

这变成了以下内容。

Foo
	>>>TripledType
	TripledType A
		>SingledType
		SingledType B
		> 
		|| ↑ Single '>' with no name means canceling the definition.
		Canceled 
		|| ↑ Canceled is the typename of this line, because there's no default typename here
		>SingledType2 || ← defines a default type again
		SingleType2 C
	TripledType StillAffected
		ThisIsNotCurrentLevel
		|| ↑ Singled definitions don't affect on cousin levels.
	>>>Triple2
	|| ↑ Tripled definition also changable and cancellable
	Triple2 D
			>Item
			BoostEnergy
			FocusSash
			>Terastal
			Normal
			Ground
			Water

这意味着宝可梦有2个'Item'和3个'Terastal'作为其子项。

转换如下。

fn third_to_pokemon(third: Third) -> Pokemon {
    match third {
        Third::Pokemon(
            name,
            poke_type,
            item,
            custom,
            move1,
            move2,
            move3,
            move4,
            param,
            children,
        ) => {
            let mut other_items: Vec<PokeItem> = vec![];
            let mut other_terastals: Vec<PokeType> = vec![];
            for v in children {
                match v {
                    Fourth::Item(item) => other_items.push(item),
                    Fourth::Terastal(t) => other_terastals.push(t),
                }
            }
            Pokemon {
                name,
                poke_type,
                item,
                custom,
                moves: vec![move1, move2, move3, move4],
                ability: param.ability,
                other_items,
                other_terastals,
            }
        }
    }
}

let mut vec = vec![] 不是优雅的,但功能强大。

其他材料

API 文档

由于Munyo是一种语言,API文档不足以使用它。其他材料可用。

示例

语言规范

动机

动机在这里解释 here

异步

此crate还包含了解析和接收解序列化数据的并发版本的函数,以及与运行时不相关的异步fn。

用法

将这些添加到您的 cargo.toml

[dependencies]
munyo = "0.5"
serde = { version = "1", features = ["derive"] }

许可

根据您的要求,许可为Apache License,版本2.0MIT许可

除非您明确声明,否则根据Apache-2.0许可证定义,您有意提交以包含在作品中的任何贡献,将双许可如上所述,没有任何附加条款或条件。

依赖项

~7–16MB
~242K SLoC