blob: dc7d7f35b065e20861600e2d3325ea59af08c3b6 [file] [log] [blame]
package ast
// ast is the abstract syntax tree for the query term mapper.
import (
"encoding/json"
)
// NodeType represents the type of a node in the AST
type NodeType string
// RelationType represents the type of relation between nodes
type RelationType string
// MatchType represents the type of match operation
type MatchType string
const (
TokenNode NodeType = "token"
TermGroupNode NodeType = "termGroup"
TermNode NodeType = "term"
RewriteNode NodeType = "rewrite"
AndRelation RelationType = "and"
OrRelation RelationType = "or"
MatchEqual MatchType = "eq"
MatchNotEqual MatchType = "ne"
)
// Node represents a node in the AST
type Node interface {
Type() NodeType
}
// Rewrite represents a koral:rewrite
type Rewrite struct {
Editor string `json:"editor,omitempty"`
Operation string `json:"operation,omitempty"`
Scope string `json:"scope,omitempty"`
Src string `json:"src,omitempty"`
Comment string `json:"_comment,omitempty"`
Original any `json:"original,omitempty"`
}
// UnmarshalJSON implements custom JSON unmarshaling for backward compatibility
func (r *Rewrite) UnmarshalJSON(data []byte) error {
// Create a temporary struct to hold all possible fields
var temp struct {
Type string `json:"@type,omitempty"`
Editor string `json:"editor,omitempty"`
Source string `json:"source,omitempty"` // legacy field
Operation string `json:"operation,omitempty"` // legacy field
Scope string `json:"scope,omitempty"`
Src string `json:"src,omitempty"`
Origin string `json:"origin,omitempty"` // legacy field
Original any `json:"original,omitempty"`
Comment string `json:"_comment,omitempty"`
}
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
// Apply precedence for editor field: editor >> source
if temp.Editor != "" {
r.Editor = temp.Editor
} else if temp.Source != "" {
r.Editor = temp.Source
}
// Apply precedence for original/src/origin: original >> src >> origin
if temp.Original != nil {
r.Original = temp.Original
} else if temp.Src != "" {
r.Src = temp.Src
} else if temp.Origin != "" {
r.Src = temp.Origin
}
// Copy other fields
r.Operation = temp.Operation
r.Scope = temp.Scope
r.Comment = temp.Comment
return nil
}
func (r *Rewrite) Type() NodeType {
return RewriteNode
}
// MarshalJSON implements custom JSON marshaling to ensure clean output
func (r *Rewrite) MarshalJSON() ([]byte, error) {
// Create a map with only the modern field names
result := make(map[string]any)
// Always include @type if this is a rewrite
result["@type"] = "koral:rewrite"
if r.Editor != "" {
result["editor"] = r.Editor
}
if r.Operation != "" {
result["operation"] = r.Operation
}
if r.Scope != "" {
result["scope"] = r.Scope
}
if r.Src != "" {
result["src"] = r.Src
}
if r.Comment != "" {
result["_comment"] = r.Comment
}
if r.Original != nil {
result["original"] = r.Original
}
return json.Marshal(result)
}
// Token represents a koral:token
type Token struct {
Wrap Node `json:"wrap"`
Rewrites []Rewrite `json:"rewrites,omitempty"`
}
func (t *Token) Type() NodeType {
return TokenNode
}
// TermGroup represents a koral:termGroup
type TermGroup struct {
Operands []Node `json:"operands"`
Relation RelationType `json:"relation"`
Rewrites []Rewrite `json:"rewrites,omitempty"`
}
func (tg *TermGroup) Type() NodeType {
return TermGroupNode
}
// Term represents a koral:term
type Term struct {
Foundry string `json:"foundry"`
Key string `json:"key"`
Layer string `json:"layer"`
Match MatchType `json:"match"`
Value string `json:"value,omitempty"`
Rewrites []Rewrite `json:"rewrites,omitempty"`
}
func (t *Term) Type() NodeType {
return TermNode
}
// Pattern represents a pattern to match in the AST
type Pattern struct {
Root Node
}
// Replacement represents a replacement pattern
type Replacement struct {
Root Node
}
// CatchallNode represents any node type not explicitly handled
type CatchallNode struct {
NodeType string // The original @type value
RawContent json.RawMessage // The original JSON content
Wrap Node // Optional wrapped node
Operands []Node // Optional operands
}
func (c *CatchallNode) Type() NodeType {
return NodeType(c.NodeType)
}