git commit -m "feat: initial release of Strata framework v0.1.0
- Static compiler with STRC pattern (Static Template Resolution with
Compartmentalized Layers)
- Template syntax: { } interpolation, { s-for }, { s-if/s-elif/s-else
}
- File types: .strata, .compiler.sts, .service.sts, .api.sts, .sts,
.scss
- CLI tools: strata dev, strata build, strata g (generators)
- create-strata scaffolding CLI with Pokemon API example
- Dev server with WebSocket HMR (Hot Module Replacement)
- Documentation: README, ARCHITECTURE, CHANGELOG, CONTRIBUTING,
LICENSE
This commit is contained in:
304
compiler/internal/ast/nodes.go
Normal file
304
compiler/internal/ast/nodes.go
Normal file
@@ -0,0 +1,304 @@
|
||||
package ast
|
||||
|
||||
// Node is the base interface for all AST nodes
|
||||
type Node interface {
|
||||
NodeType() string
|
||||
}
|
||||
|
||||
// StrataFile represents a parsed .strata file
|
||||
type StrataFile struct {
|
||||
Template *TemplateNode
|
||||
Script *ScriptNode
|
||||
Style *StyleNode
|
||||
}
|
||||
|
||||
func (s *StrataFile) NodeType() string { return "StrataFile" }
|
||||
|
||||
// TemplateNode represents the <template> block
|
||||
type TemplateNode struct {
|
||||
Children []Node
|
||||
}
|
||||
|
||||
func (t *TemplateNode) NodeType() string { return "Template" }
|
||||
|
||||
// ScriptNode represents the <script> block
|
||||
type ScriptNode struct {
|
||||
Content string
|
||||
Lang string // "ts" or "js"
|
||||
}
|
||||
|
||||
func (s *ScriptNode) NodeType() string { return "Script" }
|
||||
|
||||
// StyleNode represents the <style> block
|
||||
type StyleNode struct {
|
||||
Content string
|
||||
Lang string // "css", "scss", "sass", "less"
|
||||
Scoped bool
|
||||
}
|
||||
|
||||
func (s *StyleNode) NodeType() string { return "Style" }
|
||||
|
||||
// ElementNode represents an HTML element
|
||||
type ElementNode struct {
|
||||
Tag string
|
||||
Attributes map[string]string
|
||||
Directives []Directive
|
||||
Children []Node
|
||||
IsComponent bool
|
||||
}
|
||||
|
||||
func (e *ElementNode) NodeType() string { return "Element" }
|
||||
|
||||
// TextNode represents text content
|
||||
type TextNode struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
func (t *TextNode) NodeType() string { return "Text" }
|
||||
|
||||
// InterpolationNode represents { expression } variable binding
|
||||
type InterpolationNode struct {
|
||||
Expression string
|
||||
}
|
||||
|
||||
func (i *InterpolationNode) NodeType() string { return "Interpolation" }
|
||||
|
||||
// ForBlockNode represents { s-for item in items } ... { /s-for }
|
||||
type ForBlockNode struct {
|
||||
Item string // Loop variable name (e.g., "item")
|
||||
Index string // Optional index variable (e.g., "i" in "item, i")
|
||||
Iterable string // Expression to iterate (e.g., "items")
|
||||
Children []Node // Content inside the for block
|
||||
}
|
||||
|
||||
func (f *ForBlockNode) NodeType() string { return "ForBlock" }
|
||||
|
||||
// IfBlockNode represents { s-if } ... { s-elif } ... { s-else } ... { /s-if }
|
||||
type IfBlockNode struct {
|
||||
Condition string // The if condition
|
||||
Children []Node // Content when condition is true
|
||||
ElseIf []*ElseIfBranch
|
||||
Else []Node // Content for else branch
|
||||
}
|
||||
|
||||
func (i *IfBlockNode) NodeType() string { return "IfBlock" }
|
||||
|
||||
// ElseIfBranch represents an elif branch in conditional
|
||||
type ElseIfBranch struct {
|
||||
Condition string
|
||||
Children []Node
|
||||
}
|
||||
|
||||
// ImportNode represents { s-imp "@alias/file" }
|
||||
type ImportNode struct {
|
||||
Path string // Import path (e.g., "@components/Button")
|
||||
Alias string // Optional alias for the import
|
||||
}
|
||||
|
||||
func (i *ImportNode) NodeType() string { return "Import" }
|
||||
|
||||
// DirectiveType represents the type of directive
|
||||
type DirectiveType int
|
||||
|
||||
const (
|
||||
DirectiveConditional DirectiveType = iota // s-if, s-else-if, s-else
|
||||
DirectiveLoop // s-for
|
||||
DirectiveModel // s-model
|
||||
DirectiveEvent // s-on:event, @event
|
||||
DirectiveBind // :attr
|
||||
DirectiveClient // s-client:load, s-client:visible, etc.
|
||||
DirectiveFetch // s-fetch
|
||||
DirectiveRef // s-ref
|
||||
DirectiveHTML // s-html
|
||||
DirectiveStatic // s-static
|
||||
)
|
||||
|
||||
// Directive represents a Strata directive
|
||||
type Directive struct {
|
||||
Type DirectiveType
|
||||
Name string // Original attribute name
|
||||
Value string // Attribute value
|
||||
Arg string // Argument (e.g., "click" in @click)
|
||||
Modifiers []string // Modifiers (e.g., "prevent" in @click.prevent)
|
||||
}
|
||||
|
||||
// ComponentNode represents a component usage
|
||||
type ComponentNode struct {
|
||||
Name string
|
||||
Props map[string]string
|
||||
Directives []Directive
|
||||
Slots map[string][]Node
|
||||
}
|
||||
|
||||
func (c *ComponentNode) NodeType() string { return "Component" }
|
||||
|
||||
// SlotNode represents a <slot> element
|
||||
type SlotNode struct {
|
||||
Name string // default or named
|
||||
Fallback []Node
|
||||
}
|
||||
|
||||
func (s *SlotNode) NodeType() string { return "Slot" }
|
||||
|
||||
// ImportDeclaration represents an import statement
|
||||
type ImportDeclaration struct {
|
||||
Default string // Default import name
|
||||
Named []string // Named imports
|
||||
Source string // Import source path
|
||||
}
|
||||
|
||||
// ComponentDefinition represents a component's parsed definition
|
||||
type ComponentDefinition struct {
|
||||
Name string
|
||||
Props []PropDefinition
|
||||
Imports []ImportDeclaration
|
||||
Setup string // The setup function body
|
||||
}
|
||||
|
||||
// PropDefinition represents a component prop
|
||||
type PropDefinition struct {
|
||||
Name string
|
||||
Type string
|
||||
Required bool
|
||||
Default string
|
||||
}
|
||||
|
||||
// StoreDefinition represents a parsed store
|
||||
type StoreDefinition struct {
|
||||
Name string
|
||||
State map[string]string // Field name -> type
|
||||
Actions []ActionDefinition
|
||||
Encrypt []string // Fields to encrypt
|
||||
Persist []string // Fields to persist
|
||||
Shared []string // Fields to share across tabs
|
||||
}
|
||||
|
||||
// ActionDefinition represents a store action
|
||||
type ActionDefinition struct {
|
||||
Name string
|
||||
Params []string
|
||||
Body string
|
||||
Async bool
|
||||
}
|
||||
|
||||
// ToHTML converts a TemplateNode to HTML string
|
||||
func (t *TemplateNode) ToHTML() string {
|
||||
var result string
|
||||
for _, child := range t.Children {
|
||||
result += nodeToHTML(child)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// nodeToHTML converts any AST node to HTML
|
||||
func nodeToHTML(node Node) string {
|
||||
if node == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch n := node.(type) {
|
||||
case *ElementNode:
|
||||
return elementToHTML(n)
|
||||
case *TextNode:
|
||||
return n.Content
|
||||
case *InterpolationNode:
|
||||
// For dev mode, output a placeholder that will be filled by JS
|
||||
return `<span data-strata-bind="` + n.Expression + `"></span>`
|
||||
case *ForBlockNode:
|
||||
return forBlockToHTML(n)
|
||||
case *IfBlockNode:
|
||||
return ifBlockToHTML(n)
|
||||
case *ImportNode:
|
||||
// Imports are handled at compile time, not rendered
|
||||
return ""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// forBlockToHTML converts a ForBlockNode to HTML with data attributes
|
||||
func forBlockToHTML(f *ForBlockNode) string {
|
||||
html := `<template data-strata-for="` + f.Item
|
||||
if f.Index != "" {
|
||||
html += `, ` + f.Index
|
||||
}
|
||||
html += ` in ` + f.Iterable + `">`
|
||||
for _, child := range f.Children {
|
||||
html += nodeToHTML(child)
|
||||
}
|
||||
html += `</template>`
|
||||
return html
|
||||
}
|
||||
|
||||
// ifBlockToHTML converts an IfBlockNode to HTML with data attributes
|
||||
func ifBlockToHTML(i *IfBlockNode) string {
|
||||
html := `<template data-strata-if="` + i.Condition + `">`
|
||||
for _, child := range i.Children {
|
||||
html += nodeToHTML(child)
|
||||
}
|
||||
html += `</template>`
|
||||
|
||||
// Handle elif branches
|
||||
for _, elif := range i.ElseIf {
|
||||
html += `<template data-strata-elif="` + elif.Condition + `">`
|
||||
for _, child := range elif.Children {
|
||||
html += nodeToHTML(child)
|
||||
}
|
||||
html += `</template>`
|
||||
}
|
||||
|
||||
// Handle else branch
|
||||
if len(i.Else) > 0 {
|
||||
html += `<template data-strata-else>`
|
||||
for _, child := range i.Else {
|
||||
html += nodeToHTML(child)
|
||||
}
|
||||
html += `</template>`
|
||||
}
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
// elementToHTML converts an ElementNode to HTML
|
||||
func elementToHTML(el *ElementNode) string {
|
||||
if el == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
html := "<" + el.Tag
|
||||
|
||||
// Add attributes
|
||||
for name, value := range el.Attributes {
|
||||
if value != "" {
|
||||
html += ` ` + name + `="` + value + `"`
|
||||
} else {
|
||||
html += ` ` + name
|
||||
}
|
||||
}
|
||||
|
||||
// Add directive attributes for dev mode debugging
|
||||
for _, dir := range el.Directives {
|
||||
html += ` data-strata-` + dir.Name + `="` + dir.Value + `"`
|
||||
}
|
||||
|
||||
html += ">"
|
||||
|
||||
// Add children
|
||||
for _, child := range el.Children {
|
||||
html += nodeToHTML(child)
|
||||
}
|
||||
|
||||
// Close tag (unless void element)
|
||||
voidElements := map[string]bool{
|
||||
"area": true, "base": true, "br": true, "col": true,
|
||||
"embed": true, "hr": true, "img": true, "input": true,
|
||||
"link": true, "meta": true, "param": true, "source": true,
|
||||
"track": true, "wbr": true,
|
||||
}
|
||||
|
||||
if !voidElements[el.Tag] {
|
||||
html += "</" + el.Tag + ">"
|
||||
}
|
||||
|
||||
return html
|
||||
}
|
||||
Reference in New Issue
Block a user