diff --git a/example_test.go b/example_test.go index 5aea06d1..3af537ee 100644 --- a/example_test.go +++ b/example_test.go @@ -11,6 +11,7 @@ import ( kcl "kcl-lang.io/kcl-go" "kcl-lang.io/kcl-go/pkg/native" + "kcl-lang.io/kcl-go/pkg/parser" "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" ) @@ -213,6 +214,14 @@ age = option("age") // name: kcl } +func ExampleParseFile() { + result, err := parser.ParseFile("testdata/main.k", nil) + if err != nil { + log.Fatal(err) + } + fmt.Println(result) +} + func ExampleParseProgram() { result, err := kcl.ParseProgram(&kcl.ParseProgramArgs{ Paths: []string{"testdata/main.k"}, diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go index fc1f4fec..1b6ea577 100644 --- a/pkg/ast/ast.go +++ b/pkg/ast/ast.go @@ -1,25 +1,43 @@ package ast -// TODO: add more nodes from https://github.com/kcl-lang/kcl/blob/main/kclvm/ast/src/ast.rs +// Module is an abstract syntax tree for a single KCL file. +type Module struct { + Filename string `json:"filename"` + Pkg string `json:"pkg"` + Doc *Node[string] `json:"doc"` + Body []*Node[Stmt] `json:"body"` + Comments []*Node[Comment] `json:"comments"` +} -// Pos denotes the struct tuple (filename, line, column, end_line, end_column). -type Pos struct { - Filename string `json:"filename"` - Line uint64 `json:"line"` - Column uint64 `json:"column"` - EndLine uint64 `json:"end_line"` - EndColumn uint64 `json:"end_column"` +// NewModule creates a new Module instance +func NewModule() *Module { + return &Module{ + Body: make([]*Node[Stmt], 0), + Comments: make([]*Node[Comment], 0), + } } -// Node is the file, line, and column number information that all AST nodes need to contain. -type Node interface { - Pos() Pos - Index() string +// Node is the file, line and column number information that all AST nodes need to contain. +// In fact, column and end_column are the counts of character. For example, `\t` is counted as 1 character, +// so it is recorded as 1 here, but generally col is 4. +type Node[T any] struct { + ID AstIndex `json:"id,omitempty"` + Node T `json:"node,omitempty"` + Pos } // AstIndex represents a unique identifier for AST nodes. type AstIndex string +// Pos denotes the struct tuple (filename, line, column, end_line, end_column). +type Pos struct { + Filename string `json:"filename,omitempty"` + Line int64 `json:"line,omitempty"` + Column int64 `json:"column,omitempty"` + EndLine int64 `json:"end_line,omitempty"` + EndColumn int64 `json:"end_column,omitempty"` +} + // Comment node. type Comment struct { Text string diff --git a/pkg/ast/expr.go b/pkg/ast/expr.go new file mode 100644 index 00000000..ab8b49a4 --- /dev/null +++ b/pkg/ast/expr.go @@ -0,0 +1,692 @@ +package ast + +// Expr is an interface for all expression types +type Expr interface { + Type() string +} + +// BaseExpr is a struct that all expression types can embed to implement the Expr interface +type BaseExpr struct { + ExprType string `json:"type"` +} + +func (b BaseExpr) Type() string { + return b.ExprType +} + +// Define all the expression types +type ( + // TargetExpr represents a target expression, e.g. + // + // a + // b + // _c + // a["b"][0].c + TargetExpr struct { + BaseExpr + Name *Node[string] `json:"name"` + Pkgpath string `json:"pkgpath,omitempty"` + Paths []MemberOrIndex `json:"paths,omitempty"` + } + // IdentifierExpr represents an identifier expression, e.g. + // + // a + // b + // _c + // pkg.a + IdentifierExpr struct { + BaseExpr + Identifier + // Add fields specific to IdentifierExpr + } + // UnaryExpr represents a unary expression, e.g. + // + // +1 + // -2 + // ~3 + // not True + UnaryExpr struct { + BaseExpr + Op UnaryOp `json:"op"` + Operand *Node[Expr] `json:"operand"` + } + // BinaryExpr represents a binary expression, e.g. + // + // 1 + 1 + // 3 - 2 + // 5 / 2 + // a is None + BinaryExpr struct { + BaseExpr + Left *Node[Expr] `json:"left"` + Op BinOp `json:"op"` + Right *Node[Expr] `json:"right"` + } + // IfExpr represents an if expression, e.g. + // + // 1 if condition else 2 + IfExpr struct { + BaseExpr + Body *Node[Expr] `json:"body"` + Cond *Node[Expr] `json:"cond"` + Orelse *Node[Expr] `json:"orelse"` + } + // SelectorExpr represents a selector expression, e.g. + // + // x.y + // x?.y + SelectorExpr struct { + BaseExpr + Value *Node[Expr] `json:"value"` + Attr *Node[Identifier] `json:"attr"` + Ctx ExprContext `json:"ctx"` + HasQuestion bool `json:"has_question"` + } + // CallExpr represents a function call expression, e.g. + // + // func1() + // func2(1) + // func3(x=2) + CallExpr struct { + BaseExpr + Func *Node[Expr] `json:"func"` + Args []*Node[Expr] `json:"args"` + Keywords []*Node[Keyword] `json:"keywords"` + } + // ParenExpr represents a parenthesized expression, e.g. + // + // 1 + (2 - 3) + ParenExpr struct { + BaseExpr + Expr *Node[Expr] `json:"expr"` + } + // QuantExpr represents a quantifier expression, e.g. + // + // all x in collection {x > 0} + // any y in collection {y < 0} + // map x in collection {x + 1} + // filter x in collection {x > 1} + QuantExpr struct { + BaseExpr + Target *Node[Expr] `json:"target"` + Variables []*Node[Identifier] `json:"variables"` + Op QuantOperation `json:"op"` + Test *Node[Expr] `json:"test"` + IfCond *Node[Expr] `json:"if_cond"` + Ctx ExprContext `json:"ctx"` + } + // ListExpr represents a list expression, e.g. + // + // [1, 2, 3] + // [1, if True: 2, 3] + ListExpr struct { + BaseExpr + Elts []*Node[Expr] `json:"elts"` + Ctx ExprContext `json:"ctx"` + } + // ListIfItemExpr represents a list if-item expression, e.g. + // + // [1, if True: 2, 3] + ListIfItemExpr struct { + BaseExpr + IfCond *Node[Expr] `json:"if_cond"` + Exprs []*Node[Expr] `json:"exprs"` + Orelse *Node[Expr] `json:"orelse"` + } + // ListComp represents a list comprehension expression, e.g. + // + // [x ** 2 for x in [1, 2, 3]] + ListComp struct { + BaseExpr + Elt *Node[Expr] `json:"elt"` + Generators []*Node[CompClause] `json:"generators"` + } + // StarredExpr represents a starred expression, e.g. + // + // [1, 2, *[3, 4]] + StarredExpr struct { + BaseExpr + Value *Node[Expr] `json:"value"` + Ctx ExprContext `json:"ctx"` + } + // DictComp represents a dictionary comprehension expression, e.g. + // + // {k: v + 1 for k, v in {k1 = 1, k2 = 2}} + DictComp struct { + BaseExpr + Entry ConfigEntry `json:"entry"` + Generators []*Node[CompClause] `json:"generators"` + } + // ConfigIfEntryExpr represents a conditional configuration entry, e.g. + // + // { + // k1 = 1 + // if condition: + // k2 = 2 + // } + ConfigIfEntryExpr struct { + BaseExpr + IfCond *Node[Expr] `json:"if_cond"` + Items []*Node[ConfigEntry] `json:"items"` + Orelse *Node[Expr] `json:"orelse"` + } + // CompClause represents a comprehension clause, e.g. + // + // i, a in [1, 2, 3] if i > 1 and a > 1 + CompClause struct { + BaseExpr + Targets []*Node[Identifier] `json:"targets"` + Iter *Node[Expr] `json:"iter"` + Ifs []*Node[Expr] `json:"ifs"` + } + // SchemaExpr represents a schema expression, e.g. + // + // ASchema(arguments) { + // attr1 = 1 + // attr2 = BSchema {attr3 = 2} + // } + SchemaExpr struct { + BaseExpr + Name *Node[Identifier] `json:"name"` + Args []*Node[Expr] `json:"args"` + Kwargs []*Node[Keyword] `json:"kwargs"` + Config *Node[Expr] `json:"config"` + } + // ConfigExpr represents a configuration expression, e.g. + // + // { + // attr1 = 1 + // attr2 += [0, 1] + // attr3: {key = value} + // } + ConfigExpr struct { + BaseExpr + Items []*Node[ConfigEntry] `json:"items"` + } + // LambdaExpr represents a lambda expression, e.g. + // + // lambda x, y { + // z = 2 * x + // z + y + // } + LambdaExpr struct { + BaseExpr + Args *Node[Arguments] `json:"args"` + Body []*Node[Stmt] `json:"body"` + ReturnTy *Node[Type] `json:"return_ty"` + } + // Subscript represents a subscript expression, e.g. + // + // a[0] + // b["k"] + // c?[1] + // d[1:2:n] + Subscript struct { + BaseExpr + Value *Node[Expr] `json:"value"` + Index *Node[Expr] `json:"index"` + Lower *Node[Expr] `json:"lower"` + Upper *Node[Expr] `json:"upper"` + Step *Node[Expr] `json:"step"` + Ctx ExprContext `json:"ctx"` + HasQuestion bool `json:"has_question"` + } + // Compare represents a comparison expression, e.g. + // + // 0 < a < 10 + // b is not None + // c != d + Compare struct { + BaseExpr + Left *Node[Expr] `json:"left"` + Ops []CmpOp `json:"ops"` + Comparators []*Node[Expr] `json:"comparators"` + } + // NumberLit represents a number literal, e.g. + // + // 1 + // 2.0 + // 1m + // 1K + // 1Mi + NumberLit struct { + BaseExpr + BinarySuffix *NumberBinarySuffix `json:"binary_suffix,omitempty"` + Value NumberLitValue `json:"value"` + } + // StringLit represents a string literal, e.g. + // + // "string literal" + // """long string literal""" + StringLit struct { + BaseExpr + IsLongString bool `json:"is_long_string"` + RawValue string `json:"raw_value"` + Value string `json:"value"` + } + // NameConstantLit represents a name constant literal, e.g. + // + // True + // False + // None + // Undefined + NameConstantLit struct { + BaseExpr + Value NameConstant `json:"value"` + } + // JoinedString represents a joined string, e.g. abc in the string interpolation "${var1} abc ${var2}" + JoinedString struct { + BaseExpr + IsLongString bool `json:"is_long_string"` + Values []*Node[Expr] `json:"values"` + RawValue string `json:"raw_value"` + } + // FormattedValue represents a formatted value, e.g. var1 and var2 in the string interpolation "${var1} abc ${var2}" + FormattedValue struct { + BaseExpr + IsLongString bool `json:"is_long_string"` + Value *Node[Expr] `json:"value"` + FormatSpec string `json:"format_spec"` + } + // MissingExpr is a placeholder for error recovery + MissingExpr struct { + BaseExpr + } +) + +// NewTargetExpr creates a new TargetExpr +func NewTargetExpr() *TargetExpr { + return &TargetExpr{ + BaseExpr: BaseExpr{ExprType: "Target"}, + Paths: make([]MemberOrIndex, 0), + } +} + +// NewIdentifierExpr creates a new IdentifierExpr +func NewIdentifierExpr() *IdentifierExpr { + return &IdentifierExpr{ + BaseExpr: BaseExpr{ExprType: "Identifier"}, + Identifier: Identifier{ + Names: make([]*Node[string], 0), + }, + } +} + +// NewUnaryExpr creates a new UnaryExpr +func NewUnaryExpr() *UnaryExpr { + return &UnaryExpr{ + BaseExpr: BaseExpr{ExprType: "Unary"}, + } +} + +// NewBinaryExpr creates a new BinaryExpr +func NewBinaryExpr() *BinaryExpr { + return &BinaryExpr{ + BaseExpr: BaseExpr{ExprType: "Binary"}, + } +} + +// NewIfExpr creates a new IfExpr +func NewIfExpr() *IfExpr { + return &IfExpr{ + BaseExpr: BaseExpr{ExprType: "If"}, + } +} + +// NewCallExpr creates a new CallExpr +func NewCallExpr() *CallExpr { + return &CallExpr{ + BaseExpr: BaseExpr{ExprType: "Call"}, + Args: make([]*Node[Expr], 0), + Keywords: make([]*Node[Keyword], 0), + } +} + +// NewParenExpr creates a new ParenExpr +func NewParenExpr() *ParenExpr { + return &ParenExpr{ + BaseExpr: BaseExpr{ExprType: "Paren"}, + } +} + +// NewQuantExpr creates a new QuantExpr +func NewQuantExpr() *QuantExpr { + return &QuantExpr{ + BaseExpr: BaseExpr{ExprType: "Quant"}, + Variables: make([]*Node[Identifier], 0), + } +} + +// NewListExpr creates a new ListExpr +func NewListExpr() *ListExpr { + return &ListExpr{ + BaseExpr: BaseExpr{ExprType: "List"}, + Elts: make([]*Node[Expr], 0), + } +} + +// NewListIfItemExpr creates a new ListIfItemExpr +func NewListIfItemExpr() *ListIfItemExpr { + return &ListIfItemExpr{ + BaseExpr: BaseExpr{ExprType: "ListIfItem"}, + Exprs: make([]*Node[Expr], 0), + } +} + +// NewListComp creates a new ListComp +func NewListComp() *ListComp { + return &ListComp{ + BaseExpr: BaseExpr{ExprType: "ListComp"}, + Generators: make([]*Node[CompClause], 0), + } +} + +// NewStarredExpr creates a new StarredExpr +func NewStarredExpr() *StarredExpr { + return &StarredExpr{ + BaseExpr: BaseExpr{ExprType: "Starred"}, + } +} + +// NewDictComp creates a new DictComp +func NewDictComp() *DictComp { + return &DictComp{ + BaseExpr: BaseExpr{ExprType: "DictComp"}, + Generators: make([]*Node[CompClause], 0), + } +} + +// NewCompClause creates a new CompClause +func NewCompClause() *CompClause { + return &CompClause{ + Targets: make([]*Node[Identifier], 0), + Ifs: make([]*Node[Expr], 0), + } +} + +// NewSchemaExpr creates a new SchemaExpr +func NewSchemaExpr() *SchemaExpr { + return &SchemaExpr{ + BaseExpr: BaseExpr{ExprType: "Schema"}, + Args: make([]*Node[Expr], 0), + Kwargs: make([]*Node[Keyword], 0), + } +} + +// NewConfigExpr creates a new ConfigExpr +func NewConfigExpr() *ConfigExpr { + return &ConfigExpr{ + BaseExpr: BaseExpr{ExprType: "Config"}, + Items: make([]*Node[ConfigEntry], 0), + } +} + +// NewLambdaExpr creates a new LambdaExpr +func NewLambdaExpr() *LambdaExpr { + return &LambdaExpr{ + BaseExpr: BaseExpr{ExprType: "Lambda"}, + Body: make([]*Node[Stmt], 0), + } +} + +// NewSubscript creates a new Subscript +func NewSubscript() *Subscript { + return &Subscript{ + BaseExpr: BaseExpr{ExprType: "Subscript"}, + } +} + +// NewCompare creates a new Compare +func NewCompare() *Compare { + return &Compare{ + BaseExpr: BaseExpr{ExprType: "Compare"}, + Ops: make([]CmpOp, 0), + Comparators: make([]*Node[Expr], 0), + } +} + +// NewNumberLit creates a new NumberLit +func NewNumberLit() *NumberLit { + return &NumberLit{ + BaseExpr: BaseExpr{ExprType: "Number"}, + } +} + +// NewStringLit creates a new StringLit with default values +func NewStringLit() *StringLit { + return &StringLit{ + BaseExpr: BaseExpr{ExprType: "String"}, + Value: "", + RawValue: "\"\"", + IsLongString: false, + } +} + +// NewNameConstantLit creates a new NameConstantLit +func NewNameConstantLit() *NameConstantLit { + return &NameConstantLit{ + BaseExpr: BaseExpr{ExprType: "NameConstant"}, + } +} + +// NewJoinedString creates a new JoinedString +func NewJoinedString() *JoinedString { + return &JoinedString{ + BaseExpr: BaseExpr{ExprType: "JoinedString"}, + Values: make([]*Node[Expr], 0), + } +} + +// NewFormattedValue creates a new FormattedValue +func NewFormattedValue() *FormattedValue { + return &FormattedValue{ + BaseExpr: BaseExpr{ExprType: "FormattedValue"}, + } +} + +// NewMissingExpr creates a new MissingExpr +func NewMissingExpr() *MissingExpr { + return &MissingExpr{ + BaseExpr: BaseExpr{ExprType: "MissingExpr"}, + } +} + +// Identifier represents an identifier, e.g. +// +// a +// b +// _c +// pkg.a +type Identifier struct { + Names []*Node[string] `json:"names"` + Pkgpath string `json:"pkgpath"` + Ctx ExprContext `json:"ctx"` +} + +// ExprContext denotes the value context in the expression. e.g., +// +// The context of 'a' in 'a = b' is Store +// +// The context of 'b' in 'a = b' is Load +type ExprContext int + +const ( + Load ExprContext = iota + Store +) + +// String returns the string representation of ExprContext +func (e ExprContext) String() string { + return [...]string{"Load", "Store"}[e] +} + +// SchemaConfig represents a schema configuration, e.g. +// +// ASchema(arguments) { +// attr1 = 1 +// attr2 = BSchema {attr3 = 2} +// } +type SchemaConfig struct { + Name *Node[Identifier] `json:"name"` + Args []*Node[Expr] `json:"args"` + Kwargs []*Node[Keyword] `json:"kwargs"` + Config *Node[Expr] `json:"config"` +} + +// NewSchemaConfig creates a new SchemaConfig +func NewSchemaConfig() *SchemaConfig { + return &SchemaConfig{ + Args: make([]*Node[Expr], 0), + Kwargs: make([]*Node[Keyword], 0), + } +} + +// Keyword represents a keyword argument, e.g. +// +// arg = value +type Keyword struct { + Arg *Node[Identifier] `json:"arg"` + Value *Node[Expr] `json:"value"` +} + +// Target represents a target in an assignment, e.g. +// +// a +// b +// _c +// a["b"][0].c +type Target struct { + Name *Node[string] `json:"name"` + Pkgpath string `json:"pkgpath"` + Paths []*MemberOrIndex `json:"paths"` +} + +// MemberOrIndex is the base interface for member or index expression +// +// a. +// b[] +type MemberOrIndex interface { + Type() string +} + +// Member represents a member access +type Member struct { + Value *Node[string] `json:"value"` +} + +// Type returns the type of Member +func (m *Member) Type() string { + return "Member" +} + +// Index represents an index access +type Index struct { + Value *Node[Expr] `json:"value"` +} + +// Type returns the type of Index +func (i *Index) Type() string { + return "Index" +} + +// Decorator represents a decorator, e.g. +// +// deprecated(strict=True) +type Decorator struct { + Func *Node[Expr] `json:"func"` + Args []*Node[Expr] `json:"args,omitempty"` + Keywords []*Node[Keyword] `json:"keywords,omitempty"` +} + +// NewDecorator creates a new Decorator +func NewDecorator() *Decorator { + return &Decorator{ + Args: make([]*Node[Expr], 0), + Keywords: make([]*Node[Keyword], 0), + } +} + +// Arguments represents function arguments, e.g. +// +// lambda x: int = 1, y: int = 1 { +// x + y +// } +type Arguments struct { + Args []*Node[Identifier] `json:"args"` + Defaults []*Node[Expr] `json:"defaults,omitempty"` // Slice can contain nil to represent Rust's Vec>> + TyList []*Node[Type] `json:"ty_list,omitempty"` // Slice can contain nil to represent Rust's Vec>> +} + +// NewArguments creates a new Arguments +func NewArguments() *Arguments { + return &Arguments{ + Args: make([]*Node[Identifier], 0), + Defaults: make([]*Node[Expr], 0), + TyList: make([]*Node[Type], 0), + } +} + +// CheckExpr represents a check expression, e.g. +// +// len(attr) > 3 if attr, "Check failed message" +type CheckExpr struct { + Test *Node[Expr] `json:"test"` + IfCond *Node[Expr] `json:"if_cond,omitempty"` + Msg *Node[Expr] `json:"msg,omitempty"` +} + +// NewCheckExpr creates a new CheckExpr +func NewCheckExpr() *CheckExpr { + return &CheckExpr{} +} + +// ConfigEntry represents a configuration entry, e.g. +// +// { +// attr1 = 1 +// attr2 += [0, 1] +// attr3: {key = value} +// } +type ConfigEntry struct { + Key *Node[Expr] `json:"key"` + Value *Node[Expr] `json:"value"` + Operation ConfigEntryOperation `json:"operation"` +} + +// NewConfigEntry creates a new ConfigEntry +func NewConfigEntry() *ConfigEntry { + return &ConfigEntry{} +} + +// NewConfigIfEntryExpr creates a new ConfigIfEntryExpr +func NewConfigIfEntryExpr() *ConfigIfEntryExpr { + return &ConfigIfEntryExpr{ + BaseExpr: BaseExpr{ExprType: "ConfigIfEntry"}, + Items: make([]*Node[ConfigEntry], 0), + } +} + +// NumberLitValue represents the value of a number literal +type NumberLitValue interface { + Type() string +} + +// IntNumberLitValue represents an integer number literal value +type IntNumberLitValue struct { + Value int64 `json:"value"` +} + +// Type returns the type of the number literal value +func (i *IntNumberLitValue) Type() string { + return "Int" +} + +// FloatNumberLitValue represents a float number literal value +type FloatNumberLitValue struct { + Value float64 `json:"value"` +} + +// Type returns the type of the number literal value +func (f *FloatNumberLitValue) Type() string { + return "Float" +} diff --git a/pkg/ast/json.go b/pkg/ast/json.go new file mode 100644 index 00000000..89a42d4c --- /dev/null +++ b/pkg/ast/json.go @@ -0,0 +1,1576 @@ +package ast + +import ( + "encoding/json" + "fmt" +) + +// UnmarshalJSON implements custom JSON unmarshaling for Stmt +func UnmarshalStmt(data []byte) (Stmt, error) { + var base BaseStmt + + if err := json.Unmarshal(data, &base); err != nil { + return nil, err + } + + var stmt Stmt + switch base.StmtType { + case "TypeAlias": + stmt = &TypeAliasStmt{} + case "Expr": + stmt = &ExprStmt{} + case "Unification": + stmt = &UnificationStmt{} + case "Assign": + stmt = &AssignStmt{} + case "AugAssign": + stmt = &AugAssignStmt{} + case "Assert": + stmt = &AssertStmt{} + case "If": + stmt = &IfStmt{} + case "Import": + stmt = &ImportStmt{} + case "SchemaAttr": + stmt = &SchemaAttr{} + case "Schema": + stmt = &SchemaStmt{} + case "Rule": + stmt = &RuleStmt{} + default: + return nil, fmt.Errorf("unknown statement type: %s", base.StmtType) + } + + if err := json.Unmarshal(data, stmt); err != nil { + return nil, err + } + + return stmt, nil +} + +// UnmarshalExprJSON implements custom JSON unmarshaling for Expr +func UnmarshalExpr(data []byte) (Expr, error) { + var base BaseExpr + if err := json.Unmarshal(data, &base); err != nil { + return nil, err + } + + var expr Expr + switch base.ExprType { + case "Target": + expr = &TargetExpr{} + case "Identifier": + expr = &IdentifierExpr{} + case "Unary": + expr = &UnaryExpr{} + case "Binary": + expr = &BinaryExpr{} + case "If": + expr = &IfExpr{} + case "Selector": + expr = &SelectorExpr{} + case "Call": + expr = &CallExpr{} + case "Paren": + expr = &ParenExpr{} + case "Quant": + expr = &QuantExpr{} + case "List": + expr = &ListExpr{} + case "ListIfItem": + expr = &ListIfItemExpr{} + case "ListComp": + expr = &ListComp{} + case "Starred": + expr = &StarredExpr{} + case "DictComp": + expr = &DictComp{} + case "ConfigIfEntry": + expr = &ConfigIfEntryExpr{} + case "CompClause": + expr = &CompClause{} + case "Schema": + expr = &SchemaExpr{} + case "Config": + expr = &ConfigExpr{} + case "Lambda": + expr = &LambdaExpr{} + case "Subscript": + expr = &Subscript{} + case "Compare": + expr = &Compare{} + case "NumberLit": + expr = &NumberLit{} + case "StringLit": + expr = &StringLit{} + case "NameConstantLit": + expr = &NameConstantLit{} + case "JoinedString": + expr = &JoinedString{} + case "FormattedValue": + expr = &FormattedValue{} + case "Missing": + expr = &MissingExpr{} + default: + return nil, fmt.Errorf("unknown expression type: %s", base.ExprType) + } + + if err := json.Unmarshal(data, expr); err != nil { + return nil, err + } + + return expr, nil +} + +// MarshalJSON implements the json.Marshaler interface +func (n *Node[Stmt]) MarshalJSON() ([]byte, error) { + type Alias Node[Stmt] + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(n), + }) +} + +// UnmarshalJSON implements the json.Unmarshaler interface +func (n *Node[T]) UnmarshalJSON(data []byte) error { + var objMap map[string]json.RawMessage + if err := json.Unmarshal(data, &objMap); err != nil { + return err + } + + if data, ok := objMap["id"]; ok { + if err := json.Unmarshal(data, &n.ID); err != nil { + return err + } + } + if data, ok := objMap["filename"]; ok { + if err := json.Unmarshal(data, &n.Pos.Filename); err != nil { + return err + } + } + if data, ok := objMap["line"]; ok { + if err := json.Unmarshal(data, &n.Pos.Line); err != nil { + return err + } + } + if data, ok := objMap["column"]; ok { + if err := json.Unmarshal(data, &n.Pos.Column); err != nil { + return err + } + } + if data, ok := objMap["end_line"]; ok { + if err := json.Unmarshal(data, &n.Pos.EndLine); err != nil { + return err + } + } + if data, ok := objMap["end_column"]; ok { + if err := json.Unmarshal(data, &n.Pos.EndColumn); err != nil { + return err + } + } + + nodeData, ok := objMap["node"] + if !ok { + return fmt.Errorf("missing 'node' field") + } + if expr, err := UnmarshalExpr(nodeData); err == nil { + if n.Node, ok = expr.(T); ok { + return nil + } + } + if stmt, err := UnmarshalStmt(nodeData); err == nil { + if n.Node, ok = stmt.(T); ok { + return nil + } + } + if memberOrIndex, err := UnmarshalMemberOrIndex(nodeData); err == nil { + if n.Node, ok = memberOrIndex.(T); ok { + return nil + } + } + if numberLit, err := UnmarshalNumberLitValue(nodeData); err == nil { + if n.Node, ok = numberLit.(T); ok { + return nil + } + } + if ty, err := UnmarshalType(nodeData); err == nil { + if n.Node, ok = ty.(T); ok { + return nil + } + } else { + otherNode := new(T) + if err := json.Unmarshal(nodeData, otherNode); err != nil { + return err + } + n.Node = *otherNode + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for AssignStmt +func (a *AssignStmt) MarshalJSON() ([]byte, error) { + type Alias AssignStmt + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(a), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for AssignStmt +func (a *AssignStmt) UnmarshalJSON(data []byte) error { + type Alias AssignStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(a), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements the json.Marshaler interface for ExprContext +func (e ExprContext) MarshalJSON() ([]byte, error) { + return []byte(`"` + e.String() + `"`), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface for ExprContext +func (e *ExprContext) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + switch s { + case "Load": + *e = Load + case "Store": + *e = Store + default: + return fmt.Errorf("invalid ExprContext: %s", s) + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for SchemaConfig +func (s *SchemaConfig) MarshalJSON() ([]byte, error) { + type Alias SchemaConfig + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for SchemaConfig +func (s *SchemaConfig) UnmarshalJSON(data []byte) error { + type Alias SchemaConfig + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for Keyword +func (k *Keyword) MarshalJSON() ([]byte, error) { + type Alias Keyword + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(k), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for Keyword +func (k *Keyword) UnmarshalJSON(data []byte) error { + type Alias Keyword + aux := &struct { + *Alias + }{ + Alias: (*Alias)(k), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// UnmarshalMemberOrIndex is a helper function to unmarshal JSON into MemberOrIndex +func UnmarshalMemberOrIndex(data []byte) (MemberOrIndex, error) { + var base struct { + Type string `json:"type"` + } + if err := json.Unmarshal(data, &base); err != nil { + return nil, err + } + + var result MemberOrIndex + switch base.Type { + case "Member": + result = &Member{} + case "Index": + result = &Index{} + default: + return nil, fmt.Errorf("unknown MemberOrIndex type: %s", base.Type) + } + + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + + return result, nil +} + +// MarshalJSON implements custom JSON marshaling for Target +func (t *Target) MarshalJSON() ([]byte, error) { + type Alias Target + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(t), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for Target +func (t *Target) UnmarshalJSON(data []byte) error { + type Alias Target + aux := &struct { + *Alias + }{ + Alias: (*Alias)(t), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + // Unmarshal Paths + var rawPaths []json.RawMessage + if err := json.Unmarshal(data, &struct { + Paths *[]json.RawMessage `json:"paths"` + }{Paths: &rawPaths}); err != nil { + return err + } + + t.Paths = make([]*MemberOrIndex, len(rawPaths)) + for i, rawPath := range rawPaths { + path, err := UnmarshalMemberOrIndex(rawPath) + if err != nil { + return fmt.Errorf("error unmarshaling path %d in Target: %v", i, err) + } + t.Paths[i] = &path + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for AugAssignStmt +func (a *AugAssignStmt) MarshalJSON() ([]byte, error) { + type Alias AugAssignStmt + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(a), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for AugAssignStmt +func (a *AugAssignStmt) UnmarshalJSON(data []byte) error { + type Alias AugAssignStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(a), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for AssertStmt +func (a *AssertStmt) MarshalJSON() ([]byte, error) { + type Alias AssertStmt + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(a), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for AssertStmt +func (a *AssertStmt) UnmarshalJSON(data []byte) error { + type Alias AssertStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(a), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for IfStmt +func (i *IfStmt) MarshalJSON() ([]byte, error) { + type Alias IfStmt + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(i), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for IfStmt +func (i *IfStmt) UnmarshalJSON(data []byte) error { + type Alias IfStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(i), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for TypeAliasStmt +func (t *TypeAliasStmt) MarshalJSON() ([]byte, error) { + type Alias TypeAliasStmt + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(t), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for TypeAliasStmt +func (t *TypeAliasStmt) UnmarshalJSON(data []byte) error { + type Alias TypeAliasStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(t), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for ImportStmt +func (i *ImportStmt) MarshalJSON() ([]byte, error) { + type Alias ImportStmt + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(i), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ImportStmt +func (i *ImportStmt) UnmarshalJSON(data []byte) error { + type Alias ImportStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(i), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for SchemaAttr +func (s *SchemaAttr) MarshalJSON() ([]byte, error) { + type Alias SchemaAttr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for SchemaAttr +func (s *SchemaAttr) UnmarshalJSON(data []byte) error { + type Alias SchemaAttr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure Decorators is initialized + if s.Decorators == nil { + s.Decorators = make([]*Node[Decorator], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for Decorator +func (d *Decorator) MarshalJSON() ([]byte, error) { + type Alias Decorator + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(d), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for Decorator +func (d *Decorator) UnmarshalJSON(data []byte) error { + type Alias Decorator + aux := &struct { + *Alias + }{ + Alias: (*Alias)(d), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure Args and Keywords are initialized + if d.Args == nil { + d.Args = make([]*Node[Expr], 0) + } + if d.Keywords == nil { + d.Keywords = make([]*Node[Keyword], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for Arguments +func (a *Arguments) MarshalJSON() ([]byte, error) { + type Alias Arguments + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(a), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for Arguments +func (a *Arguments) UnmarshalJSON(data []byte) error { + type Alias Arguments + aux := &struct { + *Alias + }{ + Alias: (*Alias)(a), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if a.Args == nil { + a.Args = make([]*Node[Identifier], 0) + } + if a.Defaults == nil { + a.Defaults = make([]*Node[Expr], 0) + } + if a.TyList == nil { + a.TyList = make([]*Node[Type], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for CheckExpr +func (c *CheckExpr) MarshalJSON() ([]byte, error) { + type Alias CheckExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(c), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for CheckExpr +func (c *CheckExpr) UnmarshalJSON(data []byte) error { + type Alias CheckExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for SchemaStmt +func (s *SchemaStmt) MarshalJSON() ([]byte, error) { + type Alias SchemaStmt + return json.Marshal(&struct { + Type string `json:"type"` + *Alias + }{ + Type: "SchemaStmt", + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for SchemaStmt +func (s *SchemaStmt) UnmarshalJSON(data []byte) error { + type Alias SchemaStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if s.Mixins == nil { + s.Mixins = make([]*Node[Identifier], 0) + } + if s.Body == nil { + s.Body = make([]*Node[Stmt], 0) + } + if s.Decorators == nil { + s.Decorators = make([]*Node[Decorator], 0) + } + if s.Checks == nil { + s.Checks = make([]*Node[CheckExpr], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for SchemaIndexSignature +func (s *SchemaIndexSignature) MarshalJSON() ([]byte, error) { + type Alias SchemaIndexSignature + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for SchemaIndexSignature +func (s *SchemaIndexSignature) UnmarshalJSON(data []byte) error { + type Alias SchemaIndexSignature + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for RuleStmt +func (r *RuleStmt) MarshalJSON() ([]byte, error) { + type Alias RuleStmt + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(r), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for RuleStmt +func (r *RuleStmt) UnmarshalJSON(data []byte) error { + type Alias RuleStmt + aux := &struct { + *Alias + }{ + Alias: (*Alias)(r), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if r.ParentRules == nil { + r.ParentRules = make([]*Node[Identifier], 0) + } + if r.Decorators == nil { + r.Decorators = make([]*Node[Decorator], 0) + } + if r.Checks == nil { + r.Checks = make([]*Node[CheckExpr], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for TargetExpr +func (t *TargetExpr) MarshalJSON() ([]byte, error) { + type Alias TargetExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(t), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for TargetExpr +func (t *TargetExpr) UnmarshalJSON(data []byte) error { + type Alias TargetExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(t), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure Paths is initialized + if t.Paths == nil { + t.Paths = make([]MemberOrIndex, 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for IdentifierExpr +func (i *IdentifierExpr) MarshalJSON() ([]byte, error) { + type Alias IdentifierExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(i), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for IdentifierExpr +func (i *IdentifierExpr) UnmarshalJSON(data []byte) error { + type Alias IdentifierExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(i), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure Names is initialized + if i.Names == nil { + i.Names = make([]*Node[string], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for UnaryExpr +func (u *UnaryExpr) MarshalJSON() ([]byte, error) { + type Alias UnaryExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(u), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for UnaryExpr +func (u *UnaryExpr) UnmarshalJSON(data []byte) error { + type Alias UnaryExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(u), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for IfExpr +func (i *IfExpr) MarshalJSON() ([]byte, error) { + type Alias IfExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(i), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for IfExpr +func (i *IfExpr) UnmarshalJSON(data []byte) error { + type Alias IfExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(i), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for SelectorExpr +func (s *SelectorExpr) MarshalJSON() ([]byte, error) { + type Alias SelectorExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for SelectorExpr +func (s *SelectorExpr) UnmarshalJSON(data []byte) error { + type Alias SelectorExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for CallExpr +func (c *CallExpr) MarshalJSON() ([]byte, error) { + type Alias CallExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(c), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for CallExpr +func (c *CallExpr) UnmarshalJSON(data []byte) error { + type Alias CallExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if c.Args == nil { + c.Args = make([]*Node[Expr], 0) + } + if c.Keywords == nil { + c.Keywords = make([]*Node[Keyword], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for ParenExpr +func (p *ParenExpr) MarshalJSON() ([]byte, error) { + type Alias ParenExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(p), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ParenExpr +func (p *ParenExpr) UnmarshalJSON(data []byte) error { + type Alias ParenExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(p), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for QuantExpr +func (q *QuantExpr) MarshalJSON() ([]byte, error) { + type Alias QuantExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(q), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for QuantExpr +func (q *QuantExpr) UnmarshalJSON(data []byte) error { + type Alias QuantExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(q), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if q.Variables == nil { + q.Variables = make([]*Node[Identifier], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for ListExpr +func (l *ListExpr) MarshalJSON() ([]byte, error) { + type Alias ListExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(l), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ListExpr +func (l *ListExpr) UnmarshalJSON(data []byte) error { + type Alias ListExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(l), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if l.Elts == nil { + l.Elts = make([]*Node[Expr], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for ListIfItemExpr +func (l *ListIfItemExpr) MarshalJSON() ([]byte, error) { + type Alias ListIfItemExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(l), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ListIfItemExpr +func (l *ListIfItemExpr) UnmarshalJSON(data []byte) error { + type Alias ListIfItemExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(l), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if l.Exprs == nil { + l.Exprs = make([]*Node[Expr], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for ListComp +func (l *ListComp) MarshalJSON() ([]byte, error) { + type Alias ListComp + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(l), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ListComp +func (l *ListComp) UnmarshalJSON(data []byte) error { + type Alias ListComp + aux := &struct { + *Alias + }{ + Alias: (*Alias)(l), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if l.Generators == nil { + l.Generators = make([]*Node[CompClause], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for StarredExpr +func (s *StarredExpr) MarshalJSON() ([]byte, error) { + type Alias StarredExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for StarredExpr +func (s *StarredExpr) UnmarshalJSON(data []byte) error { + type Alias StarredExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for DictComp +func (d *DictComp) MarshalJSON() ([]byte, error) { + type Alias DictComp + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(d), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for DictComp +func (d *DictComp) UnmarshalJSON(data []byte) error { + type Alias DictComp + aux := &struct { + *Alias + }{ + Alias: (*Alias)(d), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if d.Generators == nil { + d.Generators = make([]*Node[CompClause], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for ConfigEntry +func (c *ConfigEntry) MarshalJSON() ([]byte, error) { + type Alias ConfigEntry + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(c), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ConfigEntry +func (c *ConfigEntry) UnmarshalJSON(data []byte) error { + type Alias ConfigEntry + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for ConfigIfEntryExpr +func (c *ConfigIfEntryExpr) MarshalJSON() ([]byte, error) { + type Alias ConfigIfEntryExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(c), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ConfigIfEntryExpr +func (c *ConfigIfEntryExpr) UnmarshalJSON(data []byte) error { + type Alias ConfigIfEntryExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if c.Items == nil { + c.Items = make([]*Node[ConfigEntry], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for CompClause +func (c *CompClause) MarshalJSON() ([]byte, error) { + type Alias CompClause + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(c), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for CompClause +func (c *CompClause) UnmarshalJSON(data []byte) error { + type Alias CompClause + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if c.Targets == nil { + c.Targets = make([]*Node[Identifier], 0) + } + if c.Ifs == nil { + c.Ifs = make([]*Node[Expr], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for SchemaExpr +func (s *SchemaExpr) MarshalJSON() ([]byte, error) { + type Alias SchemaExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for SchemaExpr +func (s *SchemaExpr) UnmarshalJSON(data []byte) error { + type Alias SchemaExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if s.Args == nil { + s.Args = make([]*Node[Expr], 0) + } + if s.Kwargs == nil { + s.Kwargs = make([]*Node[Keyword], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for ConfigExpr +func (c *ConfigExpr) MarshalJSON() ([]byte, error) { + type Alias ConfigExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(c), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for ConfigExpr +func (c *ConfigExpr) UnmarshalJSON(data []byte) error { + type Alias ConfigExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if c.Items == nil { + c.Items = make([]*Node[ConfigEntry], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for LambdaExpr +func (l *LambdaExpr) MarshalJSON() ([]byte, error) { + type Alias LambdaExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(l), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for LambdaExpr +func (l *LambdaExpr) UnmarshalJSON(data []byte) error { + type Alias LambdaExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(l), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if l.Body == nil { + l.Body = make([]*Node[Stmt], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for Subscript +func (s *Subscript) MarshalJSON() ([]byte, error) { + type Alias Subscript + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for Subscript +func (s *Subscript) UnmarshalJSON(data []byte) error { + type Alias Subscript + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for Compare +func (c *Compare) MarshalJSON() ([]byte, error) { + type Alias Compare + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(c), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for Compare +func (c *Compare) UnmarshalJSON(data []byte) error { + type Alias Compare + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slices are initialized + if c.Ops == nil { + c.Ops = make([]CmpOp, 0) + } + if c.Comparators == nil { + c.Comparators = make([]*Node[Expr], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for NumberLit +func (n *NumberLit) MarshalJSON() ([]byte, error) { + type Alias NumberLit + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(n), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for NumberLit +func (n *NumberLit) UnmarshalJSON(data []byte) error { + var aux struct { + Type string `json:"type"` + BinarySuffix *NumberBinarySuffix `json:"binary_suffix,omitempty"` + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + if aux.Type != "NumberLit" { + return fmt.Errorf("unexpected type for NumberLit: %s", aux.Type) + } + // Unmarshal the Value field based on its actual type + var rawValue json.RawMessage + if err := json.Unmarshal(data, &struct { + Value *json.RawMessage `json:"value"` + }{Value: &rawValue}); err != nil { + return err + } + + value, err := UnmarshalNumberLitValue(rawValue) + if err != nil { + return err + } + n.Value = value + n.BinarySuffix = aux.BinarySuffix + + return nil +} + +// UnmarshalNumberLitValue unmarshals JSON data into a NumberLitValue +func UnmarshalNumberLitValue(data []byte) (NumberLitValue, error) { + var temp struct { + Type string `json:"type"` + } + if err := json.Unmarshal(data, &temp); err != nil { + return nil, err + } + + var result NumberLitValue + switch temp.Type { + case "Int": + var intValue IntNumberLitValue + if err := json.Unmarshal(data, &intValue); err != nil { + return nil, err + } + result = &intValue + case "Float": + var floatValue FloatNumberLitValue + if err := json.Unmarshal(data, &floatValue); err != nil { + return nil, err + } + result = &floatValue + default: + return nil, fmt.Errorf("unknown NumberLitValue type: %s", temp.Type) + } + + return result, nil +} + +// MarshalJSON implements custom JSON marshaling for NumberLitValue +func MarshalNumberLitValue(v NumberLitValue) ([]byte, error) { + switch value := v.(type) { + case *IntNumberLitValue: + return json.Marshal(struct { + Type string `json:"type"` + Value int64 `json:"value"` + }{ + Type: "Int", + Value: value.Value, + }) + case *FloatNumberLitValue: + return json.Marshal(struct { + Type string `json:"type"` + Value float64 `json:"value"` + }{ + Type: "Float", + Value: value.Value, + }) + default: + return nil, fmt.Errorf("unknown NumberLitValue type: %T", v) + } +} + +// MarshalJSON implements custom JSON marshaling for StringLit +func (s *StringLit) MarshalJSON() ([]byte, error) { + type Alias StringLit + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(s), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for StringLit +func (s *StringLit) UnmarshalJSON(data []byte) error { + type Alias StringLit + aux := &struct { + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for NameConstantLit +func (n *NameConstantLit) MarshalJSON() ([]byte, error) { + type Alias NameConstantLit + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(n), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for NameConstantLit +func (n *NameConstantLit) UnmarshalJSON(data []byte) error { + type Alias NameConstantLit + aux := &struct { + *Alias + }{ + Alias: (*Alias)(n), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + return nil +} + +// MarshalJSON implements custom JSON marshaling for JoinedString +func (j *JoinedString) MarshalJSON() ([]byte, error) { + type Alias JoinedString + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(j), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for JoinedString +func (j *JoinedString) UnmarshalJSON(data []byte) error { + type Alias JoinedString + aux := &struct { + *Alias + }{ + Alias: (*Alias)(j), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Ensure slice is initialized + if j.Values == nil { + j.Values = make([]*Node[Expr], 0) + } + + return nil +} + +// MarshalJSON implements custom JSON marshaling for FormattedValue +func (f *FormattedValue) MarshalJSON() ([]byte, error) { + type Alias FormattedValue + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(f), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for FormattedValue +func (f *FormattedValue) UnmarshalJSON(data []byte) error { + type Alias FormattedValue + aux := &struct { + *Alias + }{ + Alias: (*Alias)(f), + } + return json.Unmarshal(data, &aux) +} + +// MarshalJSON implements custom JSON marshaling for MissingExpr +func (m *MissingExpr) MarshalJSON() ([]byte, error) { + type Alias MissingExpr + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(m), + }) +} + +// UnmarshalJSON implements custom JSON unmarshaling for MissingExpr +func (m *MissingExpr) UnmarshalJSON(data []byte) error { + type Alias MissingExpr + aux := &struct { + *Alias + }{ + Alias: (*Alias)(m), + } + return json.Unmarshal(data, &aux) +} + +// UnmarshalType is a helper function to unmarshal JSON into Type +func UnmarshalType(data []byte) (Type, error) { + var base struct { + Type string `json:"type"` + } + if err := json.Unmarshal(data, &base); err != nil { + return nil, err + } + + var result Type + switch base.Type { + case "Named": + result = &NamedType{} + case "Any": + result = &AnyType{} + case "Basic": + result = &BasicType{} + case "List": + result = &ListType{} + case "Dict": + result = &DictType{} + case "Union": + result = &UnionType{} + case "Literal": + literalType := &LiteralType{} + var literalBase struct { + Value struct { + Type string `json:"type"` + Value json.RawMessage `json:"value"` + } `json:"value"` + } + if err := json.Unmarshal(data, &literalBase); err != nil { + return nil, err + } + + var literalValue LiteralTypeValue + switch literalBase.Value.Type { + case "Bool": + literalValue = new(BoolLiteralType) + case "Int": + literalValue = &IntLiteralType{} + case "Float": + literalValue = new(FloatLiteralType) + case "Str": + literalValue = new(StrLiteralType) + default: + return nil, fmt.Errorf("unknown LiteralType: %s", literalBase.Value.Type) + } + + if err := json.Unmarshal(literalBase.Value.Value, literalValue); err != nil { + return nil, err + } + + literalType.Value = literalValue + return literalType, nil + case "Function": + result = &FunctionType{} + default: + return nil, fmt.Errorf("unknown Type: %s", base.Type) + } + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + return result, nil +} diff --git a/pkg/ast/op.go b/pkg/ast/op.go new file mode 100644 index 00000000..d5c2bb1f --- /dev/null +++ b/pkg/ast/op.go @@ -0,0 +1,519 @@ +package ast + +import ( + "encoding/json" + "fmt" +) + +// AugOp represents augmented assignment operations +type AugOp string + +const ( + AugOpAssign AugOp = "=" + AugOpAdd AugOp = "+=" + AugOpSub AugOp = "-=" + AugOpMul AugOp = "*=" + AugOpDiv AugOp = "/=" + AugOpMod AugOp = "%=" + AugOpPow AugOp = "**=" + AugOpFloorDiv AugOp = "//=" + AugOpLShift AugOp = "<<=" + AugOpRShift AugOp = ">>=" + AugOpBitXor AugOp = "^=" + AugOpBitAnd AugOp = "&=" + AugOpBitOr AugOp = "|=" +) + +// Symbol returns the string representation of the AugOp +func (a AugOp) Symbol() string { + return string(a) +} + +// ToBinOp converts AugOp to BinOp, if possible +func (a AugOp) ToBinOp() (BinOp, error) { + switch a { + case AugOpAdd: + return BinOpAdd, nil + case AugOpSub: + return BinOpSub, nil + case AugOpMul: + return BinOpMul, nil + case AugOpDiv: + return BinOpDiv, nil + case AugOpMod: + return BinOpMod, nil + case AugOpPow: + return BinOpPow, nil + case AugOpFloorDiv: + return BinOpFloorDiv, nil + case AugOpLShift: + return BinOpLShift, nil + case AugOpRShift: + return BinOpRShift, nil + case AugOpBitXor: + return BinOpBitXor, nil + case AugOpBitAnd: + return BinOpBitAnd, nil + case AugOpBitOr: + return BinOpBitOr, nil + default: + return "", fmt.Errorf("AugOp cannot be converted to BinOp") + } +} + +// UnaryOp represents a unary operator +type UnaryOp string + +const ( + UnaryOpUAdd UnaryOp = "+" + UnaryOpUSub UnaryOp = "-" + UnaryOpInvert UnaryOp = "~" + UnaryOpNot UnaryOp = "not" +) + +// Symbol returns the string representation of the unary operator +func (op UnaryOp) Symbol() string { + return string(op) +} + +// AllUnaryOps returns all possible UnaryOp values +func AllUnaryOps() []UnaryOp { + return []UnaryOp{ + UnaryOpUAdd, + UnaryOpUSub, + UnaryOpInvert, + UnaryOpNot, + } +} + +// UnaryOpFromSymbol returns the UnaryOp corresponding to the given symbol +func UnaryOpFromSymbol(symbol string) (UnaryOp, bool) { + switch symbol { + case "+": + return UnaryOpUAdd, true + case "-": + return UnaryOpUSub, true + case "~": + return UnaryOpInvert, true + case "not": + return UnaryOpNot, true + default: + return "", false + } +} + +// BinOp represents a binary operator +type BinOp string + +const ( + BinOpAdd BinOp = "+" + BinOpSub BinOp = "-" + BinOpMul BinOp = "*" + BinOpDiv BinOp = "/" + BinOpMod BinOp = "%" + BinOpPow BinOp = "**" + BinOpFloorDiv BinOp = "//" + BinOpLShift BinOp = "<<" + BinOpRShift BinOp = ">>" + BinOpBitXor BinOp = "^" + BinOpBitAnd BinOp = "&" + BinOpBitOr BinOp = "|" + BinOpAnd BinOp = "and" + BinOpOr BinOp = "or" + BinOpAs BinOp = "as" +) + +// Symbol returns the string representation of the binary operator +func (op BinOp) Symbol() string { + return string(op) +} + +// AllBinOps returns all possible BinOp values +func AllBinOps() []BinOp { + return []BinOp{ + BinOpAdd, + BinOpSub, + BinOpMul, + BinOpDiv, + BinOpMod, + BinOpPow, + BinOpFloorDiv, + BinOpLShift, + BinOpRShift, + BinOpBitXor, + BinOpBitAnd, + BinOpBitOr, + BinOpAnd, + BinOpOr, + BinOpAs, + } +} + +// BinOpFromSymbol returns the BinOp corresponding to the given symbol +func BinOpFromSymbol(symbol string) (BinOp, bool) { + switch symbol { + case "+": + return BinOpAdd, true + case "-": + return BinOpSub, true + case "*": + return BinOpMul, true + case "/": + return BinOpDiv, true + case "%": + return BinOpMod, true + case "**": + return BinOpPow, true + case "//": + return BinOpFloorDiv, true + case "<<": + return BinOpLShift, true + case ">>": + return BinOpRShift, true + case "^": + return BinOpBitXor, true + case "&": + return BinOpBitAnd, true + case "|": + return BinOpBitOr, true + case "and": + return BinOpAnd, true + case "or": + return BinOpOr, true + case "as": + return BinOpAs, true + default: + return "", false + } +} + +// CmpOp represents a comparison operator +type CmpOp string + +const ( + CmpOpEq CmpOp = "==" + CmpOpNotEq CmpOp = "!=" + CmpOpLt CmpOp = "<" + CmpOpLtE CmpOp = "<=" + CmpOpGt CmpOp = ">" + CmpOpGtE CmpOp = ">=" + CmpOpIs CmpOp = "is" + CmpOpIn CmpOp = "in" + CmpOpNotIn CmpOp = "not in" + CmpOpNot CmpOp = "not" + CmpOpIsNot CmpOp = "is not" +) + +// Symbol returns the string representation of the comparison operator +func (c CmpOp) Symbol() string { + return string(c) +} + +// AllCmpOps returns all possible CmpOp values +func AllCmpOps() []CmpOp { + return []CmpOp{ + CmpOpEq, + CmpOpNotEq, + CmpOpLt, + CmpOpLtE, + CmpOpGt, + CmpOpGtE, + CmpOpIs, + CmpOpIn, + CmpOpNotIn, + CmpOpNot, + CmpOpIsNot, + } +} + +// CmpOpFromString returns the CmpOp corresponding to the given string +func CmpOpFromString(s string) (CmpOp, bool) { + switch s { + case "==": + return CmpOpEq, true + case "!=": + return CmpOpNotEq, true + case "<": + return CmpOpLt, true + case "<=": + return CmpOpLtE, true + case ">": + return CmpOpGt, true + case ">=": + return CmpOpGtE, true + case "is": + return CmpOpIs, true + case "in": + return CmpOpIn, true + case "not in": + return CmpOpNotIn, true + case "not": + return CmpOpNot, true + case "is not": + return CmpOpIsNot, true + default: + return "", false + } +} + +// QuantOperation represents the operation of a quantifier expression +type QuantOperation string + +const ( + QuantOperationAll QuantOperation = "All" + QuantOperationAny QuantOperation = "Any" + QuantOperationFilter QuantOperation = "Filter" + QuantOperationMap QuantOperation = "Map" +) + +// String returns the string representation of the QuantOperation +func (qo QuantOperation) String() string { + return string(qo) +} + +// AllQuantOperations returns all possible QuantOperation values +func AllQuantOperations() []QuantOperation { + return []QuantOperation{ + QuantOperationAll, + QuantOperationAny, + QuantOperationFilter, + QuantOperationMap, + } +} + +// QuantOperationFromString returns the QuantOperation corresponding to the given string +func QuantOperationFromString(s string) (QuantOperation, bool) { + switch s { + case "All": + return QuantOperationAll, true + case "Any": + return QuantOperationAny, true + case "Filter": + return QuantOperationFilter, true + case "Map": + return QuantOperationMap, true + default: + return "", false + } +} + +// ConfigEntryOperation represents the operation of a configuration entry +type ConfigEntryOperation string + +const ( + ConfigEntryOperationUnion ConfigEntryOperation = "Union" + ConfigEntryOperationOverride ConfigEntryOperation = "Override" + ConfigEntryOperationInsert ConfigEntryOperation = "Insert" +) + +// String returns the string representation of the ConfigEntryOperation +func (c ConfigEntryOperation) String() string { + return string(c) +} + +// Value returns the integer value of the ConfigEntryOperation +func (c ConfigEntryOperation) Value() int { + switch c { + case ConfigEntryOperationUnion: + return 0 + case ConfigEntryOperationOverride: + return 1 + case ConfigEntryOperationInsert: + return 2 + default: + panic(fmt.Sprintf("unknown operation: %v", c)) + } +} + +// Symbol returns the symbol representation of the ConfigEntryOperation +func (c ConfigEntryOperation) Symbol() string { + switch c { + case ConfigEntryOperationUnion: + return ":" + case ConfigEntryOperationOverride: + return "=" + case ConfigEntryOperationInsert: + return "+=" + default: + panic(fmt.Sprintf("unknown operation: %v", c)) + } +} + +// ConfigEntryOperationFromString returns the ConfigEntryOperation corresponding to the given string +func ConfigEntryOperationFromString(s string) (ConfigEntryOperation, error) { + switch s { + case "Union": + return ConfigEntryOperationUnion, nil + case "Override": + return ConfigEntryOperationOverride, nil + case "Insert": + return ConfigEntryOperationInsert, nil + default: + return ConfigEntryOperation(-1), fmt.Errorf("unknown ConfigEntryOperation: %s", s) + } +} + +// AllConfigEntryOperations returns all possible ConfigEntryOperation values +func AllConfigEntryOperations() []ConfigEntryOperation { + return []ConfigEntryOperation{ + ConfigEntryOperationUnion, + ConfigEntryOperationOverride, + ConfigEntryOperationInsert, + } +} + +// NumberBinarySuffix represents the binary suffix of a number +type NumberBinarySuffix string + +const ( + NumberBinarySuffixN NumberBinarySuffix = "n" + NumberBinarySuffixU NumberBinarySuffix = "u" + NumberBinarySuffixM NumberBinarySuffix = "m" + NumberBinarySuffixK NumberBinarySuffix = "k" + NumberBinarySuffixKU NumberBinarySuffix = "K" + NumberBinarySuffixMU NumberBinarySuffix = "M" + NumberBinarySuffixG NumberBinarySuffix = "G" + NumberBinarySuffixT NumberBinarySuffix = "T" + NumberBinarySuffixP NumberBinarySuffix = "P" + NumberBinarySuffixKi NumberBinarySuffix = "Ki" + NumberBinarySuffixMi NumberBinarySuffix = "Mi" + NumberBinarySuffixGi NumberBinarySuffix = "Gi" + NumberBinarySuffixTi NumberBinarySuffix = "Ti" + NumberBinarySuffixPi NumberBinarySuffix = "Pi" +) + +// Value returns the string representation of the NumberBinarySuffix +func (n NumberBinarySuffix) Value() string { + return string(n) +} + +// AllNumberBinarySuffixes returns all possible NumberBinarySuffix values +func AllNumberBinarySuffixes() []NumberBinarySuffix { + return []NumberBinarySuffix{ + NumberBinarySuffixN, + NumberBinarySuffixU, + NumberBinarySuffixM, + NumberBinarySuffixK, + NumberBinarySuffixKU, + NumberBinarySuffixMU, + NumberBinarySuffixG, + NumberBinarySuffixT, + NumberBinarySuffixP, + NumberBinarySuffixKi, + NumberBinarySuffixMi, + NumberBinarySuffixGi, + NumberBinarySuffixTi, + NumberBinarySuffixPi, + } +} + +// AllNumberBinarySuffixNames returns all names of NumberBinarySuffix +func AllNumberBinarySuffixNames() []string { + return []string{"n", "u", "m", "k", "K", "M", "G", "T", "P", "Ki", "Mi", "Gi", "Ti", "Pi", "i"} +} + +// NumberBinarySuffixFromString returns the NumberBinarySuffix corresponding to the given string +func NumberBinarySuffixFromString(s string) (NumberBinarySuffix, bool) { + switch s { + case "n": + return NumberBinarySuffixN, true + case "u": + return NumberBinarySuffixU, true + case "m": + return NumberBinarySuffixM, true + case "k": + return NumberBinarySuffixK, true + case "K": + return NumberBinarySuffixKU, true + case "M": + return NumberBinarySuffixMU, true + case "G": + return NumberBinarySuffixG, true + case "T": + return NumberBinarySuffixT, true + case "P": + return NumberBinarySuffixP, true + case "Ki": + return NumberBinarySuffixKi, true + case "Mi": + return NumberBinarySuffixMi, true + case "Gi": + return NumberBinarySuffixGi, true + case "Ti": + return NumberBinarySuffixTi, true + case "Pi": + return NumberBinarySuffixPi, true + default: + return "", false + } +} + +// NameConstant represents a name constant, e.g. +// +// True +// False +// None +// Undefined +type NameConstant string + +const ( + NameConstantTrue NameConstant = "True" + NameConstantFalse NameConstant = "False" + NameConstantNone NameConstant = "None" + NameConstantUndefined NameConstant = "Undefined" +) + +// Symbol returns the symbol for each constant +func (n NameConstant) Symbol() string { + return string(n) +} + +// JSONValue returns the JSON value for each constant +func (n NameConstant) JSONValue() string { + switch n { + case NameConstantTrue: + return "true" + case NameConstantFalse: + return "false" + case NameConstantNone, NameConstantUndefined: + return "null" + default: + panic(fmt.Sprintf("unknown NameConstant: %s", n)) + } +} + +// MarshalJSON implements custom JSON marshaling for NameConstant +func (n NameConstant) MarshalJSON() ([]byte, error) { + return json.Marshal(n.Symbol()) +} + +// UnmarshalJSON implements custom JSON unmarshaling for NameConstant +func (n *NameConstant) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + switch s { + case "True": + *n = NameConstantTrue + case "False": + *n = NameConstantFalse + case "None": + *n = NameConstantNone + case "Undefined": + *n = NameConstantUndefined + default: + return fmt.Errorf("unknown NameConstant: %s", s) + } + return nil +} + +// AllNameConstants returns all possible NameConstant values +func AllNameConstants() []NameConstant { + return []NameConstant{ + NameConstantTrue, + NameConstantFalse, + NameConstantNone, + NameConstantUndefined, + } +} diff --git a/pkg/ast/stmt.go b/pkg/ast/stmt.go new file mode 100644 index 00000000..ba35550d --- /dev/null +++ b/pkg/ast/stmt.go @@ -0,0 +1,282 @@ +package ast + +// Stmt is an interface for all statement types +type Stmt interface { + Type() string +} + +// BaseStmt is a struct that all statement types can embed to implement the Stmt interface +type BaseStmt struct { + StmtType string `json:"type"` +} + +func (b BaseStmt) Type() string { + return b.StmtType +} + +// Define all the statement types +type ( + // TypeAliasStmt represents a type alias statement, e.g. + // + // type StrOrInt = str | int + TypeAliasStmt struct { + BaseStmt + TypeName *Node[Identifier] `json:"type_name"` + TypeValue *Node[string] `json:"type_value"` + Ty *Node[Type] `json:"ty"` + } + // ExprStmt represents a expression statement, e.g. + // + // 1 + // + // """A long string""" + // + // 'A string' + ExprStmt struct { + BaseStmt + Exprs []*Node[Expr] `json:"exprs"` + } + // UnificationStmt represents a declare statement with the union operator, e.g. + // + // data: ASchema {} + UnificationStmt struct { + BaseStmt + Target *Node[Identifier] `json:"target"` + Value *Node[SchemaConfig] `json:"value"` + } + // AssignStmt represents an assignment, e.g. + // + // a: int = 1 + // + // a = 1 + // + // a = b = 1 + AssignStmt struct { + BaseStmt + Targets []*Node[Target] `json:"targets"` + Value *Node[Expr] `json:"value"` + Ty *Node[Type] `json:"ty"` + } + // AugAssignStmt represents an augmented assignment, e.g. + // + // a += 1 + // + // a -= 1 + AugAssignStmt struct { + BaseStmt + Target *Node[Target] `json:"target"` + Value *Node[Expr] `json:"value"` + Op AugOp `json:"op"` + } + // AssertStmt represents an assert statement, e.g. + // + // assert True if condition, "Assert failed message" + AssertStmt struct { + BaseStmt + Test *Node[Expr] `json:"test"` + IfCond *Node[Expr] `json:"if_cond,omitempty"` + Msg *Node[Expr] `json:"msg,omitempty"` + } + // IfStmt represents an if statement, e.g. + // + // if condition1: + // + // if condition2: + // a = 1 + // + // elif condition3: + // + // b = 2 + // + // else: + // + // c = 3 + IfStmt struct { + BaseStmt + Body []*Node[Stmt] `json:"body"` + Cond *Node[Expr] `json:"cond"` + Orelse []*Node[Stmt] `json:"orelse,omitempty"` + } + // ImportStmt represents an import statement, e.g. + // + // import pkg as pkg_alias + ImportStmt struct { + BaseStmt + Path *Node[string] `json:"path"` + Rawpath string `json:"rawpath"` + Name string `json:"name"` + Asname *Node[string] `json:"asname,omitempty"` + PkgName string `json:"pkg_name"` + } + // SchemaAttr represents schema attribute definitions, e.g. + // + // schema SchemaAttrExample: + // + // x: int + // y: str + SchemaAttr struct { + BaseStmt + Doc string `json:"doc,omitempty"` + Name *Node[string] `json:"name"` + Op AugOp `json:"op,omitempty"` + Value *Node[Expr] `json:"value,omitempty"` + IsOptional bool `json:"is_optional"` + Decorators []*Node[Decorator] `json:"decorators,omitempty"` + Ty *Node[Type] `json:"ty,omitempty"` + } + // SchemaStmt represents a schema statement, e.g. + // + // schema BaseSchema: + // + // schema SchemaExample(BaseSchema)[arg: str]: + // + // """Schema documents""" + // attr?: str = arg + // check: + // len(attr) > 3 if attr, "Check failed message" + // + // mixin MixinExample for ProtocolExample: + // + // attr: int + // + // protocol ProtocolExample: + // + // attr: int + SchemaStmt struct { + BaseStmt + Doc *Node[string] `json:"doc,omitempty"` + Name *Node[string] `json:"name"` + ParentName *Node[Identifier] `json:"parent_name,omitempty"` + ForHostName *Node[Identifier] `json:"for_host_name,omitempty"` + IsMixin bool `json:"is_mixin"` + IsProtocol bool `json:"is_protocol"` + Args *Node[Arguments] `json:"args,omitempty"` + Mixins []*Node[Identifier] `json:"mixins,omitempty"` + Body []*Node[Stmt] `json:"body,omitempty"` + Decorators []*Node[Decorator] `json:"decorators,omitempty"` + Checks []*Node[CheckExpr] `json:"checks,omitempty"` + IndexSignature *Node[SchemaIndexSignature] `json:"index_signature,omitempty"` + } + // RuleStmt represents a rule statement, e.g. + // + // rule RuleExample: + // + // a > 1 + // b < 0 + RuleStmt struct { + BaseStmt + Doc *Node[string] `json:"doc,omitempty"` + Name *Node[string] `json:"name"` + ParentRules []*Node[Identifier] `json:"parent_rules,omitempty"` + Decorators []*Node[Decorator] `json:"decorators,omitempty"` + Checks []*Node[CheckExpr] `json:"checks,omitempty"` + Args *Node[Arguments] `json:"args,omitempty"` + ForHostName *Node[Identifier] `json:"for_host_name,omitempty"` + } +) + +// NewTypeAliasStmt creates a new TypeAliasStmt +func NewTypeAliasStmt() *TypeAliasStmt { + return &TypeAliasStmt{ + BaseStmt: BaseStmt{StmtType: "TypeAlias"}, + } +} + +// NewExprStmt creates a new ExprStmt +func NewExprStmt() *ExprStmt { + return &ExprStmt{ + BaseStmt: BaseStmt{StmtType: "Expr"}, + Exprs: make([]*Node[Expr], 0), + } +} + +// NewUnificationStmt creates a new UnificationStmt +func NewUnificationStmt() *UnificationStmt { + return &UnificationStmt{ + BaseStmt: BaseStmt{StmtType: "Unification"}, + } +} + +// NewAssignStmt creates a new AssignStmt +func NewAssignStmt() *AssignStmt { + return &AssignStmt{ + BaseStmt: BaseStmt{StmtType: "Assign"}, + } +} + +// NewAugAssignStmt creates a new AugAssignStmt +func NewAugAssignStmt() *AugAssignStmt { + return &AugAssignStmt{ + BaseStmt: BaseStmt{StmtType: "AugAssign"}, + } +} + +// NewAssertStmt creates a new AssertStmt +func NewAssertStmt() *AssertStmt { + return &AssertStmt{ + BaseStmt: BaseStmt{StmtType: "Assert"}, + } +} + +// NewIfStmt creates a new IfStmt +func NewIfStmt() *IfStmt { + return &IfStmt{ + BaseStmt: BaseStmt{StmtType: "If"}, + Body: make([]*Node[Stmt], 0), + Orelse: make([]*Node[Stmt], 0), + } +} + +// NewImportStmt creates a new ImportStmt +func NewImportStmt() *ImportStmt { + return &ImportStmt{ + BaseStmt: BaseStmt{StmtType: "Import"}, + } +} + +// NewSchemaAttr creates a new SchemaAttr +func NewSchemaAttr() *SchemaAttr { + return &SchemaAttr{ + BaseStmt: BaseStmt{StmtType: "SchemaAttr"}, + Decorators: make([]*Node[Decorator], 0), + } +} + +// NewSchemaStmt creates a new SchemaStmt +func NewSchemaStmt() *SchemaStmt { + return &SchemaStmt{ + BaseStmt: BaseStmt{StmtType: "Schema"}, + Mixins: make([]*Node[Identifier], 0), + Body: make([]*Node[Stmt], 0), + Decorators: make([]*Node[Decorator], 0), + Checks: make([]*Node[CheckExpr], 0), + } +} + +// NewRuleStmt creates a new RuleStmt +func NewRuleStmt() *RuleStmt { + return &RuleStmt{ + BaseStmt: BaseStmt{StmtType: "Rule"}, + ParentRules: make([]*Node[Identifier], 0), + Decorators: make([]*Node[Decorator], 0), + Checks: make([]*Node[CheckExpr], 0), + } +} + +// SchemaIndexSignature represents a schema index signature, e.g. +// +// schema SchemaIndexSignatureExample: +// +// [str]: int +type SchemaIndexSignature struct { + KeyName *Node[string] `json:"key_name,omitempty"` + Value *Node[Expr] `json:"value,omitempty"` + AnyOther bool `json:"any_other"` + KeyTy *Node[Type] `json:"key_ty,omitempty"` + ValueTy *Node[Type] `json:"value_ty,omitempty"` +} + +// NewSchemaIndexSignature creates a new SchemaIndexSignature +func NewSchemaIndexSignature() *SchemaIndexSignature { + return &SchemaIndexSignature{} +} diff --git a/pkg/ast/type.go b/pkg/ast/type.go new file mode 100644 index 00000000..2bc78035 --- /dev/null +++ b/pkg/ast/type.go @@ -0,0 +1,109 @@ +package ast + +// Type is the base interface for all AST types +type Type interface { + TypeName() string +} + +// NamedType represents a named type +type NamedType struct { + Value struct { + Identifier *Identifier `json:"identifier"` + } `json:"value"` +} + +func (n *NamedType) TypeName() string { return "Named" } + +// AnyType represents a the any type +type AnyType struct{} + +func (n *AnyType) TypeName() string { return "Any" } + +// BasicType represents a basic type +type BasicType struct { + Value BasicTypeEnum `json:"value"` +} + +func (b *BasicType) TypeName() string { return "Basic" } + +type BasicTypeEnum string + +const ( + Bool BasicTypeEnum = "Bool" + Int BasicTypeEnum = "Int" + Float BasicTypeEnum = "Float" + Str BasicTypeEnum = "Str" +) + +// ListType represents a list type +type ListType struct { + Value struct { + InnerType *Node[Type] `json:"inner_type,omitempty"` + } `json:"value"` +} + +func (l *ListType) TypeName() string { return "List" } + +// DictType represents a dictionary type +type DictType struct { + Value struct { + KeyType *Node[Type] `json:"key_type,omitempty"` + ValueType *Node[Type] `json:"value_type,omitempty"` + } `json:"value"` +} + +func (d *DictType) TypeName() string { return "Dict" } + +// UnionType represents a union type +type UnionType struct { + Value struct { + TypeElements []*Node[Type] `json:"type_elements"` + } `json:"value"` +} + +func (u *UnionType) TypeName() string { return "Union" } + +// LiteralType represents a literal type +type LiteralType struct { + Value LiteralTypeValue `json:"value"` +} + +func (l *LiteralType) TypeName() string { return "Literal" } + +// LiteralTypeValue is an interface for different literal types +type LiteralTypeValue interface { + LiteralTypeName() string +} + +// BoolLiteralType represents a boolean literal type +type BoolLiteralType bool + +func (b *BoolLiteralType) LiteralTypeName() string { return "Bool" } + +// IntLiteralType represents an integer literal type +type IntLiteralType struct { + Value int `json:"value"` + Suffix *NumberBinarySuffix `json:"binary_suffix,omitempty"` +} + +func (i *IntLiteralType) LiteralTypeName() string { return "Int" } + +// FloatLiteralType represents a float literal type +type FloatLiteralType float64 + +func (f *FloatLiteralType) LiteralTypeName() string { return "Float" } + +// StrLiteralType represents a string literal type +type StrLiteralType string + +func (s *StrLiteralType) LiteralTypeName() string { return "Str" } + +// FunctionType represents a function type +type FunctionType struct { + Value struct { + ParamsTy []*Node[Type] `json:"paramsTy,omitempty"` + RetTy *Node[Type] `json:"retTy,omitempty"` + } `json:"value"` +} + +func (f *FunctionType) TypeName() string { return "Function" } diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 080f7077..50abd0e7 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -1,9 +1,11 @@ package parser import ( + "encoding/json" "fmt" "io" + "kcl-lang.io/kcl-go/pkg/ast" "kcl-lang.io/kcl-go/pkg/kcl" "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" ) @@ -45,6 +47,24 @@ func ParseFileASTJson(filename string, src interface{}) (result string, err erro return resp.AstJson, nil } +// ParseFile parses the source code from the specified file or Reader +// and returns the Go structure representation of the Abstract Syntax +// Tree (AST). The source code can be provided directly as a string or +// []byte, or indirectly via a filename or an io.Reader. If src is nil, +// the function reads the content from the provided filename. +func ParseFile(filename string, src interface{}) (m *ast.Module, err error) { + astJson, err := ParseFileASTJson(filename, src) + if err != nil { + return nil, err + } + m = ast.NewModule() + err = json.Unmarshal([]byte(astJson), m) + if err != nil { + return nil, err + } + return +} + // Parse KCL program with entry files and return the AST JSON string. func ParseProgram(args *ParseProgramArgs) (*ParseProgramResult, error) { svc := kcl.Service() diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go index 79633ce0..2556935a 100644 --- a/pkg/parser/parser_test.go +++ b/pkg/parser/parser_test.go @@ -1,11 +1,110 @@ package parser import ( + "os" + "path/filepath" "strings" "testing" "time" + + "kcl-lang.io/kcl-go/pkg/ast" ) +func TestParseFile(t *testing.T) { + // Example: Test with string source + src := `schema Name: + name: str + +a: int = 1 +b = 2Ki +c = 1 + 1 +d = "123" +e: 123 = 123 +f: "Red" | "Blue" = "Red" +n1 = Name() +n2 = Name {name = "name"} +n3: Name {name = "name"} +schema Container: + name: str = "main" + command?: [str] + ports?: [ContainerPort] + +schema Person: + name?: any + +version = "dev" + +appConfiguration = xxx.xxxAppConfiguration { + mainContainer = container.Main { + readinessProbe = probe_tpl.defaultReadinessProbe + env : [ + e.Env { + name: "a" + value: "b" + }, + ] + values._envs + } +} +` // Sample KCL source code + module, err := ParseFile("", src) + if err != nil { + t.Errorf("ParseFile failed with string source: %v and error: %v", src, err) + } + if module == nil { + t.Errorf("Expected non-empty AST JSON with string source") + } else { + schemaStmt := module.Body[0].Node.(*ast.SchemaStmt) + if len(schemaStmt.Body) != 1 { + t.Errorf("wrong schema stmt body count") + } + simpleAssignStmt := module.Body[1].Node.(*ast.AssignStmt) + if simpleAssignStmt.Value.Node.(*ast.NumberLit).Value.(*ast.IntNumberLitValue).Value != 1 { + t.Errorf("wrong assign stmt literal value") + } + schemaAssignStmt := module.Body[8].Node.(*ast.AssignStmt) + if len(schemaAssignStmt.Value.Node.(*ast.SchemaExpr).Config.Node.(*ast.ConfigExpr).Items) != 1 { + t.Errorf("wrong assign stmt schema entry count") + } + schemaUnificationStmt := module.Body[9].Node.(*ast.UnificationStmt) + if len(schemaUnificationStmt.Value.Node.Config.Node.(*ast.ConfigExpr).Items) != 1 { + t.Errorf("wrong assign stmt schema entry count") + } + } +} + +func TestParseFileInTheWholeRepo(t *testing.T) { + root := filepath.Join(".", "..", "..") + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && strings.HasSuffix(info.Name(), ".k") { + testParseFile(t, path) + } + return nil + }) + if err != nil { + t.Errorf("Error walking the path %v: %v", root, err) + } +} + +func testParseFile(t *testing.T, path string) { + var err error + var content string + + t.Logf("Start parse file: %s", path) + module, err := ParseFile(path, content) + if err != nil { + t.Errorf("ParseFile failed for %s: %v", path, err) + return + } + if module == nil { + t.Errorf("Expected non-empty AST JSON for %s", path) + return + } + t.Logf("Successfully parsed file: %s", path) +} + // TestParseFileASTJson tests the ParseFileASTJson function with various input sources. func TestParseFileASTJson(t *testing.T) { // Example: Test with string source