3 releases

new 0.1.2 Nov 14, 2024
0.1.1 Nov 14, 2024
0.1.0 Nov 14, 2024

#616 in Template engine

37 downloads per month
Used in markerml_cli

MIT license

94KB
2K SLoC

MarkerML

General

This library is a top level crate for MarkerML, which provides function that attempts to convert given MarkerML code to HTML. MarkerML stands for Marker Markup Language. It's a simple language for formatting and layouting text similar to HTML.

Syntax is described in details on docs.rs.

Example

box {
    header[1](Hello, world!)
    paragraph(Some text)
    box[horizontal] {
        @(Wow)
    }
    box[vertical, x_align = "center"] {
        @(Wow)
    }
}

lib.rs:

General

This library provides parse function that attempts to convert given MarkerML code to HTML. MarkerML stands for Marker Markup Language. It's a simple language for formatting and layouting text similar to HTML.

Note that custom components are not yet implemented in the backend library.

Syntax

Here is an overview of the syntax.

Component

The main building block is the component

// Turns into an empty div
box

// Component might also specify properties
box[x_align = "center"]

// And also have children
box[x_align = "center"] {
    box {

    }
}

There are also text components, which can't have children, but contain text instead.

// Example of a text component
header[1](This is a header)
box {
    @(This is some text)
    paragraph(This is a paragraph)
}

Properties

More about properties. Properties contain optional default property that doesn't need to be named, then list of either flag or key value properties.

// Example of a default property
#["//google.com"](Link to Google)

// Example of flag property. It is simply an identifier
box[horizontal] {}

// Example of named property
// Identifier is followed by `'='` sign and then by value
box[x_align = "center", y_align = "center"] {}

// Flag and named properties can be combined
box[x_align = "center", vertical] {}

// Example of variable interpolation
box[x_align = ${align}] {}

Identifiers

Identifier must begin with ascii alphabetic character or underscore, followed by sequence ascii alphanumeric characters or underscores. Identifiers are used for component and property names, although there are also several built-in component names which are not identifiers, namely: @ (text) and # (link).

Types

There are several types in this language:

  • int - integers like 0, 42, or -252

  • bool - true or false

  • string - "Text inside quotes", might also have interpolated variables like: "Hello, ${user_name}"

  • slot and slot[] for component composition

Component definitions

Custom component specify list of properties, their types and default values. Component might either have text property or children. It might also have single default property.

component custom_component[
    default prop: string,
    smth: int,
    abc: string = "Default value"
] {
    box {
        header[level = ${smth}](Header text)
        @(${abc} ${prop})
    }
}

Modules

Module is a top-level entity that is a sequence of components and component definitions. That's what was used in previous examples.

Comments

These examples make heavy use of the comments, which are lines that begin with // and then ignored.

Whitespaces

Most of the syntax elements can also be separated by whitespaces, which are a sequence of space ' ', tab '\t' or newline '\n' characters.

Built-in components

Box

Name: box
Properties:

  • vertical
  • horizontal
  • x_align: string = "start" | "center" | "end". Default: "start"
  • y_align: string = "start" | "center" | "end". Default: "start"

Text

Name: @
Properties:

  • text content

Image

Name: image
Properties:

  • default url: string

Name: #
Properties:

  • default url: string
  • text name

List

Name: list
Properties:

  • unordered
  • ordered
  • children: slot[]

Header

Name: header
Properties:

  • default level: integer = 1

Paragraph

Name: paragraph
Properties:

  • text content

Grammar

WHITESPACE = _{ (" " | "\t" | NEWLINE)+ }

COMMENT = _{ "//" ~ (!NEWLINE ~ ANY)* ~ NEWLINE }

integer = @{ "-"? ~ ASCII_DIGIT+ }

bool = @{ "true" | "false" }

identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }

literal_newline = @{ NEWLINE ~ (" " | "\t")* }

string_literal_segment = @{ (!("$" | "\"" | NEWLINE) ~ ANY)+ }

text_literal_segment = @{ (!("$" | ")" | NEWLINE) ~ ANY)+ }

variable_interpolation = { "${" ~ identifier ~ "}" }

string_segment = ${ literal_newline | variable_interpolation | string_literal_segment }

text_segment = ${ literal_newline | variable_interpolation | text_literal_segment }

string = @{ "\"" ~ string_segment* ~ "\"" }

text = @{ "(" ~ text_segment* ~ ")" }

value = { variable_interpolation | bool | string | integer }

component_name = { "@" | "#" | identifier }

default_property = { value }

named_property = { identifier ~ "=" ~ value }

flag_property = { identifier }

property = { named_property | flag_property }

properties_list = _{ property ~ ("," ~ property)* }

properties = { "[" ~ (properties_list | (default_property ~ ("," ~ properties_list)?))?  ~ ","? ~ "]" }

children = { "{" ~ component* ~ "}" }

component = { component_name ~ properties? ~ children? ~ text? }

ty = @{ "string" | "int" | "bool" | "slot[]" | "slot" }

default_property_definition = { "default" ~ identifier ~ ":" ~ ty }

text_property_definition = { "text" ~ identifier }

named_property_definition = { identifier ~ ":" ~ ty ~ ("=" ~ value)? }

property_definition = { default_property_definition | text_property_definition | named_property_definition }

properties_definition_list = _{ property_definition ~ ("," ~ property_definition)* }

properties_definition = { "[" ~ properties_definition_list? ~ "]" }

component_definition = { "component" ~ identifier ~ properties_definition? ~ children? }

module_item = _{ component_definition | component }

module = { SOI ~ module_item* ~ EOI}

Dependencies

~6–12MB
~154K SLoC