314 lines
12 KiB
Go
314 lines
12 KiB
Go
package lang
|
|
|
|
import (
|
|
"github.com/alecthomas/participle/v2"
|
|
)
|
|
|
|
// AST Root AST node containing all definitions
|
|
type AST struct {
|
|
Definitions []Definition `parser:"@@*"`
|
|
}
|
|
|
|
// Definition Union type for top-level definitions
|
|
type Definition struct {
|
|
Server *Server `parser:"@@"`
|
|
Entity *Entity `parser:"| @@"`
|
|
Endpoint *Endpoint `parser:"| @@"`
|
|
Page *Page `parser:"| @@"`
|
|
}
|
|
|
|
// ConfigValue Flexible value that can be literal or environment variable
|
|
type ConfigValue struct {
|
|
Literal *string `parser:"@String"`
|
|
EnvVar *EnvVar `parser:"| @@"`
|
|
}
|
|
|
|
// EnvVar Environment variable configuration
|
|
type EnvVar struct {
|
|
Name string `parser:"'env' @String"`
|
|
Default *string `parser:"('default' @String)?"`
|
|
Required bool `parser:"@'required'?"`
|
|
}
|
|
|
|
// Server Clean server syntax
|
|
type Server struct {
|
|
Name string `parser:"'server' @Ident"`
|
|
Settings []ServerSetting `parser:"('{' @@* '}')?"` // Block-delimited settings for consistency
|
|
}
|
|
|
|
type ServerSetting struct {
|
|
Host *ConfigValue `parser:"('host' @@)"`
|
|
Port *IntValue `parser:"| ('port' @@)"`
|
|
DatabaseURL *ConfigValue `parser:"| ('database_url' @@)"`
|
|
APIKey *ConfigValue `parser:"| ('api_key' @@)"`
|
|
SSLCert *ConfigValue `parser:"| ('ssl_cert' @@)"`
|
|
SSLKey *ConfigValue `parser:"| ('ssl_key' @@)"`
|
|
}
|
|
|
|
// IntValue Similar to ConfigValue but for integers
|
|
type IntValue struct {
|
|
Literal *int `parser:"@Int"`
|
|
EnvVar *EnvVar `parser:"| @@"`
|
|
}
|
|
|
|
// Entity Clean entity syntax with better readability
|
|
type Entity struct {
|
|
Name string `parser:"'entity' @Ident"`
|
|
Description *string `parser:"('desc' @String)?"`
|
|
Fields []Field `parser:"('{' @@* '}')?"` // Block-delimited fields for consistency
|
|
}
|
|
|
|
// Field Detailed field syntax
|
|
type Field struct {
|
|
Name string `parser:"@Ident ':'"`
|
|
Type string `parser:"@Ident"`
|
|
Required bool `parser:"@'required'?"`
|
|
Unique bool `parser:"@'unique'?"`
|
|
Index bool `parser:"@'indexed'?"`
|
|
Default *string `parser:"('default' @String)?"`
|
|
Validations []Validation `parser:"@@*"`
|
|
Relationship *Relationship `parser:"@@?"`
|
|
Endpoints []string `parser:"('endpoints' '[' @Ident (',' @Ident)* ']')?"` // with transforms this might not be needed
|
|
Transform []Transform `parser:"@@*"`
|
|
}
|
|
|
|
// Transform Field transformation specification
|
|
type Transform struct {
|
|
Type string `parser:"'transform' @Ident"`
|
|
Column *string `parser:"('to' @Ident)?"`
|
|
Direction *string `parser:"('on' @('input' | 'output' | 'both'))?"`
|
|
}
|
|
|
|
// Validation Simple validation syntax
|
|
type Validation struct {
|
|
Type string `parser:"'validate' @Ident"`
|
|
Value *string `parser:"@String?"`
|
|
}
|
|
|
|
// Relationship Clear relationship syntax
|
|
type Relationship struct {
|
|
Type string `parser:"'relates' 'to' @Ident"`
|
|
Cardinality string `parser:"'as' @('one' | 'many')"`
|
|
ForeignKey *string `parser:"('via' @String)?"`
|
|
Through *string `parser:"('through' @String)?"`
|
|
}
|
|
|
|
// Endpoint definitions with clean, readable syntax
|
|
type Endpoint struct {
|
|
Method string `parser:"'endpoint' @('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH')"`
|
|
Path string `parser:"@String"`
|
|
Entity *string `parser:"('for' @Ident)?"`
|
|
Description *string `parser:"('desc' @String)?"`
|
|
Auth bool `parser:"@'auth'?"`
|
|
Params []EndpointParam `parser:"('{' @@*"` // Block-delimited parameters
|
|
Response *ResponseSpec `parser:"@@?"`
|
|
CustomLogic *string `parser:"('custom' @String)? '}')?"` // Close block after all content
|
|
}
|
|
|
|
// EndpointParam Clean parameter syntax
|
|
type EndpointParam struct {
|
|
Name string `parser:"'param' @Ident ':'"`
|
|
Type string `parser:"@Ident"`
|
|
Required bool `parser:"@'required'?"`
|
|
Source string `parser:"'from' @('path' | 'query' | 'body')"`
|
|
}
|
|
|
|
// ResponseSpec Response specification
|
|
type ResponseSpec struct {
|
|
Type string `parser:"'returns' @Ident"`
|
|
Format *string `parser:"('as' @String)?"`
|
|
Fields []string `parser:"('fields' '[' @Ident (',' @Ident)* ']')?"`
|
|
}
|
|
|
|
// Page Enhanced Page definitions with unified section model
|
|
type Page struct {
|
|
Name string `parser:"'page' @Ident"`
|
|
Path string `parser:"'at' @String"`
|
|
Layout string `parser:"'layout' @Ident"`
|
|
Title *string `parser:"('title' @String)?"`
|
|
Description *string `parser:"('desc' @String)?"`
|
|
Auth bool `parser:"@'auth'?"`
|
|
Meta []MetaTag `parser:"('{' @@*"` // Block-delimited content
|
|
Sections []Section `parser:"@@*"` // Unified sections replace containers/tabs/panels/modals
|
|
Components []Component `parser:"@@* '}')?"` // Direct components within the block
|
|
}
|
|
|
|
// MetaTag Meta tags for SEO
|
|
type MetaTag struct {
|
|
Name string `parser:"'meta' @Ident"`
|
|
Content string `parser:"@String"`
|
|
}
|
|
|
|
// Section Unified Section type that replaces Container, Tab, Panel, Modal, MasterDetail
|
|
type Section struct {
|
|
Name string `parser:"'section' @Ident"`
|
|
Type *string `parser:"('type' @('container' | 'tab' | 'panel' | 'modal' | 'master' | 'detail'))?"`
|
|
Class *string `parser:"('class' @String)?"`
|
|
Label *string `parser:"('label' @String)?"` // for tabs
|
|
Active bool `parser:"@'active'?"` // for tabs
|
|
Trigger *string `parser:"('trigger' @String)?"` // for panels/modals/detail
|
|
Position *string `parser:"('position' @String)?"` // for panels
|
|
Entity *string `parser:"('for' @Ident)?"` // for panels
|
|
Elements []SectionElement `parser:"('{' @@* '}')?"` // Block-delimited elements for unambiguous nesting
|
|
}
|
|
|
|
// SectionElement New unified element type for sections
|
|
type SectionElement struct {
|
|
Attribute *SectionAttribute `parser:"@@"`
|
|
Component *Component `parser:"| @@"`
|
|
Section *Section `parser:"| @@"`
|
|
When *WhenCondition `parser:"| @@"`
|
|
}
|
|
|
|
// SectionAttribute Flexible section attributes (replaces complex config types)
|
|
type SectionAttribute struct {
|
|
DataSource *string `parser:"('data' 'from' @String)"`
|
|
Style *string `parser:"| ('style' @String)"`
|
|
Classes *string `parser:"| ('classes' @String)"`
|
|
Size *int `parser:"| ('size' @Int)"` // for pagination, etc.
|
|
Theme *string `parser:"| ('theme' @String)"`
|
|
}
|
|
|
|
// Component Simplified Component with unified attributes - reordered for better parsing
|
|
type Component struct {
|
|
Type string `parser:"'component' @Ident"`
|
|
Entity *string `parser:"('for' @Ident)?"`
|
|
Elements []ComponentElement `parser:"('{' @@* '}')?"` // Parse everything inside the block
|
|
}
|
|
|
|
// ComponentElement Enhanced ComponentElement with recursive section support - now includes attributes
|
|
type ComponentElement struct {
|
|
Attribute *ComponentAttr `parser:"@@"` // Component attributes can be inside the block
|
|
Field *ComponentField `parser:"| @@"`
|
|
Section *Section `parser:"| @@"` // Sections can be nested in components
|
|
Button *ComponentButton `parser:"| @@"`
|
|
When *WhenCondition `parser:"| @@"`
|
|
}
|
|
|
|
// ComponentAttr Simplified component attributes using key-value pattern - reordered for precedence
|
|
type ComponentAttr struct {
|
|
DataSource *string `parser:"('data' 'from' @String)"`
|
|
Fields []string `parser:"| ('fields' '[' @Ident (',' @Ident)* ']')"`
|
|
Actions []string `parser:"| ('actions' '[' @Ident (',' @Ident)* ']')"`
|
|
Style *string `parser:"| ('style' @String)"`
|
|
Classes *string `parser:"| ('classes' @String)"`
|
|
PageSize *int `parser:"| ('pagination' 'size' @Int)"`
|
|
Validate bool `parser:"| @'validate'"`
|
|
}
|
|
|
|
// ComponentField Enhanced component field with detailed configuration using flexible attributes
|
|
type ComponentField struct {
|
|
Name string `parser:"'field' @Ident"`
|
|
Type string `parser:"'type' @Ident"`
|
|
Attributes []ComponentFieldAttribute `parser:"@@* ('{' @@* '}')?"` // Support both inline and block attributes
|
|
}
|
|
|
|
// ComponentFieldAttribute Flexible field attribute system
|
|
type ComponentFieldAttribute struct {
|
|
Label *string `parser:"('label' @String)"`
|
|
Placeholder *string `parser:"| ('placeholder' @String)"`
|
|
Required bool `parser:"| @'required'"`
|
|
Sortable bool `parser:"| @'sortable'"`
|
|
Searchable bool `parser:"| @'searchable'"`
|
|
Thumbnail bool `parser:"| @'thumbnail'"`
|
|
Default *string `parser:"| ('default' @String)"`
|
|
Options []string `parser:"| ('options' '[' @String (',' @String)* ']')"`
|
|
Accept *string `parser:"| ('accept' @String)"`
|
|
Rows *int `parser:"| ('rows' @Int)"`
|
|
Format *string `parser:"| ('format' @String)"`
|
|
Size *string `parser:"| ('size' @String)"`
|
|
Display *string `parser:"| ('display' @String)"`
|
|
Value *string `parser:"| ('value' @String)"`
|
|
Source *string `parser:"| ('source' @String)"`
|
|
Relates *FieldRelation `parser:"| @@"`
|
|
Validation *ComponentValidation `parser:"| @@"`
|
|
}
|
|
|
|
// FieldRelation Field relationship for autocomplete and select fields
|
|
type FieldRelation struct {
|
|
Type string `parser:"'relates' 'to' @Ident"`
|
|
}
|
|
|
|
// ComponentValidation Component validation
|
|
type ComponentValidation struct {
|
|
Type string `parser:"'validate' @Ident"`
|
|
Value *string `parser:"@String?"`
|
|
}
|
|
|
|
// WhenCondition Enhanced WhenCondition with recursive support for both sections and components
|
|
type WhenCondition struct {
|
|
Field string `parser:"'when' @Ident"`
|
|
Operator string `parser:"@('equals' | 'not_equals' | 'contains')"`
|
|
Value string `parser:"@String"`
|
|
Fields []ComponentField `parser:"('{' @@*"`
|
|
Sections []Section `parser:"@@*"` // Can contain sections
|
|
Components []Component `parser:"@@*"` // Can contain components
|
|
Buttons []ComponentButton `parser:"@@* '}')?"` // Block-delimited for unambiguous nesting
|
|
}
|
|
|
|
// ComponentButton Simplified button with flexible attribute ordering
|
|
type ComponentButton struct {
|
|
Name string `parser:"'button' @Ident"`
|
|
Label string `parser:"'label' @String"`
|
|
Attributes []ComponentButtonAttr `parser:"@@*"`
|
|
}
|
|
|
|
// ComponentButtonAttr Flexible button attribute system - each attribute is a separate alternative
|
|
type ComponentButtonAttr struct {
|
|
Style *ComponentButtonStyle `parser:"@@"`
|
|
Icon *ComponentButtonIcon `parser:"| @@"`
|
|
Loading *ComponentButtonLoading `parser:"| @@"`
|
|
Disabled *ComponentButtonDisabled `parser:"| @@"`
|
|
Confirm *ComponentButtonConfirm `parser:"| @@"`
|
|
Target *ComponentButtonTarget `parser:"| @@"`
|
|
Position *ComponentButtonPosition `parser:"| @@"`
|
|
Via *ComponentButtonVia `parser:"| @@"`
|
|
}
|
|
|
|
// ComponentButtonStyle Individual button attribute types
|
|
type ComponentButtonStyle struct {
|
|
Value string `parser:"'style' @String"`
|
|
}
|
|
|
|
type ComponentButtonIcon struct {
|
|
Value string `parser:"'icon' @String"`
|
|
}
|
|
|
|
type ComponentButtonLoading struct {
|
|
Value string `parser:"'loading' @String"`
|
|
}
|
|
|
|
type ComponentButtonDisabled struct {
|
|
Value string `parser:"'disabled' 'when' @Ident"`
|
|
}
|
|
|
|
type ComponentButtonConfirm struct {
|
|
Value string `parser:"'confirm' @String"`
|
|
}
|
|
|
|
type ComponentButtonTarget struct {
|
|
Value string `parser:"'target' @Ident"`
|
|
}
|
|
|
|
type ComponentButtonPosition struct {
|
|
Value string `parser:"'position' @String"`
|
|
}
|
|
|
|
type ComponentButtonVia struct {
|
|
Value string `parser:"'via' @String"`
|
|
}
|
|
|
|
func ParseInput(input string) (AST, error) {
|
|
parser, err := participle.Build[AST](
|
|
participle.Unquote("String"),
|
|
)
|
|
if err != nil {
|
|
return AST{}, err
|
|
}
|
|
ast, err := parser.ParseString("", input)
|
|
if err != nil {
|
|
return AST{}, err
|
|
}
|
|
return *ast, nil
|
|
}
|