Scripting: Component Schema Language

(Zicklag) #1

Continuing the discussion from Scripting: What Do We Need to Get There:

I created a parser for a component schema language for use with the scripting solution. Here is an example schema file:

// Line comments are supported

// This defines the type that contains the root component data. It *must* be
// defined as `type Component = TypeName`. It is the only `type` statement
// allowed. The purpose is to distinguish which struct out of the ones in the
// schema is to be used as the component struct.
type Component = Player;

// This is the player object
struct Player {
    position: Position,
    // You can have type parameters:
    life: Option<u8>, // Struct fields can have trailing commas
    //          ^^^^
}

struct Position {
    x: /* You can stick inline,
        * multiline comments anywhere */ f32,
    y: f32 // Trailing commas aren't required
}

// Structs can have type parameters, too.
struct HashMap<T, U> {
    keys: Vec<T>,
    values: Vec<U>,
}

enum PlayerState {
    Walking,
    Running,
    Standing,
    Falling,
    Jumping,
}

// TODO:
// - References?

It is pretty much just a subset of Rust.

The parser currently just loads the schema file into this AST:

AST dump for example above
[
    ComponentName(
        "Player",
    ),
    Struct(
        Struct {
            struct_type: Type {
                type_name: "Player",
                type_parameters: None,
            },
            fields: [
                Field {
                    name: "position",
                    field_kind: Type(
                        Type {
                            type_name: "Position",
                            type_parameters: None,
                        },
                    ),
                },
                Field {
                    name: "state",
                    field_kind: Type(
                        Type {
                            type_name: "PlayerState",
                            type_parameters: None,
                        },
                    ),
                },
                Field {
                    name: "life",
                    field_kind: Type(
                        Type {
                            type_name: "Option",
                            type_parameters: Some(
                                [
                                    Primitive(
                                        U8,
                                    ),
                                ],
                            ),
                        },
                    ),
                },
            ],
        },
    ),
    Struct(
        Struct {
            struct_type: Type {
                type_name: "Position",
                type_parameters: None,
            },
            fields: [
                Field {
                    name: "x",
                    field_kind: Primitive(
                        F32,
                    ),
                },
                Field {
                    name: "y",
                    field_kind: Primitive(
                        F32,
                    ),
                },
            ],
        },
    ),
    Struct(
        Struct {
            struct_type: Type {
                type_name: "HashMap",
                type_parameters: Some(
                    [
                        Type(
                            Type {
                                type_name: "T",
                                type_parameters: None,
                            },
                        ),
                        Type(
                            Type {
                                type_name: "U",
                                type_parameters: None,
                            },
                        ),
                    ],
                ),
            },
            fields: [
                Field {
                    name: "keys",
                    field_kind: Type(
                        Type {
                            type_name: "Vec",
                            type_parameters: Some(
                                [
                                    Type(
                                        Type {
                                            type_name: "T",
                                            type_parameters: None,
                                        },
                                    ),
                                ],
                            ),
                        },
                    ),
                },
                Field {
                    name: "values",
                    field_kind: Type(
                        Type {
                            type_name: "Vec",
                            type_parameters: Some(
                                [
                                    Type(
                                        Type {
                                            type_name: "U",
                                            type_parameters: None,
                                        },
                                    ),
                                ],
                            ),
                        },
                    ),
                },
            ],
        },
    ),
    Enum(
        Enum {
            name: "PlayerState",
            variants: [
                "Walking",
                "Running",
                "Standing",
                "Falling",
                "Jumping",
            ],
        },
    ),
]

I’m open to feedback, it should be easy to work whatever else we might need into the parser.

1 Like
(Théo Degioanni) #2

I feel like type Component = Player would carry the wrong intention: are you creating a component named Component?

Instead, what about using component instead of struct for the component struct? That way we can easily differentiate multiple components, and even store multiple in one file.

Also, I don’t think references are necessary. At least we should try without them first, because it adds a lot more complexity. Great job so far!

2 Likes
(Zicklag) #3

Yeah, that was the thing I was the least sure that I liked.

That is a great idea! Gets the idea across without forcing you to name your component struct “Component”.

Good, I didn’t want to mess with that yet anyway.

Thanks! :slightly_smiling_face:


Edit: There, now components are just structs defined with component instead of struct and it sets the is_component field in the AST to true if it is a component.

1 Like
#4

When I was making DSL and needed to mark a type I chose to use attribute instead of another keyword.
Like this

#[Component]
struct Player { ... }

Alternatively you may add keyword instead of replacing:

component struct Player { ... }

Or short form

comp struct Player { ... }

This allows you to signal that component is still a struct, just marked.

2 Likes
(Zicklag) #7

I think that makes sense. Using an attribute would make it fully aligned with Rust syntax highlighters, too. I’ll do that.

Edit: There we go:

2 Likes
(Théo Degioanni) #8

Correct me if I’m wrong, but I believe the convention in Rust is to have attributes start with a lowercase letter. Maybe it would be nice to follow that convention as well.

1 Like