The .gx grammar
Every construct that the .gx parser accepts.
This page lists every construct that the .gx parser accepts. Nothing
else parses. A construct that is not here is a parse error, GX1000.
File
A file is one component. The shape is:
package clause
import lines (optional)
props block (optional)
signals block (optional)
bodyThe component name is the file base name. It must be an exported Go
identifier. A file named card.gx gives GX1001. The package clause names the
Go package, for example package card.
An import is a Go import line or block: import "path" or import ( ... ).
Blocks
props and signals blocks hold Go struct field syntax:
props {
// Title is the heading text.
Title string
Variant Variant = Default
Attrs gx.Attrs = nil
}- A field is
Name Type. A field with= expris optional. - A default can span lines inside brackets or braces.
- A
//comment on the lines above a field is the description of the field. The generated props struct carries it as a Go doc comment. - A block holds no other comment. A comment after a field on its line, a
comment with no field below it and a
/* ... */comment giveGX1000. - Inside markup,
pis the props value. $Namereads a signal. It is valid only in client expressions.
Body nodes
The body holds these nodes, in any order:
- element:
<div>,<Button>,<ui.Card>,<wa-button>,<:slot>. - text: literal HTML text, including entities such as
&. - expression:
{expr}, a Go expression or a client expression. - control:
if,forandswitchblocks. - statement:
name := expron its own line. - comment:
{/* ... */}is stripped from the render;<!-- ... -->renders. - raw text: the body of
scriptandstyleis text, not markup.
Elements
- A lowercase tag is an HTML element.
- An uppercase tag is a component in the same package.
- A dotted tag is a component from an imported package.
- A tag with a hyphen is a custom element.
- A
:tag is a slot, such as<:header>or<:row let={it}>. - A void element has no children and no closing tag:
br,hr,img,input,meta,linkand the other HTML void elements. - A self-closing tag ends in
/>.
Attributes
An attribute is one of:
- boolean:
disabled,checked,open - string:
class="flex"orclass='flex', and unquotedclass=flex - expression:
href={url},class:shadow-lg={p.Variant == Raised} - spread:
{...p.Attrs}on an HTML element - fragment:
#row(it Item)and#name, which set a component-scoped id and generate a typed fragment function
A directive name contains a colon: class:name, bind:value, on:click,
attr:aria-expanded, show, text.
Control flow
A control line starts at a line start and ends with {:
if p.Header != nil {
<header>{p.Header}</header>
} else if p.Title != "" {
...
} else {
...
}
for _, it := range p.Items {
<li>{it}</li>
}
switch p.Variant {
case Raised:
<b>raised</b>
default:
<b>flat</b>
}Every Go for form is accepted: three-clause, range over a slice, map,
iterator or integer, and a bare for. A case holds one or more expressions
before :. Text that starts with if, for or switch but does not end
with { stays text.
A statement line is name := expr, or a, b := expr. The body of a block
ends at a } that is the first non-space character on its line.
Expressions
{expr} in text or an attribute value is a Go expression, type-checked
against real Go types. Typical expressions are p.Title, len(p.Items) and
$Qty > 0. Strings and comments inside an expression can contain braces.
Whitespace, comments and formatting
gx fmt has one canonical form:
- two-space indentation
- one attribute line, unless the source used one attribute per line
- one blank line between top-level sections
- Go expressions have single spaces between tokens
- a field description is
// texton the lines directly above its field; a comment with no text is removed - a whitespace-only gap between nodes stays a single space when the source gap had no newline, and becomes a newline plus indentation otherwise
gx fmt is idempotent and never changes rendered output.