From 910011555128012ca2d74f5ce09537a3471617e3 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Fri, 5 Jun 2026 20:56:08 +0200 Subject: [PATCH 01/16] added generation of type specific 'MarshalJSON' & 'UnmarshalJSON' implementations * also added generic 'MarshalJSON' & 'UnmarshalJSON' implementation for 'Reference' --- cmd/fastbelt/generate.go | 4 + examples/statemachine/json_gen.go | 162 +++++++ internal/generator/json_generator.go | 154 ++++++ internal/grammar/json_gen.go | 557 ++++++++++++++++++++++ internal/languages/completion/json_gen.go | 271 +++++++++++ reference.go | 81 ++++ 6 files changed, 1229 insertions(+) create mode 100644 examples/statemachine/json_gen.go create mode 100644 internal/generator/json_generator.go create mode 100644 internal/grammar/json_gen.go create mode 100644 internal/languages/completion/json_gen.go diff --git a/cmd/fastbelt/generate.go b/cmd/fastbelt/generate.go index 8ea31df4..5033be07 100644 --- a/cmd/fastbelt/generate.go +++ b/cmd/fastbelt/generate.go @@ -129,6 +129,10 @@ func runGenerateCLI(opts generateOptions) error { generator.GenerateTypes(grammar, packageName)); err != nil { return err } + if err := writeFile("json", filepath.Join(outputPath, "json_gen.go"), + generator.GenerateJSON(grammar, packageName)); err != nil { + return err + } tokenTypes := generator.GenerateTokenTypes(grammar) atnData := generator.BuildParserATNData(grammar, tokenTypes) if err := writeFile("parser", filepath.Join(outputPath, "parser_gen.go"), diff --git a/examples/statemachine/json_gen.go b/examples/statemachine/json_gen.go new file mode 100644 index 00000000..2f9875f0 --- /dev/null +++ b/examples/statemachine/json_gen.go @@ -0,0 +1,162 @@ +// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT. + +package statemachine + +import ( + "encoding/json" + + core "typefox.dev/fastbelt" +) + +func (i *StatemachineData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Events []Event `json:"events"` + Commands []Command `json:"commands"` + Init *core.Reference[State] `json:"init"` + States []State `json:"states"` + }{ + T__: "Statemachine", + Name: i.Name(), + Events: i.Events(), + Commands: i.Commands(), + Init: i.Init(), + States: i.States(), + }) +} + +func (i *EventData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{ + T__: "Event", + Name: i.Name(), + }) +} + +func (i *CommandData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{ + T__: "Command", + Name: i.Name(), + }) +} + +func (i *StateData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Actions []*core.Reference[Command] `json:"actions"` + Transitions []Transition `json:"transitions"` + }{ + T__: "State", + Name: i.Name(), + Actions: i.Actions(), + Transitions: i.Transitions(), + }) +} + +func (i *TransitionData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Event *core.Reference[Event] `json:"event"` + State *core.Reference[State] `json:"state"` + }{ + T__: "Transition", + Event: i.Event(), + State: i.State(), + }) +} + +func (i *StatemachineData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Events []*EventImpl `json:"events"` + Commands []*CommandImpl `json:"commands"` + Init *core.Reference[State] `json:"init"` + States []*StateImpl `json:"states"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + i.events = []Event{} + for _, item := range aux.Events { + i.SetEventsItem(item) + } + i.commands = []Command{} + for _, item := range aux.Commands { + i.SetCommandsItem(item) + } + i.SetInit(aux.Init) + i.states = []State{} + for _, item := range aux.States { + i.SetStatesItem(item) + } + return nil +} + +func (i *EventData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + return nil +} + +func (i *CommandData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + return nil +} + +func (i *StateData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Actions []*core.Reference[Command] `json:"actions"` + Transitions []*TransitionImpl `json:"transitions"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + i.actions = []*core.Reference[Command]{} + for _, item := range aux.Actions { + i.SetActionsItem(item) + } + i.transitions = []Transition{} + for _, item := range aux.Transitions { + i.SetTransitionsItem(item) + } + return nil +} + +func (i *TransitionData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Event *core.Reference[Event] `json:"event"` + State *core.Reference[State] `json:"state"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetEvent(aux.Event) + i.SetState(aux.State) + return nil +} diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go new file mode 100644 index 00000000..83a8430f --- /dev/null +++ b/internal/generator/json_generator.go @@ -0,0 +1,154 @@ +// Copyright 2025 TypeFox GmbH +// This program and the accompanying materials are made available under the +// terms of the MIT License, which is available in the project root. + +package generator + +import ( + "strings" + + "typefox.dev/fastbelt/internal/grammar" + "typefox.dev/fastbelt/util/codegen" +) + +func GenerateJSON(grammr grammar.Grammar, packageName string) string { + node := codegen.NewNode() + node.AppendLine("// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT.") + node.AppendLine() + node.AppendLine("package ", packageName) + node.AppendLine() + + node.AppendLine("import (") + node.Indent(func(n codegen.Node) { + n.AppendLine("\"encoding/json\"") + n.AppendLine() + n.AppendLine("core \"typefox.dev/fastbelt\"") + }) + node.AppendLine(")") + node.AppendLine() + + for _, iface := range grammr.Interfaces() { + generateJSONMarshal(node, iface) + } + + for _, iface := range grammr.Interfaces() { + generateJSONUnmarshal(node, iface) + } + + return FormatIfPossible(node.String()) +} + +func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { + fields := []FieldInfo{} + for _, field := range iface.Fields() { + fields = append(fields, getFieldInfo(field)) + } + + node.AppendLine("func (i *", iface.Name(), "Data) MarshalJSON() ([]byte, error) {") + node.Indent(func(n codegen.Node) { + n.AppendLine("return json.Marshal(struct {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("T__", " ", "string", " `json:\"$type\"`") + for _, field := range fields { + jsonTag := strings.ToLower(field.Name[:1]) + field.Name[1:] + var typeStr string + if field.Array { + typeStr = "[]" + field.GType + } else { + typeStr = field.Type + } + n2.AppendLine(field.Name, " ", typeStr, " `json:\"", jsonTag, "\"`") + } + }) + n.AppendLine("}{") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("T__: ", "\"", iface.Name(), "\",") + for _, field := range fields { + getterName := field.Name + if field.Boolean && !field.Array { + getterName = "Is" + field.Name + } + n2.AppendLine(field.Name, ": i.", getterName, "(),") + } + }) + n.AppendLine("})") + }) + node.AppendLine("}") + node.AppendLine() +} + +func getAuxFieldType(field FieldInfo) string { + if field.Reference { + if field.Array { + return "[]" + field.GType + } + return field.Type + } + if field.Array { + if field.Boolean { + return "[]bool" + } + if field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE { + return "[]string" + } + return "[]*" + field.GType + "Impl" + } + if field.Boolean { + return "bool" + } + if field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE { + return "string" + } + return "*" + field.GType + "Impl" +} + +func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { + fields := []FieldInfo{} + for _, field := range iface.Fields() { + fields = append(fields, getFieldInfo(field)) + } + + node.AppendLine("func (i *", iface.Name(), "Data) UnmarshalJSON(data []byte) error {") + node.Indent(func(n codegen.Node) { + n.AppendLine("aux := &struct {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("T__", " ", "string", " `json:\"$type\"`") + for _, field := range fields { + jsonTag := strings.ToLower(field.Name[:1]) + field.Name[1:] + n2.AppendLine(field.Name, " ", getAuxFieldType(field), " `json:\"", jsonTag, "\"`") + } + }) + n.AppendLine("}{}") + n.AppendLine("if err := json.Unmarshal(data, aux); err != nil {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("return err") + }) + n.AppendLine("}") + + hasComposeNodeTempVar := false + for _, field := range fields { + if field.Array { + n.AppendLine("i.", field.PName, " = []", field.GType, "{}") + n.AppendLine("for _, item := range aux.", field.Name, " {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("i.Set", field.Name, "Item(item)") + }) + n.AppendLine("}") + } else if field.Boolean || field.HasTokenGetter { + n.AppendLine("i.Set", field.Name, "(&core.Token{Image: aux.", field.Name, "})") + } else if field.HasNodeGetter { + if !hasComposeNodeTempVar { + n.AppendLine("var cn core.CompositeNode") + } + n.AppendLine("cn = core.NewCompositeNode()") + n.AppendLine("cn.SetToken(&core.Token{Image: aux.", field.Name, "})") + n.AppendLine("i.Set", field.Name, "(cn)") + } else { + n.AppendLine("i.Set", field.Name, "(aux.", field.Name, ")") + } + } + n.AppendLine("return nil") + }) + node.AppendLine("}") + node.AppendLine() +} diff --git a/internal/grammar/json_gen.go b/internal/grammar/json_gen.go new file mode 100644 index 00000000..cc79285a --- /dev/null +++ b/internal/grammar/json_gen.go @@ -0,0 +1,557 @@ +// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT. + +package grammar + +import ( + "encoding/json" + + core "typefox.dev/fastbelt" +) + +func (i *GrammarData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Rules []ParserRule `json:"rules"` + Composites []CompositeRule `json:"composites"` + Terminals []Token `json:"terminals"` + Interfaces []Interface `json:"interfaces"` + }{ + T__: "Grammar", + Name: i.Name(), + Rules: i.Rules(), + Composites: i.Composites(), + Terminals: i.Terminals(), + Interfaces: i.Interfaces(), + }) +} + +func (i *InterfaceData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Extends []*core.Reference[Interface] `json:"extends"` + Fields []Field `json:"fields"` + }{ + T__: "Interface", + Name: i.Name(), + Extends: i.Extends(), + Fields: i.Fields(), + }) +} + +func (i *FieldData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Type FieldType `json:"type"` + }{ + T__: "Field", + Name: i.Name(), + Type: i.Type(), + }) +} + +func (i *FieldTypeData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + }{ + T__: "FieldType", + }) +} + +func (i *ArrayTypeData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + InternalType FieldType `json:"internalType"` + }{ + T__: "ArrayType", + InternalType: i.InternalType(), + }) +} + +func (i *ReferenceTypeData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + }{ + T__: "ReferenceType", + Type: i.Type(), + }) +} + +func (i *SimpleTypeData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + }{ + T__: "SimpleType", + Type: i.Type(), + }) +} + +func (i *PrimitiveTypeData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Type string `json:"type"` + }{ + T__: "PrimitiveType", + Type: i.Type(), + }) +} + +func (i *AbstractRuleData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{ + T__: "AbstractRule", + Name: i.Name(), + }) +} + +func (i *AbstractRuleWithBodyData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Body Element `json:"body"` + }{ + T__: "AbstractRuleWithBody", + Body: i.Body(), + }) +} + +func (i *ParserRuleData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + ReturnType *core.Reference[Interface] `json:"returnType"` + }{ + T__: "ParserRule", + ReturnType: i.ReturnType(), + }) +} + +func (i *TokenData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Type string `json:"type"` + Regexp string `json:"regexp"` + }{ + T__: "Token", + Type: i.Type(), + Regexp: i.Regexp(), + }) +} + +func (i *ElementData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + }{ + T__: "Element", + Cardinality: i.Cardinality(), + }) +} + +func (i *AlternativesData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Alts []Element `json:"alts"` + }{ + T__: "Alternatives", + Alts: i.Alts(), + }) +} + +func (i *GroupData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Elements []Element `json:"elements"` + }{ + T__: "Group", + Elements: i.Elements(), + }) +} + +func (i *KeywordData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Value string `json:"value"` + }{ + T__: "Keyword", + Value: i.Value(), + }) +} + +func (i *AssignmentData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Property *core.Reference[Field] `json:"property"` + Operator string `json:"operator"` + Value Assignable `json:"value"` + }{ + T__: "Assignment", + Property: i.Property(), + Operator: i.Operator(), + Value: i.Value(), + }) +} + +func (i *AssignableData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + }{ + T__: "Assignable", + }) +} + +func (i *CrossRefData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + Rule RuleCall `json:"rule"` + }{ + T__: "CrossRef", + Type: i.Type(), + Rule: i.Rule(), + }) +} + +func (i *RuleCallData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Rule *core.Reference[AbstractRule] `json:"rule"` + }{ + T__: "RuleCall", + Rule: i.Rule(), + }) +} + +func (i *ActionData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + Operator string `json:"operator"` + Property *core.Reference[Field] `json:"property"` + }{ + T__: "Action", + Type: i.Type(), + Operator: i.Operator(), + Property: i.Property(), + }) +} + +func (i *CompositeRuleData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + }{ + T__: "CompositeRule", + }) +} + +func (i *GrammarData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Rules []*ParserRuleImpl `json:"rules"` + Composites []*CompositeRuleImpl `json:"composites"` + Terminals []*TokenImpl `json:"terminals"` + Interfaces []*InterfaceImpl `json:"interfaces"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + i.rules = []ParserRule{} + for _, item := range aux.Rules { + i.SetRulesItem(item) + } + i.composites = []CompositeRule{} + for _, item := range aux.Composites { + i.SetCompositesItem(item) + } + i.terminals = []Token{} + for _, item := range aux.Terminals { + i.SetTerminalsItem(item) + } + i.interfaces = []Interface{} + for _, item := range aux.Interfaces { + i.SetInterfacesItem(item) + } + return nil +} + +func (i *InterfaceData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Extends []*core.Reference[Interface] `json:"extends"` + Fields []*FieldImpl `json:"fields"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + i.extends = []*core.Reference[Interface]{} + for _, item := range aux.Extends { + i.SetExtendsItem(item) + } + i.fields = []Field{} + for _, item := range aux.Fields { + i.SetFieldsItem(item) + } + return nil +} + +func (i *FieldData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Type *FieldTypeImpl `json:"type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + i.SetType(aux.Type) + return nil +} + +func (i *FieldTypeData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + return nil +} + +func (i *ArrayTypeData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + InternalType *FieldTypeImpl `json:"internalType"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetInternalType(aux.InternalType) + return nil +} + +func (i *ReferenceTypeData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetType(aux.Type) + return nil +} + +func (i *SimpleTypeData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetType(aux.Type) + return nil +} + +func (i *PrimitiveTypeData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Type string `json:"type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetType(&core.Token{Image: aux.Type}) + return nil +} + +func (i *AbstractRuleData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(&core.Token{Image: aux.Name}) + return nil +} + +func (i *AbstractRuleWithBodyData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Body *ElementImpl `json:"body"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetBody(aux.Body) + return nil +} + +func (i *ParserRuleData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + ReturnType *core.Reference[Interface] `json:"returnType"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetReturnType(aux.ReturnType) + return nil +} + +func (i *TokenData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Type string `json:"type"` + Regexp string `json:"regexp"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetType(&core.Token{Image: aux.Type}) + i.SetRegexp(&core.Token{Image: aux.Regexp}) + return nil +} + +func (i *ElementData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetCardinality(&core.Token{Image: aux.Cardinality}) + return nil +} + +func (i *AlternativesData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Alts []*ElementImpl `json:"alts"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.alts = []Element{} + for _, item := range aux.Alts { + i.SetAltsItem(item) + } + return nil +} + +func (i *GroupData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Elements []*ElementImpl `json:"elements"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.elements = []Element{} + for _, item := range aux.Elements { + i.SetElementsItem(item) + } + return nil +} + +func (i *KeywordData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Value string `json:"value"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetValue(&core.Token{Image: aux.Value}) + return nil +} + +func (i *AssignmentData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Property *core.Reference[Field] `json:"property"` + Operator string `json:"operator"` + Value *AssignableImpl `json:"value"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetProperty(aux.Property) + i.SetOperator(&core.Token{Image: aux.Operator}) + i.SetValue(aux.Value) + return nil +} + +func (i *AssignableData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + return nil +} + +func (i *CrossRefData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + Rule *RuleCallImpl `json:"rule"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetType(aux.Type) + i.SetRule(aux.Rule) + return nil +} + +func (i *RuleCallData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Rule *core.Reference[AbstractRule] `json:"rule"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRule(aux.Rule) + return nil +} + +func (i *ActionData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Type *core.Reference[Interface] `json:"type"` + Operator string `json:"operator"` + Property *core.Reference[Field] `json:"property"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetType(aux.Type) + i.SetOperator(&core.Token{Image: aux.Operator}) + i.SetProperty(aux.Property) + return nil +} + +func (i *CompositeRuleData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + return nil +} diff --git a/internal/languages/completion/json_gen.go b/internal/languages/completion/json_gen.go new file mode 100644 index 00000000..bdfa377b --- /dev/null +++ b/internal/languages/completion/json_gen.go @@ -0,0 +1,271 @@ +// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT. + +package completion + +import ( + "encoding/json" + + core "typefox.dev/fastbelt" +) + +func (i *ObjData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + }{ + T__: "Obj", + }) +} + +func (i *RootData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Objects []Obj `json:"objects"` + }{ + T__: "Root", + Objects: i.Objects(), + }) +} + +func (i *DeclareData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Children []Declare `json:"children"` + }{ + T__: "Declare", + Name: i.Name(), + Children: i.Children(), + }) +} + +func (i *EData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{ + T__: "E", + Ref: i.Ref(), + }) +} + +func (i *FData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Items []FItem `json:"items"` + }{ + T__: "F", + Items: i.Items(), + }) +} + +func (i *FItemData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{ + T__: "FItem", + Ref: i.Ref(), + }) +} + +func (i *GData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{ + T__: "G", + Ref: i.Ref(), + }) +} + +func (i *HData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Member MemberCall `json:"member"` + }{ + T__: "H", + Member: i.Member(), + }) +} + +func (i *MemberCallData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + Previous MemberCall `json:"previous"` + }{ + T__: "MemberCall", + Ref: i.Ref(), + Previous: i.Previous(), + }) +} + +func (i *JData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{ + T__: "J", + Ref: i.Ref(), + }) +} + +func (i *KData) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref1 *core.Reference[Declare] `json:"ref1"` + Ref2 *core.Reference[Declare] `json:"ref2"` + }{ + T__: "K", + Ref1: i.Ref1(), + Ref2: i.Ref2(), + }) +} + +func (i *ObjData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + return nil +} + +func (i *RootData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Objects []*ObjImpl `json:"objects"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.objects = []Obj{} + for _, item := range aux.Objects { + i.SetObjectsItem(item) + } + return nil +} + +func (i *DeclareData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Children []*DeclareImpl `json:"children"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + var cn core.CompositeNode + cn = core.NewCompositeNode() + cn.SetToken(&core.Token{Image: aux.Name}) + i.SetName(cn) + i.children = []Declare{} + for _, item := range aux.Children { + i.SetChildrenItem(item) + } + return nil +} + +func (i *EData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRef(aux.Ref) + return nil +} + +func (i *FData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Items []*FItemImpl `json:"items"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.items = []FItem{} + for _, item := range aux.Items { + i.SetItemsItem(item) + } + return nil +} + +func (i *FItemData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRef(aux.Ref) + return nil +} + +func (i *GData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRef(aux.Ref) + return nil +} + +func (i *HData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Member *MemberCallImpl `json:"member"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetMember(aux.Member) + return nil +} + +func (i *MemberCallData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + Previous *MemberCallImpl `json:"previous"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRef(aux.Ref) + i.SetPrevious(aux.Previous) + return nil +} + +func (i *JData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRef(aux.Ref) + return nil +} + +func (i *KData) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref1 *core.Reference[Declare] `json:"ref1"` + Ref2 *core.Reference[Declare] `json:"ref2"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRef1(aux.Ref1) + i.SetRef2(aux.Ref2) + return nil +} diff --git a/reference.go b/reference.go index 2cad32e0..aafc4445 100644 --- a/reference.go +++ b/reference.go @@ -6,6 +6,8 @@ package fastbelt import ( "context" + "encoding/json" + "errors" "iter" "reflect" "slices" @@ -292,3 +294,82 @@ func NewReferenceDescriptionsFromMap(descriptions collections.MultiMap[AstNode, descriptions: descriptions, } } + +// MarshalJSON serializes the reference as a JSON object containing the URI of the resolved target's document. +// With this function Reference[T] implements json.Marshaler.MarshalJSON() and the method is called by `json.Marshal()` and `json.MarshalIndent()`. +func (r *Reference[T]) MarshalJSON() ([]byte, error) { + // A nil reference marshals as JSON null. + if r == nil { + return []byte("null"), nil + } + // We expect at this point that all references have been attempted to resolve. + if !r.resolved.Load() { + return nil, errors.New("reference not resolved") + } + + var uri string + var errMsg string + if r.err != nil { + errMsg = r.err.Msg + } else if doc := r.ref.Document(); doc != nil { + uri = doc.URI.WithFragment("todo-fragment").StringUnencoded() + } else { + return nil, errors.New("unexpected state: resolved reference has no document") + } + + return json.Marshal(struct { + RefText string `json:"$refText"` + Ref string `json:"$ref,omitempty"` + Err string `json:"$error,omitempty"` + }{ + RefText: r.unit.String(), + Ref: uri, + Err: errMsg, + }) +} + +func (r *Reference[T]) UnmarshalJSON(data []byte) error { + aux := &struct { + RefText string `json:"$refText"` + Ref string `json:"$ref,omitempty"` + Err string `json:"$error,omitempty"` + }{} + + if err := json.Unmarshal(data, aux); err != nil { + return err + } + + r.unit = &RefText{ + refText: aux.RefText, + } + if aux.Err != "" { + r.err = NewReferenceError(aux.Err) + } + r.getter = func(ctx context.Context, ref *Reference[T]) (*SymbolDescription, *ReferenceError) { + if ref.err != nil { + return nil, ref.err + } + return &SymbolDescription{ + URI: ParseURI(aux.Ref), + }, nil + } + + return nil +} + +type RefText struct { + owner AstNode + refText string +} + +func (r *RefText) String() string { + return r.refText +} + +func (r *RefText) Owner() AstNode { + return r.owner +} + +func (r *RefText) Segment() *TextSegment { + return nil +} From dad7938f3f8ea14f9ffb2212350945623b7414a3 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Wed, 17 Jun 2026 11:11:38 +0200 Subject: [PATCH 02/16] progress on 'json_generator.go' --- examples/arithmetics/json_gen.go | 259 +++++++++++++ examples/statemachine/json_gen.go | 33 +- internal/generator/json_generator.go | 53 ++- internal/grammar/json_gen.go | 405 +++++++++++++------- internal/languages/completion/json_gen.go | 73 ++-- internal/languages/token_groups/json_gen.go | 88 +++++ 6 files changed, 726 insertions(+), 185 deletions(-) create mode 100644 examples/arithmetics/json_gen.go create mode 100644 internal/languages/token_groups/json_gen.go diff --git a/examples/arithmetics/json_gen.go b/examples/arithmetics/json_gen.go new file mode 100644 index 00000000..bb7782f3 --- /dev/null +++ b/examples/arithmetics/json_gen.go @@ -0,0 +1,259 @@ +// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT. + +package arithmetics + +import ( + "encoding/json" + + core "typefox.dev/fastbelt" +) + +func newToken(tokenType *core.TokenType, view string) *core.Token { + token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0) + return &token +} + +func (i *ModuleImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Statements []Statement `json:"statements"` + }{ + T__: "Module", + Name: i.Name(), + Statements: i.Statements(), + }) +} + +func (i *StatementImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + }{ + T__: "Statement", + }) +} + +func (i *AbstractDefinitionImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{ + T__: "AbstractDefinition", + Name: i.Name(), + }) +} + +func (i *DefinitionImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + Args []DeclaredParameter `json:"args"` + Expression Expression `json:"expression"` + }{ + T__: "Definition", + Name: i.Name(), + Args: i.Args(), + Expression: i.Expression(), + }) +} + +func (i *DeclaredParameterImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{ + T__: "DeclaredParameter", + Name: i.Name(), + }) +} + +func (i *EvaluationImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Expression Expression `json:"expression"` + }{ + T__: "Evaluation", + Expression: i.Expression(), + }) +} + +func (i *ExpressionImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + }{ + T__: "Expression", + }) +} + +func (i *BinaryExpressionImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Left Expression `json:"left"` + Operator string `json:"operator"` + Right Expression `json:"right"` + }{ + T__: "BinaryExpression", + Left: i.Left(), + Operator: i.Operator(), + Right: i.Right(), + }) +} + +func (i *FunctionCallImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Args []Expression `json:"args"` + Callable *core.Reference[AbstractDefinition] `json:"callable"` + }{ + T__: "FunctionCall", + Args: i.Args(), + Callable: i.Callable(), + }) +} + +func (i *NumberLiteralImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Value string `json:"value"` + }{ + T__: "NumberLiteral", + Value: i.Value(), + }) +} + +func (i *ModuleImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Statements []*StatementImpl `json:"statements"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(newToken(Token_ID, aux.Name)) + i.statements = []Statement{} + for _, item := range aux.Statements { + i.SetStatementsItem(item) + } + return nil +} + +func (i *StatementImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + return nil +} + +func (i *AbstractDefinitionImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(newToken(Token_ID, aux.Name)) + return nil +} + +func (i *DefinitionImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + Args []*DeclaredParameterImpl `json:"args"` + Expression *ExpressionImpl `json:"expression"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(newToken(Token_ID, aux.Name)) + i.args = []DeclaredParameter{} + for _, item := range aux.Args { + i.SetArgsItem(item) + } + i.SetExpression(aux.Expression) + return nil +} + +func (i *DeclaredParameterImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(newToken(Token_ID, aux.Name)) + return nil +} + +func (i *EvaluationImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Expression *ExpressionImpl `json:"expression"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetExpression(aux.Expression) + return nil +} + +func (i *ExpressionImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + return nil +} + +func (i *BinaryExpressionImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Left *ExpressionImpl `json:"left"` + Operator string `json:"operator"` + Right *ExpressionImpl `json:"right"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetLeft(aux.Left) + i.SetOperator(newToken(Token_ID, aux.Operator)) + i.SetRight(aux.Right) + return nil +} + +func (i *FunctionCallImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Args []*ExpressionImpl `json:"args"` + Callable *core.Reference[AbstractDefinition] `json:"callable"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.args = []Expression{} + for _, item := range aux.Args { + i.SetArgsItem(item) + } + i.SetCallable(aux.Callable) + return nil +} + +func (i *NumberLiteralImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Value string `json:"value"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetValue(newToken(Token_ID, aux.Value)) + return nil +} diff --git a/examples/statemachine/json_gen.go b/examples/statemachine/json_gen.go index 2f9875f0..857beb05 100644 --- a/examples/statemachine/json_gen.go +++ b/examples/statemachine/json_gen.go @@ -8,7 +8,12 @@ import ( core "typefox.dev/fastbelt" ) -func (i *StatemachineData) MarshalJSON() ([]byte, error) { +func newToken(tokenType *core.TokenType, view string) *core.Token { + token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0) + return &token +} + +func (i *StatemachineImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -26,7 +31,7 @@ func (i *StatemachineData) MarshalJSON() ([]byte, error) { }) } -func (i *EventData) MarshalJSON() ([]byte, error) { +func (i *EventImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -36,7 +41,7 @@ func (i *EventData) MarshalJSON() ([]byte, error) { }) } -func (i *CommandData) MarshalJSON() ([]byte, error) { +func (i *CommandImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -46,7 +51,7 @@ func (i *CommandData) MarshalJSON() ([]byte, error) { }) } -func (i *StateData) MarshalJSON() ([]byte, error) { +func (i *StateImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -60,7 +65,7 @@ func (i *StateData) MarshalJSON() ([]byte, error) { }) } -func (i *TransitionData) MarshalJSON() ([]byte, error) { +func (i *TransitionImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Event *core.Reference[Event] `json:"event"` @@ -72,7 +77,7 @@ func (i *TransitionData) MarshalJSON() ([]byte, error) { }) } -func (i *StatemachineData) UnmarshalJSON(data []byte) error { +func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -84,7 +89,7 @@ func (i *StatemachineData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) i.events = []Event{} for _, item := range aux.Events { i.SetEventsItem(item) @@ -101,7 +106,7 @@ func (i *StatemachineData) UnmarshalJSON(data []byte) error { return nil } -func (i *EventData) UnmarshalJSON(data []byte) error { +func (i *EventImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -109,11 +114,11 @@ func (i *EventData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) return nil } -func (i *CommandData) UnmarshalJSON(data []byte) error { +func (i *CommandImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -121,11 +126,11 @@ func (i *CommandData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) return nil } -func (i *StateData) UnmarshalJSON(data []byte) error { +func (i *StateImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -135,7 +140,7 @@ func (i *StateData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) i.actions = []*core.Reference[Command]{} for _, item := range aux.Actions { i.SetActionsItem(item) @@ -147,7 +152,7 @@ func (i *StateData) UnmarshalJSON(data []byte) error { return nil } -func (i *TransitionData) UnmarshalJSON(data []byte) error { +func (i *TransitionImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Event *core.Reference[Event] `json:"event"` diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go index 83a8430f..fecb6de6 100644 --- a/internal/generator/json_generator.go +++ b/internal/generator/json_generator.go @@ -5,6 +5,7 @@ package generator import ( + "context" "strings" "typefox.dev/fastbelt/internal/grammar" @@ -27,6 +28,14 @@ func GenerateJSON(grammr grammar.Grammar, packageName string) string { node.AppendLine(")") node.AppendLine() + node.AppendLine(("func newToken(tokenType *core.TokenType, view string) *core.Token {")) + node.Indent(func(n codegen.Node) { + n.AppendLine("token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0)") + n.AppendLine("return &token") + }) + node.AppendLine("}") + node.AppendLine() + for _, iface := range grammr.Interfaces() { generateJSONMarshal(node, iface) } @@ -38,13 +47,27 @@ func GenerateJSON(grammr grammar.Grammar, packageName string) string { return FormatIfPossible(node.String()) } -func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { +func collectAllFields(iface grammar.Interface, visited map[string]struct{}) []FieldInfo { + if _, seen := visited[iface.Name()]; seen { + return nil + } + visited[iface.Name()] = struct{}{} fields := []FieldInfo{} + for _, ext := range iface.Extends() { + if parent := ext.Ref(context.TODO()); parent != nil { + fields = append(fields, collectAllFields(parent, visited)...) + } + } for _, field := range iface.Fields() { fields = append(fields, getFieldInfo(field)) } + return fields +} + +func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { + fields := collectAllFields(iface, map[string]struct{}{}) - node.AppendLine("func (i *", iface.Name(), "Data) MarshalJSON() ([]byte, error) {") + node.AppendLine("func (i *", iface.Name(), "Impl) MarshalJSON() ([]byte, error) {") node.Indent(func(n codegen.Node) { n.AppendLine("return json.Marshal(struct {") n.Indent(func(n2 codegen.Node) { @@ -103,12 +126,9 @@ func getAuxFieldType(field FieldInfo) string { } func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { - fields := []FieldInfo{} - for _, field := range iface.Fields() { - fields = append(fields, getFieldInfo(field)) - } + fields := collectAllFields(iface, map[string]struct{}{}) - node.AppendLine("func (i *", iface.Name(), "Data) UnmarshalJSON(data []byte) error {") + node.AppendLine("func (i *", iface.Name(), "Impl) UnmarshalJSON(data []byte) error {") node.Indent(func(n codegen.Node) { n.AppendLine("aux := &struct {") n.Indent(func(n2 codegen.Node) { @@ -131,17 +151,26 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { n.AppendLine("i.", field.PName, " = []", field.GType, "{}") n.AppendLine("for _, item := range aux.", field.Name, " {") n.Indent(func(n2 codegen.Node) { - n2.AppendLine("i.Set", field.Name, "Item(item)") + switch field.GType { + case TOKEN_TYPE: + n2.AppendLine("i.Set", field.Name, "Item(newToken(Token_ID, item))") + case COMPOSITE_TYPE: + n.AppendLine("cn := core.NewCompositeNode()") + n.AppendLine("cn.SetToken(newToken(Token_ID, item))") + n2.AppendLine("i.Set", field.Name, "Item(cn)") + default: + n2.AppendLine("i.Set", field.Name, "Item(item)") + } }) n.AppendLine("}") - } else if field.Boolean || field.HasTokenGetter { - n.AppendLine("i.Set", field.Name, "(&core.Token{Image: aux.", field.Name, "})") - } else if field.HasNodeGetter { + } else if field.Boolean || field.GType == TOKEN_TYPE { + n.AppendLine("i.Set", field.Name, "(newToken(Token_ID, aux.", field.Name, "))") + } else if field.GType == COMPOSITE_TYPE { if !hasComposeNodeTempVar { n.AppendLine("var cn core.CompositeNode") } n.AppendLine("cn = core.NewCompositeNode()") - n.AppendLine("cn.SetToken(&core.Token{Image: aux.", field.Name, "})") + n.AppendLine("cn.SetToken(newToken(Token_ID, aux.", field.Name, "))") n.AppendLine("i.Set", field.Name, "(cn)") } else { n.AppendLine("i.Set", field.Name, "(aux.", field.Name, ")") diff --git a/internal/grammar/json_gen.go b/internal/grammar/json_gen.go index cc79285a..2f4aa6dd 100644 --- a/internal/grammar/json_gen.go +++ b/internal/grammar/json_gen.go @@ -8,25 +8,32 @@ import ( core "typefox.dev/fastbelt" ) -func (i *GrammarData) MarshalJSON() ([]byte, error) { +func newToken(tokenType *core.TokenType, view string) *core.Token { + token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0) + return &token +} + +func (i *GrammarImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Name string `json:"name"` - Rules []ParserRule `json:"rules"` - Composites []CompositeRule `json:"composites"` - Terminals []Token `json:"terminals"` - Interfaces []Interface `json:"interfaces"` + T__ string `json:"$type"` + Name string `json:"name"` + Rules []ParserRule `json:"rules"` + Composites []CompositeRule `json:"composites"` + Terminals []Token `json:"terminals"` + TokenGroups []TokenGroup `json:"tokenGroups"` + Interfaces []Interface `json:"interfaces"` }{ - T__: "Grammar", - Name: i.Name(), - Rules: i.Rules(), - Composites: i.Composites(), - Terminals: i.Terminals(), - Interfaces: i.Interfaces(), + T__: "Grammar", + Name: i.Name(), + Rules: i.Rules(), + Composites: i.Composites(), + Terminals: i.Terminals(), + TokenGroups: i.TokenGroups(), + Interfaces: i.Interfaces(), }) } -func (i *InterfaceData) MarshalJSON() ([]byte, error) { +func (i *InterfaceImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -40,7 +47,7 @@ func (i *InterfaceData) MarshalJSON() ([]byte, error) { }) } -func (i *FieldData) MarshalJSON() ([]byte, error) { +func (i *FieldImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -52,7 +59,7 @@ func (i *FieldData) MarshalJSON() ([]byte, error) { }) } -func (i *FieldTypeData) MarshalJSON() ([]byte, error) { +func (i *FieldTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` }{ @@ -60,7 +67,7 @@ func (i *FieldTypeData) MarshalJSON() ([]byte, error) { }) } -func (i *ArrayTypeData) MarshalJSON() ([]byte, error) { +func (i *ArrayTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` InternalType FieldType `json:"internalType"` @@ -70,7 +77,7 @@ func (i *ArrayTypeData) MarshalJSON() ([]byte, error) { }) } -func (i *ReferenceTypeData) MarshalJSON() ([]byte, error) { +func (i *ReferenceTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Type *core.Reference[Interface] `json:"type"` @@ -80,7 +87,7 @@ func (i *ReferenceTypeData) MarshalJSON() ([]byte, error) { }) } -func (i *SimpleTypeData) MarshalJSON() ([]byte, error) { +func (i *SimpleTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Type *core.Reference[Interface] `json:"type"` @@ -90,7 +97,7 @@ func (i *SimpleTypeData) MarshalJSON() ([]byte, error) { }) } -func (i *PrimitiveTypeData) MarshalJSON() ([]byte, error) { +func (i *PrimitiveTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Type string `json:"type"` @@ -100,7 +107,7 @@ func (i *PrimitiveTypeData) MarshalJSON() ([]byte, error) { }) } -func (i *AbstractRuleData) MarshalJSON() ([]byte, error) { +func (i *AbstractRuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -110,39 +117,73 @@ func (i *AbstractRuleData) MarshalJSON() ([]byte, error) { }) } -func (i *AbstractRuleWithBodyData) MarshalJSON() ([]byte, error) { +func (i *AbstractRuleWithBodyImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` + Name string `json:"name"` Body Element `json:"body"` }{ T__: "AbstractRuleWithBody", + Name: i.Name(), Body: i.Body(), }) } -func (i *ParserRuleData) MarshalJSON() ([]byte, error) { +func (i *AbstractTokenRuleImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{ + T__: "AbstractTokenRule", + Name: i.Name(), + }) +} + +func (i *ParserRuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` + Name string `json:"name"` + Body Element `json:"body"` ReturnType *core.Reference[Interface] `json:"returnType"` }{ T__: "ParserRule", + Name: i.Name(), + Body: i.Body(), ReturnType: i.ReturnType(), }) } -func (i *TokenData) MarshalJSON() ([]byte, error) { +func (i *TokenImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` + Name string `json:"name"` Type string `json:"type"` Regexp string `json:"regexp"` }{ T__: "Token", + Name: i.Name(), Type: i.Type(), Regexp: i.Regexp(), }) } -func (i *ElementData) MarshalJSON() ([]byte, error) { +func (i *TokenGroupImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Name string `json:"name"` + TokenRefs []*core.Reference[AbstractTokenRule] `json:"tokenRefs"` + Regexps []*core.Token `json:"regexps"` + Keywords []Keyword `json:"keywords"` + }{ + T__: "TokenGroup", + Name: i.Name(), + TokenRefs: i.TokenRefs(), + Regexps: i.Regexps(), + Keywords: i.Keywords(), + }) +} + +func (i *ElementImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Cardinality string `json:"cardinality"` @@ -152,115 +193,136 @@ func (i *ElementData) MarshalJSON() ([]byte, error) { }) } -func (i *AlternativesData) MarshalJSON() ([]byte, error) { +func (i *AlternativesImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Alts []Element `json:"alts"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Alts []Element `json:"alts"` }{ - T__: "Alternatives", - Alts: i.Alts(), + T__: "Alternatives", + Cardinality: i.Cardinality(), + Alts: i.Alts(), }) } -func (i *GroupData) MarshalJSON() ([]byte, error) { +func (i *GroupImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Elements []Element `json:"elements"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Elements []Element `json:"elements"` }{ - T__: "Group", - Elements: i.Elements(), + T__: "Group", + Cardinality: i.Cardinality(), + Elements: i.Elements(), }) } -func (i *KeywordData) MarshalJSON() ([]byte, error) { +func (i *KeywordImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Value string `json:"value"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Value string `json:"value"` }{ - T__: "Keyword", - Value: i.Value(), + T__: "Keyword", + Cardinality: i.Cardinality(), + Value: i.Value(), }) } -func (i *AssignmentData) MarshalJSON() ([]byte, error) { +func (i *AssignmentImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Property *core.Reference[Field] `json:"property"` - Operator string `json:"operator"` - Value Assignable `json:"value"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Property *core.Reference[Field] `json:"property"` + Operator string `json:"operator"` + Value Assignable `json:"value"` }{ - T__: "Assignment", - Property: i.Property(), - Operator: i.Operator(), - Value: i.Value(), + T__: "Assignment", + Cardinality: i.Cardinality(), + Property: i.Property(), + Operator: i.Operator(), + Value: i.Value(), }) } -func (i *AssignableData) MarshalJSON() ([]byte, error) { +func (i *AssignableImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` }{ - T__: "Assignable", + T__: "Assignable", + Cardinality: i.Cardinality(), }) } -func (i *CrossRefData) MarshalJSON() ([]byte, error) { +func (i *CrossRefImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` - Rule RuleCall `json:"rule"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Type *core.Reference[Interface] `json:"type"` + Rule RuleCall `json:"rule"` }{ - T__: "CrossRef", - Type: i.Type(), - Rule: i.Rule(), + T__: "CrossRef", + Cardinality: i.Cardinality(), + Type: i.Type(), + Rule: i.Rule(), }) } -func (i *RuleCallData) MarshalJSON() ([]byte, error) { +func (i *RuleCallImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Rule *core.Reference[AbstractRule] `json:"rule"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Rule *core.Reference[AbstractRule] `json:"rule"` }{ - T__: "RuleCall", - Rule: i.Rule(), + T__: "RuleCall", + Cardinality: i.Cardinality(), + Rule: i.Rule(), }) } -func (i *ActionData) MarshalJSON() ([]byte, error) { +func (i *ActionImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` - Operator string `json:"operator"` - Property *core.Reference[Field] `json:"property"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Type *core.Reference[Interface] `json:"type"` + Operator string `json:"operator"` + Property *core.Reference[Field] `json:"property"` }{ - T__: "Action", - Type: i.Type(), - Operator: i.Operator(), - Property: i.Property(), + T__: "Action", + Cardinality: i.Cardinality(), + Type: i.Type(), + Operator: i.Operator(), + Property: i.Property(), }) } -func (i *CompositeRuleData) MarshalJSON() ([]byte, error) { +func (i *CompositeRuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - T__ string `json:"$type"` + T__ string `json:"$type"` + Name string `json:"name"` + Body Element `json:"body"` }{ - T__: "CompositeRule", + T__: "CompositeRule", + Name: i.Name(), + Body: i.Body(), }) } -func (i *GrammarData) UnmarshalJSON(data []byte) error { +func (i *GrammarImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Rules []*ParserRuleImpl `json:"rules"` - Composites []*CompositeRuleImpl `json:"composites"` - Terminals []*TokenImpl `json:"terminals"` - Interfaces []*InterfaceImpl `json:"interfaces"` + T__ string `json:"$type"` + Name string `json:"name"` + Rules []*ParserRuleImpl `json:"rules"` + Composites []*CompositeRuleImpl `json:"composites"` + Terminals []*TokenImpl `json:"terminals"` + TokenGroups []*TokenGroupImpl `json:"tokenGroups"` + Interfaces []*InterfaceImpl `json:"interfaces"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) i.rules = []ParserRule{} for _, item := range aux.Rules { i.SetRulesItem(item) @@ -273,6 +335,10 @@ func (i *GrammarData) UnmarshalJSON(data []byte) error { for _, item := range aux.Terminals { i.SetTerminalsItem(item) } + i.tokenGroups = []TokenGroup{} + for _, item := range aux.TokenGroups { + i.SetTokenGroupsItem(item) + } i.interfaces = []Interface{} for _, item := range aux.Interfaces { i.SetInterfacesItem(item) @@ -280,7 +346,7 @@ func (i *GrammarData) UnmarshalJSON(data []byte) error { return nil } -func (i *InterfaceData) UnmarshalJSON(data []byte) error { +func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -290,7 +356,7 @@ func (i *InterfaceData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) i.extends = []*core.Reference[Interface]{} for _, item := range aux.Extends { i.SetExtendsItem(item) @@ -302,7 +368,7 @@ func (i *InterfaceData) UnmarshalJSON(data []byte) error { return nil } -func (i *FieldData) UnmarshalJSON(data []byte) error { +func (i *FieldImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -311,12 +377,12 @@ func (i *FieldData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) i.SetType(aux.Type) return nil } -func (i *FieldTypeData) UnmarshalJSON(data []byte) error { +func (i *FieldTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` }{} @@ -326,7 +392,7 @@ func (i *FieldTypeData) UnmarshalJSON(data []byte) error { return nil } -func (i *ArrayTypeData) UnmarshalJSON(data []byte) error { +func (i *ArrayTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` InternalType *FieldTypeImpl `json:"internalType"` @@ -338,7 +404,7 @@ func (i *ArrayTypeData) UnmarshalJSON(data []byte) error { return nil } -func (i *ReferenceTypeData) UnmarshalJSON(data []byte) error { +func (i *ReferenceTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Type *core.Reference[Interface] `json:"type"` @@ -350,7 +416,7 @@ func (i *ReferenceTypeData) UnmarshalJSON(data []byte) error { return nil } -func (i *SimpleTypeData) UnmarshalJSON(data []byte) error { +func (i *SimpleTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Type *core.Reference[Interface] `json:"type"` @@ -362,7 +428,7 @@ func (i *SimpleTypeData) UnmarshalJSON(data []byte) error { return nil } -func (i *PrimitiveTypeData) UnmarshalJSON(data []byte) error { +func (i *PrimitiveTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Type string `json:"type"` @@ -370,11 +436,11 @@ func (i *PrimitiveTypeData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetType(&core.Token{Image: aux.Type}) + i.SetType(newToken(Token_ID, aux.Type)) return nil } -func (i *AbstractRuleData) UnmarshalJSON(data []byte) error { +func (i *AbstractRuleImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -382,49 +448,96 @@ func (i *AbstractRuleData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetName(&core.Token{Image: aux.Name}) + i.SetName(newToken(Token_ID, aux.Name)) return nil } -func (i *AbstractRuleWithBodyData) UnmarshalJSON(data []byte) error { +func (i *AbstractRuleWithBodyImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` + Name string `json:"name"` Body *ElementImpl `json:"body"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetName(newToken(Token_ID, aux.Name)) i.SetBody(aux.Body) return nil } -func (i *ParserRuleData) UnmarshalJSON(data []byte) error { +func (i *AbstractTokenRuleImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(newToken(Token_ID, aux.Name)) + return nil +} + +func (i *ParserRuleImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` + Name string `json:"name"` + Body *ElementImpl `json:"body"` ReturnType *core.Reference[Interface] `json:"returnType"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetName(newToken(Token_ID, aux.Name)) + i.SetBody(aux.Body) i.SetReturnType(aux.ReturnType) return nil } -func (i *TokenData) UnmarshalJSON(data []byte) error { +func (i *TokenImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` + Name string `json:"name"` Type string `json:"type"` Regexp string `json:"regexp"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetType(&core.Token{Image: aux.Type}) - i.SetRegexp(&core.Token{Image: aux.Regexp}) + i.SetName(newToken(Token_ID, aux.Name)) + i.SetType(newToken(Token_ID, aux.Type)) + i.SetRegexp(newToken(Token_ID, aux.Regexp)) return nil } -func (i *ElementData) UnmarshalJSON(data []byte) error { +func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Name string `json:"name"` + TokenRefs []*core.Reference[AbstractTokenRule] `json:"tokenRefs"` + Regexps []string `json:"regexps"` + Keywords []*KeywordImpl `json:"keywords"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetName(newToken(Token_ID, aux.Name)) + i.tokenRefs = []*core.Reference[AbstractTokenRule]{} + for _, item := range aux.TokenRefs { + i.SetTokenRefsItem(item) + } + i.regexps = []*core.Token{} + for _, item := range aux.Regexps { + i.SetRegexpsItem(newToken(Token_ID, item)) + } + i.keywords = []Keyword{} + for _, item := range aux.Keywords { + i.SetKeywordsItem(item) + } + return nil +} + +func (i *ElementImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Cardinality string `json:"cardinality"` @@ -432,18 +545,20 @@ func (i *ElementData) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetCardinality(&core.Token{Image: aux.Cardinality}) + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) return nil } -func (i *AlternativesData) UnmarshalJSON(data []byte) error { +func (i *AlternativesImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Alts []*ElementImpl `json:"alts"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Alts []*ElementImpl `json:"alts"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.alts = []Element{} for _, item := range aux.Alts { i.SetAltsItem(item) @@ -451,14 +566,16 @@ func (i *AlternativesData) UnmarshalJSON(data []byte) error { return nil } -func (i *GroupData) UnmarshalJSON(data []byte) error { +func (i *GroupImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Elements []*ElementImpl `json:"elements"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Elements []*ElementImpl `json:"elements"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.elements = []Element{} for _, item := range aux.Elements { i.SetElementsItem(item) @@ -466,92 +583,108 @@ func (i *GroupData) UnmarshalJSON(data []byte) error { return nil } -func (i *KeywordData) UnmarshalJSON(data []byte) error { +func (i *KeywordImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Value string `json:"value"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Value string `json:"value"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetValue(&core.Token{Image: aux.Value}) + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) + i.SetValue(newToken(Token_ID, aux.Value)) return nil } -func (i *AssignmentData) UnmarshalJSON(data []byte) error { +func (i *AssignmentImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Property *core.Reference[Field] `json:"property"` - Operator string `json:"operator"` - Value *AssignableImpl `json:"value"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Property *core.Reference[Field] `json:"property"` + Operator string `json:"operator"` + Value *AssignableImpl `json:"value"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.SetProperty(aux.Property) - i.SetOperator(&core.Token{Image: aux.Operator}) + i.SetOperator(newToken(Token_ID, aux.Operator)) i.SetValue(aux.Value) return nil } -func (i *AssignableData) UnmarshalJSON(data []byte) error { +func (i *AssignableImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) return nil } -func (i *CrossRefData) UnmarshalJSON(data []byte) error { +func (i *CrossRefImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` - Rule *RuleCallImpl `json:"rule"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Type *core.Reference[Interface] `json:"type"` + Rule *RuleCallImpl `json:"rule"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.SetType(aux.Type) i.SetRule(aux.Rule) return nil } -func (i *RuleCallData) UnmarshalJSON(data []byte) error { +func (i *RuleCallImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Rule *core.Reference[AbstractRule] `json:"rule"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Rule *core.Reference[AbstractRule] `json:"rule"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.SetRule(aux.Rule) return nil } -func (i *ActionData) UnmarshalJSON(data []byte) error { +func (i *ActionImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` - Operator string `json:"operator"` - Property *core.Reference[Field] `json:"property"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Type *core.Reference[Interface] `json:"type"` + Operator string `json:"operator"` + Property *core.Reference[Field] `json:"property"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.SetType(aux.Type) - i.SetOperator(&core.Token{Image: aux.Operator}) + i.SetOperator(newToken(Token_ID, aux.Operator)) i.SetProperty(aux.Property) return nil } -func (i *CompositeRuleData) UnmarshalJSON(data []byte) error { +func (i *CompositeRuleImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` + T__ string `json:"$type"` + Name string `json:"name"` + Body *ElementImpl `json:"body"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } + i.SetName(newToken(Token_ID, aux.Name)) + i.SetBody(aux.Body) return nil } diff --git a/internal/languages/completion/json_gen.go b/internal/languages/completion/json_gen.go index bdfa377b..8518e7bf 100644 --- a/internal/languages/completion/json_gen.go +++ b/internal/languages/completion/json_gen.go @@ -8,7 +8,12 @@ import ( core "typefox.dev/fastbelt" ) -func (i *ObjData) MarshalJSON() ([]byte, error) { +func newToken(tokenType *core.TokenType, view string) *core.Token { + token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0) + return &token +} + +func (i *ObjImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` }{ @@ -16,7 +21,7 @@ func (i *ObjData) MarshalJSON() ([]byte, error) { }) } -func (i *RootData) MarshalJSON() ([]byte, error) { +func (i *RootImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Objects []Obj `json:"objects"` @@ -26,7 +31,7 @@ func (i *RootData) MarshalJSON() ([]byte, error) { }) } -func (i *DeclareData) MarshalJSON() ([]byte, error) { +func (i *DeclareImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` @@ -38,7 +43,7 @@ func (i *DeclareData) MarshalJSON() ([]byte, error) { }) } -func (i *EData) MarshalJSON() ([]byte, error) { +func (i *EImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -48,7 +53,7 @@ func (i *EData) MarshalJSON() ([]byte, error) { }) } -func (i *FData) MarshalJSON() ([]byte, error) { +func (i *FImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Items []FItem `json:"items"` @@ -58,7 +63,7 @@ func (i *FData) MarshalJSON() ([]byte, error) { }) } -func (i *FItemData) MarshalJSON() ([]byte, error) { +func (i *FItemImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -68,7 +73,7 @@ func (i *FItemData) MarshalJSON() ([]byte, error) { }) } -func (i *GData) MarshalJSON() ([]byte, error) { +func (i *GImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -78,7 +83,7 @@ func (i *GData) MarshalJSON() ([]byte, error) { }) } -func (i *HData) MarshalJSON() ([]byte, error) { +func (i *HImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Member MemberCall `json:"member"` @@ -88,7 +93,7 @@ func (i *HData) MarshalJSON() ([]byte, error) { }) } -func (i *MemberCallData) MarshalJSON() ([]byte, error) { +func (i *MemberCallImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -100,7 +105,7 @@ func (i *MemberCallData) MarshalJSON() ([]byte, error) { }) } -func (i *JData) MarshalJSON() ([]byte, error) { +func (i *JImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -110,7 +115,7 @@ func (i *JData) MarshalJSON() ([]byte, error) { }) } -func (i *KData) MarshalJSON() ([]byte, error) { +func (i *KImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` Ref1 *core.Reference[Declare] `json:"ref1"` @@ -122,7 +127,17 @@ func (i *KData) MarshalJSON() ([]byte, error) { }) } -func (i *ObjData) UnmarshalJSON(data []byte) error { +func (i *NImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{ + T__: "N", + Ref: i.Ref(), + }) +} + +func (i *ObjImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` }{} @@ -132,7 +147,7 @@ func (i *ObjData) UnmarshalJSON(data []byte) error { return nil } -func (i *RootData) UnmarshalJSON(data []byte) error { +func (i *RootImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Objects []*ObjImpl `json:"objects"` @@ -147,7 +162,7 @@ func (i *RootData) UnmarshalJSON(data []byte) error { return nil } -func (i *DeclareData) UnmarshalJSON(data []byte) error { +func (i *DeclareImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` @@ -158,7 +173,7 @@ func (i *DeclareData) UnmarshalJSON(data []byte) error { } var cn core.CompositeNode cn = core.NewCompositeNode() - cn.SetToken(&core.Token{Image: aux.Name}) + cn.SetToken(newToken(Token_ID, aux.Name)) i.SetName(cn) i.children = []Declare{} for _, item := range aux.Children { @@ -167,7 +182,7 @@ func (i *DeclareData) UnmarshalJSON(data []byte) error { return nil } -func (i *EData) UnmarshalJSON(data []byte) error { +func (i *EImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -179,7 +194,7 @@ func (i *EData) UnmarshalJSON(data []byte) error { return nil } -func (i *FData) UnmarshalJSON(data []byte) error { +func (i *FImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Items []*FItemImpl `json:"items"` @@ -194,7 +209,7 @@ func (i *FData) UnmarshalJSON(data []byte) error { return nil } -func (i *FItemData) UnmarshalJSON(data []byte) error { +func (i *FItemImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -206,7 +221,7 @@ func (i *FItemData) UnmarshalJSON(data []byte) error { return nil } -func (i *GData) UnmarshalJSON(data []byte) error { +func (i *GImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -218,7 +233,7 @@ func (i *GData) UnmarshalJSON(data []byte) error { return nil } -func (i *HData) UnmarshalJSON(data []byte) error { +func (i *HImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Member *MemberCallImpl `json:"member"` @@ -230,7 +245,7 @@ func (i *HData) UnmarshalJSON(data []byte) error { return nil } -func (i *MemberCallData) UnmarshalJSON(data []byte) error { +func (i *MemberCallImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -244,7 +259,7 @@ func (i *MemberCallData) UnmarshalJSON(data []byte) error { return nil } -func (i *JData) UnmarshalJSON(data []byte) error { +func (i *JImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` @@ -256,7 +271,7 @@ func (i *JData) UnmarshalJSON(data []byte) error { return nil } -func (i *KData) UnmarshalJSON(data []byte) error { +func (i *KImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Ref1 *core.Reference[Declare] `json:"ref1"` @@ -269,3 +284,15 @@ func (i *KData) UnmarshalJSON(data []byte) error { i.SetRef2(aux.Ref2) return nil } + +func (i *NImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetRef(aux.Ref) + return nil +} diff --git a/internal/languages/token_groups/json_gen.go b/internal/languages/token_groups/json_gen.go new file mode 100644 index 00000000..86e1b50a --- /dev/null +++ b/internal/languages/token_groups/json_gen.go @@ -0,0 +1,88 @@ +// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT. + +package token_groups + +import ( + "encoding/json" + + core "typefox.dev/fastbelt" +) + +func newToken(tokenType *core.TokenType, view string) *core.Token { + token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0) + return &token +} + +func (i *ModelImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Item Item `json:"item"` + }{ + T__: "Model", + Item: i.Item(), + }) +} + +func (i *ItemImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Value string `json:"value"` + }{ + T__: "Item", + Value: i.Value(), + }) +} + +func (i *RecoveryImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Value string `json:"value"` + First string `json:"first"` + Second string `json:"second"` + }{ + T__: "Recovery", + Value: i.Value(), + First: i.First(), + Second: i.Second(), + }) +} + +func (i *ModelImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Item *ItemImpl `json:"item"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetItem(aux.Item) + return nil +} + +func (i *ItemImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Value string `json:"value"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetValue(newToken(Token_ID, aux.Value)) + return nil +} + +func (i *RecoveryImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Value string `json:"value"` + First string `json:"first"` + Second string `json:"second"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetValue(newToken(Token_ID, aux.Value)) + i.SetFirst(newToken(Token_ID, aux.First)) + i.SetSecond(newToken(Token_ID, aux.Second)) + return nil +} From 803470669bf37e87bb3c376ca417ff311c8d75e7 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Wed, 17 Jun 2026 14:54:50 +0200 Subject: [PATCH 03/16] unmarshaling --- examples/arithmetics/json_gen.go | 77 ++++++---- examples/statemachine/json_gen.go | 33 +++- internal/generator/json_generator.go | 108 +++++++++---- internal/grammar/json_gen.go | 161 +++++++++++++------- internal/languages/completion/json_gen.go | 55 ++++--- internal/languages/token_groups/json_gen.go | 11 +- util/json/json.go | 54 +++++++ 7 files changed, 355 insertions(+), 144 deletions(-) create mode 100644 util/json/json.go diff --git a/examples/arithmetics/json_gen.go b/examples/arithmetics/json_gen.go index bb7782f3..a6824eec 100644 --- a/examples/arithmetics/json_gen.go +++ b/examples/arithmetics/json_gen.go @@ -6,6 +6,7 @@ import ( "encoding/json" core "typefox.dev/fastbelt" + utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -123,9 +124,9 @@ func (i *NumberLiteralImpl) MarshalJSON() ([]byte, error) { func (i *ModuleImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Statements []*StatementImpl `json:"statements"` + T__ string `json:"$type"` + Name string `json:"name"` + Statements []json.RawMessage `json:"statements"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -133,18 +134,16 @@ func (i *ModuleImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.statements = []Statement{} for _, item := range aux.Statements { - i.SetStatementsItem(item) + node, err := utilJson.Unmarshal[Statement](item, ArithmeticsSyntheticFactories) + if err != nil { + return err + } + i.SetStatementsItem(node) } return nil } func (i *StatementImpl) UnmarshalJSON(data []byte) error { - aux := &struct { - T__ string `json:"$type"` - }{} - if err := json.Unmarshal(data, aux); err != nil { - return err - } return nil } @@ -162,10 +161,10 @@ func (i *AbstractDefinitionImpl) UnmarshalJSON(data []byte) error { func (i *DefinitionImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Args []*DeclaredParameterImpl `json:"args"` - Expression *ExpressionImpl `json:"expression"` + T__ string `json:"$type"` + Name string `json:"name"` + Args []json.RawMessage `json:"args"` + Expression json.RawMessage `json:"expression"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -173,9 +172,17 @@ func (i *DefinitionImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.args = []DeclaredParameter{} for _, item := range aux.Args { - i.SetArgsItem(item) + node, err := utilJson.Unmarshal[DeclaredParameter](item, ArithmeticsSyntheticFactories) + if err != nil { + return err + } + i.SetArgsItem(node) + } + expression, err := utilJson.Unmarshal[Expression](aux.Expression, ArithmeticsSyntheticFactories) + if err != nil { + return err } - i.SetExpression(aux.Expression) + i.SetExpression(expression) return nil } @@ -194,45 +201,51 @@ func (i *DeclaredParameterImpl) UnmarshalJSON(data []byte) error { func (i *EvaluationImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` - Expression *ExpressionImpl `json:"expression"` + Expression json.RawMessage `json:"expression"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetExpression(aux.Expression) + expression, err := utilJson.Unmarshal[Expression](aux.Expression, ArithmeticsSyntheticFactories) + if err != nil { + return err + } + i.SetExpression(expression) return nil } func (i *ExpressionImpl) UnmarshalJSON(data []byte) error { - aux := &struct { - T__ string `json:"$type"` - }{} - if err := json.Unmarshal(data, aux); err != nil { - return err - } return nil } func (i *BinaryExpressionImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` - Left *ExpressionImpl `json:"left"` + Left json.RawMessage `json:"left"` Operator string `json:"operator"` - Right *ExpressionImpl `json:"right"` + Right json.RawMessage `json:"right"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetLeft(aux.Left) + left, err := utilJson.Unmarshal[Expression](aux.Left, ArithmeticsSyntheticFactories) + if err != nil { + return err + } + i.SetLeft(left) i.SetOperator(newToken(Token_ID, aux.Operator)) - i.SetRight(aux.Right) + right, err := utilJson.Unmarshal[Expression](aux.Right, ArithmeticsSyntheticFactories) + if err != nil { + return err + } + i.SetRight(right) return nil } func (i *FunctionCallImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` - Args []*ExpressionImpl `json:"args"` + Args []json.RawMessage `json:"args"` Callable *core.Reference[AbstractDefinition] `json:"callable"` }{} if err := json.Unmarshal(data, aux); err != nil { @@ -240,7 +253,11 @@ func (i *FunctionCallImpl) UnmarshalJSON(data []byte) error { } i.args = []Expression{} for _, item := range aux.Args { - i.SetArgsItem(item) + node, err := utilJson.Unmarshal[Expression](item, ArithmeticsSyntheticFactories) + if err != nil { + return err + } + i.SetArgsItem(node) } i.SetCallable(aux.Callable) return nil diff --git a/examples/statemachine/json_gen.go b/examples/statemachine/json_gen.go index 857beb05..b9ee5c16 100644 --- a/examples/statemachine/json_gen.go +++ b/examples/statemachine/json_gen.go @@ -6,6 +6,7 @@ import ( "encoding/json" core "typefox.dev/fastbelt" + utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -81,10 +82,10 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` - Events []*EventImpl `json:"events"` - Commands []*CommandImpl `json:"commands"` + Events []json.RawMessage `json:"events"` + Commands []json.RawMessage `json:"commands"` Init *core.Reference[State] `json:"init"` - States []*StateImpl `json:"states"` + States []json.RawMessage `json:"states"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -92,16 +93,28 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.events = []Event{} for _, item := range aux.Events { - i.SetEventsItem(item) + node, err := utilJson.Unmarshal[Event](item, StatemachineModelSyntheticFactories) + if err != nil { + return err + } + i.SetEventsItem(node) } i.commands = []Command{} for _, item := range aux.Commands { - i.SetCommandsItem(item) + node, err := utilJson.Unmarshal[Command](item, StatemachineModelSyntheticFactories) + if err != nil { + return err + } + i.SetCommandsItem(node) } i.SetInit(aux.Init) i.states = []State{} for _, item := range aux.States { - i.SetStatesItem(item) + node, err := utilJson.Unmarshal[State](item, StatemachineModelSyntheticFactories) + if err != nil { + return err + } + i.SetStatesItem(node) } return nil } @@ -135,7 +148,7 @@ func (i *StateImpl) UnmarshalJSON(data []byte) error { T__ string `json:"$type"` Name string `json:"name"` Actions []*core.Reference[Command] `json:"actions"` - Transitions []*TransitionImpl `json:"transitions"` + Transitions []json.RawMessage `json:"transitions"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -147,7 +160,11 @@ func (i *StateImpl) UnmarshalJSON(data []byte) error { } i.transitions = []Transition{} for _, item := range aux.Transitions { - i.SetTransitionsItem(item) + node, err := utilJson.Unmarshal[Transition](item, StatemachineModelSyntheticFactories) + if err != nil { + return err + } + i.SetTransitionsItem(node) } return nil } diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go index fecb6de6..f3b0e989 100644 --- a/internal/generator/json_generator.go +++ b/internal/generator/json_generator.go @@ -12,22 +12,34 @@ import ( "typefox.dev/fastbelt/util/codegen" ) -func GenerateJSON(grammr grammar.Grammar, packageName string) string { +func GenerateJSON(grammar grammar.Grammar, packageName string) string { node := codegen.NewNode() node.AppendLine("// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT.") node.AppendLine() node.AppendLine("package ", packageName) node.AppendLine() + content, requireUtilJson := generateFunctions(grammar) + node.AppendLine("import (") node.Indent(func(n codegen.Node) { n.AppendLine("\"encoding/json\"") n.AppendLine() n.AppendLine("core \"typefox.dev/fastbelt\"") + if requireUtilJson { + n.AppendLine("utilJson \"typefox.dev/fastbelt/util/json\"") + } }) node.AppendLine(")") node.AppendLine() + node.AppendNode(content) + + return FormatIfPossible(node.String()) +} + +func generateFunctions(grammar grammar.Grammar) (node codegen.Node, requireUtilJson bool) { + node = codegen.NewNode() node.AppendLine(("func newToken(tokenType *core.TokenType, view string) *core.Token {")) node.Indent(func(n codegen.Node) { n.AppendLine("token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0)") @@ -36,15 +48,15 @@ func GenerateJSON(grammr grammar.Grammar, packageName string) string { node.AppendLine("}") node.AppendLine() - for _, iface := range grammr.Interfaces() { + for _, iface := range grammar.Interfaces() { generateJSONMarshal(node, iface) } - for _, iface := range grammr.Interfaces() { - generateJSONUnmarshal(node, iface) + for _, iface := range grammar.Interfaces() { + requireUtilJson = generateJSONUnmarshal(node, iface) || requireUtilJson } - return FormatIfPossible(node.String()) + return node, requireUtilJson } func collectAllFields(iface grammar.Interface, visited map[string]struct{}) []FieldInfo { @@ -101,34 +113,40 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { } func getAuxFieldType(field FieldInfo) string { + var typ string if field.Reference { - if field.Array { - return "[]" + field.GType - } - return field.Type + typ = field.GType + } else if field.Boolean { + typ = "bool" + } else if field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE { + typ = "string" + } else { + typ = "json.RawMessage" } + if field.Array { - if field.Boolean { - return "[]bool" - } - if field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE { - return "[]string" - } - return "[]*" + field.GType + "Impl" - } - if field.Boolean { - return "bool" + return "[]" + typ + } else { + return typ } - if field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE { - return "string" - } - return "*" + field.GType + "Impl" } -func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { +func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) (requireUtilJson bool) { fields := collectAllFields(iface, map[string]struct{}{}) node.AppendLine("func (i *", iface.Name(), "Impl) UnmarshalJSON(data []byte) error {") + + if len(fields) == 0 { + node.Indent(func(n2 codegen.Node) { + n2.AppendLine("return nil") + }) + node.AppendLine("}") + node.AppendLine() + return + } + + factoriesName := iface.Container().(grammar.Grammar).Name() + "SyntheticFactories" + node.Indent(func(n codegen.Node) { n.AppendLine("aux := &struct {") n.Indent(func(n2 codegen.Node) { @@ -140,10 +158,7 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { }) n.AppendLine("}{}") n.AppendLine("if err := json.Unmarshal(data, aux); err != nil {") - n.Indent(func(n2 codegen.Node) { - n2.AppendLine("return err") - }) - n.AppendLine("}") + genReturnErr(n) hasComposeNodeTempVar := false for _, field := range fields { @@ -155,11 +170,16 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { case TOKEN_TYPE: n2.AppendLine("i.Set", field.Name, "Item(newToken(Token_ID, item))") case COMPOSITE_TYPE: - n.AppendLine("cn := core.NewCompositeNode()") - n.AppendLine("cn.SetToken(newToken(Token_ID, item))") + n2.AppendLine("cn := core.NewCompositeNode()") + n2.AppendLine("cn.SetToken(newToken(Token_ID, item))") n2.AppendLine("i.Set", field.Name, "Item(cn)") default: - n2.AppendLine("i.Set", field.Name, "Item(item)") + if field.Reference { + n2.AppendLine("i.Set", field.Name, "Item(item)") + } else { + genUnmarshalChild(n2, field, "item", "node", true, factoriesName) + requireUtilJson = true + } } }) n.AppendLine("}") @@ -172,12 +192,36 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { n.AppendLine("cn = core.NewCompositeNode()") n.AppendLine("cn.SetToken(newToken(Token_ID, aux.", field.Name, "))") n.AppendLine("i.Set", field.Name, "(cn)") - } else { + } else if field.Reference { n.AppendLine("i.Set", field.Name, "(aux.", field.Name, ")") + } else { + genUnmarshalChild(n, field, "aux."+field.Name, field.PName, false, factoriesName) + requireUtilJson = true } } n.AppendLine("return nil") }) node.AppendLine("}") node.AppendLine() + + return requireUtilJson +} + +func genUnmarshalChild(node codegen.Node, field FieldInfo, srcName string, targetName string, loopItem bool, factoriesName string) { + node.AppendLine(targetName, ", err := utilJson.Unmarshal[", field.Type, "](", srcName, ", ", factoriesName, ")") + node.AppendLine("if err != nil {") + genReturnErr(node) + + if loopItem { + node.AppendLine("i.Set", field.Name, "Item(", targetName, ")") + } else { + node.AppendLine("i.Set", field.Name, "(", targetName, ")") + } +} + +func genReturnErr(node codegen.Node) { + node.Indent(func(n2 codegen.Node) { + n2.AppendLine("return err") + }) + node.AppendLine("}") } diff --git a/internal/grammar/json_gen.go b/internal/grammar/json_gen.go index 2f4aa6dd..efcee68a 100644 --- a/internal/grammar/json_gen.go +++ b/internal/grammar/json_gen.go @@ -6,6 +6,7 @@ import ( "encoding/json" core "typefox.dev/fastbelt" + utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -311,13 +312,13 @@ func (i *CompositeRuleImpl) MarshalJSON() ([]byte, error) { func (i *GrammarImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Rules []*ParserRuleImpl `json:"rules"` - Composites []*CompositeRuleImpl `json:"composites"` - Terminals []*TokenImpl `json:"terminals"` - TokenGroups []*TokenGroupImpl `json:"tokenGroups"` - Interfaces []*InterfaceImpl `json:"interfaces"` + T__ string `json:"$type"` + Name string `json:"name"` + Rules []json.RawMessage `json:"rules"` + Composites []json.RawMessage `json:"composites"` + Terminals []json.RawMessage `json:"terminals"` + TokenGroups []json.RawMessage `json:"tokenGroups"` + Interfaces []json.RawMessage `json:"interfaces"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -325,23 +326,43 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.rules = []ParserRule{} for _, item := range aux.Rules { - i.SetRulesItem(item) + node, err := utilJson.Unmarshal[ParserRule](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetRulesItem(node) } i.composites = []CompositeRule{} for _, item := range aux.Composites { - i.SetCompositesItem(item) + node, err := utilJson.Unmarshal[CompositeRule](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetCompositesItem(node) } i.terminals = []Token{} for _, item := range aux.Terminals { - i.SetTerminalsItem(item) + node, err := utilJson.Unmarshal[Token](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetTerminalsItem(node) } i.tokenGroups = []TokenGroup{} for _, item := range aux.TokenGroups { - i.SetTokenGroupsItem(item) + node, err := utilJson.Unmarshal[TokenGroup](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetTokenGroupsItem(node) } i.interfaces = []Interface{} for _, item := range aux.Interfaces { - i.SetInterfacesItem(item) + node, err := utilJson.Unmarshal[Interface](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetInterfacesItem(node) } return nil } @@ -351,7 +372,7 @@ func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { T__ string `json:"$type"` Name string `json:"name"` Extends []*core.Reference[Interface] `json:"extends"` - Fields []*FieldImpl `json:"fields"` + Fields []json.RawMessage `json:"fields"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -363,44 +384,50 @@ func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { } i.fields = []Field{} for _, item := range aux.Fields { - i.SetFieldsItem(item) + node, err := utilJson.Unmarshal[Field](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetFieldsItem(node) } return nil } func (i *FieldImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Type *FieldTypeImpl `json:"type"` + T__ string `json:"$type"` + Name string `json:"name"` + Type json.RawMessage `json:"type"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.SetType(aux.Type) + _Type, err := utilJson.Unmarshal[FieldType](aux.Type, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetType(_Type) return nil } func (i *FieldTypeImpl) UnmarshalJSON(data []byte) error { - aux := &struct { - T__ string `json:"$type"` - }{} - if err := json.Unmarshal(data, aux); err != nil { - return err - } return nil } func (i *ArrayTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - InternalType *FieldTypeImpl `json:"internalType"` + T__ string `json:"$type"` + InternalType json.RawMessage `json:"internalType"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetInternalType(aux.InternalType) + internalType, err := utilJson.Unmarshal[FieldType](aux.InternalType, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetInternalType(internalType) return nil } @@ -454,15 +481,19 @@ func (i *AbstractRuleImpl) UnmarshalJSON(data []byte) error { func (i *AbstractRuleWithBodyImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Body *ElementImpl `json:"body"` + T__ string `json:"$type"` + Name string `json:"name"` + Body json.RawMessage `json:"body"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.SetBody(aux.Body) + body, err := utilJson.Unmarshal[Element](aux.Body, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetBody(body) return nil } @@ -482,14 +513,18 @@ func (i *ParserRuleImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Name string `json:"name"` - Body *ElementImpl `json:"body"` + Body json.RawMessage `json:"body"` ReturnType *core.Reference[Interface] `json:"returnType"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.SetBody(aux.Body) + body, err := utilJson.Unmarshal[Element](aux.Body, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetBody(body) i.SetReturnType(aux.ReturnType) return nil } @@ -516,7 +551,7 @@ func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { Name string `json:"name"` TokenRefs []*core.Reference[AbstractTokenRule] `json:"tokenRefs"` Regexps []string `json:"regexps"` - Keywords []*KeywordImpl `json:"keywords"` + Keywords []json.RawMessage `json:"keywords"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -532,7 +567,11 @@ func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { } i.keywords = []Keyword{} for _, item := range aux.Keywords { - i.SetKeywordsItem(item) + node, err := utilJson.Unmarshal[Keyword](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetKeywordsItem(node) } return nil } @@ -551,9 +590,9 @@ func (i *ElementImpl) UnmarshalJSON(data []byte) error { func (i *AlternativesImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Alts []*ElementImpl `json:"alts"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Alts []json.RawMessage `json:"alts"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -561,16 +600,20 @@ func (i *AlternativesImpl) UnmarshalJSON(data []byte) error { i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.alts = []Element{} for _, item := range aux.Alts { - i.SetAltsItem(item) + node, err := utilJson.Unmarshal[Element](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetAltsItem(node) } return nil } func (i *GroupImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Elements []*ElementImpl `json:"elements"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Elements []json.RawMessage `json:"elements"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -578,7 +621,11 @@ func (i *GroupImpl) UnmarshalJSON(data []byte) error { i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.elements = []Element{} for _, item := range aux.Elements { - i.SetElementsItem(item) + node, err := utilJson.Unmarshal[Element](item, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetElementsItem(node) } return nil } @@ -603,7 +650,7 @@ func (i *AssignmentImpl) UnmarshalJSON(data []byte) error { Cardinality string `json:"cardinality"` Property *core.Reference[Field] `json:"property"` Operator string `json:"operator"` - Value *AssignableImpl `json:"value"` + Value json.RawMessage `json:"value"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -611,7 +658,11 @@ func (i *AssignmentImpl) UnmarshalJSON(data []byte) error { i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.SetProperty(aux.Property) i.SetOperator(newToken(Token_ID, aux.Operator)) - i.SetValue(aux.Value) + value, err := utilJson.Unmarshal[Assignable](aux.Value, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetValue(value) return nil } @@ -632,14 +683,18 @@ func (i *CrossRefImpl) UnmarshalJSON(data []byte) error { T__ string `json:"$type"` Cardinality string `json:"cardinality"` Type *core.Reference[Interface] `json:"type"` - Rule *RuleCallImpl `json:"rule"` + Rule json.RawMessage `json:"rule"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.SetType(aux.Type) - i.SetRule(aux.Rule) + rule, err := utilJson.Unmarshal[RuleCall](aux.Rule, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetRule(rule) return nil } @@ -677,14 +732,18 @@ func (i *ActionImpl) UnmarshalJSON(data []byte) error { func (i *CompositeRuleImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Body *ElementImpl `json:"body"` + T__ string `json:"$type"` + Name string `json:"name"` + Body json.RawMessage `json:"body"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.SetBody(aux.Body) + body, err := utilJson.Unmarshal[Element](aux.Body, FastbeltSyntheticFactories) + if err != nil { + return err + } + i.SetBody(body) return nil } diff --git a/internal/languages/completion/json_gen.go b/internal/languages/completion/json_gen.go index 8518e7bf..3c7ea7f1 100644 --- a/internal/languages/completion/json_gen.go +++ b/internal/languages/completion/json_gen.go @@ -6,6 +6,7 @@ import ( "encoding/json" core "typefox.dev/fastbelt" + utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -138,35 +139,33 @@ func (i *NImpl) MarshalJSON() ([]byte, error) { } func (i *ObjImpl) UnmarshalJSON(data []byte) error { - aux := &struct { - T__ string `json:"$type"` - }{} - if err := json.Unmarshal(data, aux); err != nil { - return err - } return nil } func (i *RootImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Objects []*ObjImpl `json:"objects"` + T__ string `json:"$type"` + Objects []json.RawMessage `json:"objects"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.objects = []Obj{} for _, item := range aux.Objects { - i.SetObjectsItem(item) + node, err := utilJson.Unmarshal[Obj](item, CompletionSyntheticFactories) + if err != nil { + return err + } + i.SetObjectsItem(node) } return nil } func (i *DeclareImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Children []*DeclareImpl `json:"children"` + T__ string `json:"$type"` + Name string `json:"name"` + Children []json.RawMessage `json:"children"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -177,7 +176,11 @@ func (i *DeclareImpl) UnmarshalJSON(data []byte) error { i.SetName(cn) i.children = []Declare{} for _, item := range aux.Children { - i.SetChildrenItem(item) + node, err := utilJson.Unmarshal[Declare](item, CompletionSyntheticFactories) + if err != nil { + return err + } + i.SetChildrenItem(node) } return nil } @@ -196,15 +199,19 @@ func (i *EImpl) UnmarshalJSON(data []byte) error { func (i *FImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Items []*FItemImpl `json:"items"` + T__ string `json:"$type"` + Items []json.RawMessage `json:"items"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.items = []FItem{} for _, item := range aux.Items { - i.SetItemsItem(item) + node, err := utilJson.Unmarshal[FItem](item, CompletionSyntheticFactories) + if err != nil { + return err + } + i.SetItemsItem(node) } return nil } @@ -236,12 +243,16 @@ func (i *GImpl) UnmarshalJSON(data []byte) error { func (i *HImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` - Member *MemberCallImpl `json:"member"` + Member json.RawMessage `json:"member"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetMember(aux.Member) + member, err := utilJson.Unmarshal[MemberCall](aux.Member, CompletionSyntheticFactories) + if err != nil { + return err + } + i.SetMember(member) return nil } @@ -249,13 +260,17 @@ func (i *MemberCallImpl) UnmarshalJSON(data []byte) error { aux := &struct { T__ string `json:"$type"` Ref *core.Reference[Declare] `json:"ref"` - Previous *MemberCallImpl `json:"previous"` + Previous json.RawMessage `json:"previous"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetRef(aux.Ref) - i.SetPrevious(aux.Previous) + previous, err := utilJson.Unmarshal[MemberCall](aux.Previous, CompletionSyntheticFactories) + if err != nil { + return err + } + i.SetPrevious(previous) return nil } diff --git a/internal/languages/token_groups/json_gen.go b/internal/languages/token_groups/json_gen.go index 86e1b50a..34a02a93 100644 --- a/internal/languages/token_groups/json_gen.go +++ b/internal/languages/token_groups/json_gen.go @@ -6,6 +6,7 @@ import ( "encoding/json" core "typefox.dev/fastbelt" + utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -49,13 +50,17 @@ func (i *RecoveryImpl) MarshalJSON() ([]byte, error) { func (i *ModelImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Item *ItemImpl `json:"item"` + T__ string `json:"$type"` + Item json.RawMessage `json:"item"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetItem(aux.Item) + item, err := utilJson.Unmarshal[Item](aux.Item, token_groupsSyntheticFactories) + if err != nil { + return err + } + i.SetItem(item) return nil } diff --git a/util/json/json.go b/util/json/json.go new file mode 100644 index 00000000..d9212175 --- /dev/null +++ b/util/json/json.go @@ -0,0 +1,54 @@ +// Copyright 2026 TypeFox GmbH +// This program and the accompanying materials are made available under the +// terms of the MIT License, which is available in the project root. + +package json + +import ( + "encoding/json" + "fmt" + "reflect" + + core "typefox.dev/fastbelt" +) + +// Unmarshal decodes data into T by reading the "$type" field to select a factory from factories. +func Unmarshal[T any](data []byte, factories map[string]func() core.AstNode) (T, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal: %w", err) + } + + typeRaw, ok := raw["$type"] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: missing $type field") + } + + var typeName string + if err := json.Unmarshal(typeRaw, &typeName); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal $type: %w", err) + } + + factory, ok := factories[typeName] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: unknown type %q", typeName) + } + + instance := factory() + casted, ok := instance.(T) + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + } + + if err := json.Unmarshal(data, casted); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", typeName, err) + } + + return casted, nil +} From 159c62894b6c46550813122b0a6346dd2493ee2b Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Wed, 17 Jun 2026 20:10:54 +0200 Subject: [PATCH 04/16] improvement in 'util/json.go' --- util/json/json.go | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/util/json/json.go b/util/json/json.go index d9212175..17d308ca 100644 --- a/util/json/json.go +++ b/util/json/json.go @@ -14,28 +14,18 @@ import ( // Unmarshal decodes data into T by reading the "$type" field to select a factory from factories. func Unmarshal[T any](data []byte, factories map[string]func() core.AstNode) (T, error) { - var raw map[string]json.RawMessage - if err := json.Unmarshal(data, &raw); err != nil { + node := &struct { + Type string `json:"$type"` + }{} + if err := json.Unmarshal(data, node); err != nil { var zero T return zero, fmt.Errorf("unmarshal: %w", err) } - typeRaw, ok := raw["$type"] + factory, ok := factories[node.Type] if !ok { var zero T - return zero, fmt.Errorf("unmarshal: missing $type field") - } - - var typeName string - if err := json.Unmarshal(typeRaw, &typeName); err != nil { - var zero T - return zero, fmt.Errorf("unmarshal $type: %w", err) - } - - factory, ok := factories[typeName] - if !ok { - var zero T - return zero, fmt.Errorf("unmarshal: unknown type %q", typeName) + return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) } instance := factory() @@ -47,7 +37,7 @@ func Unmarshal[T any](data []byte, factories map[string]func() core.AstNode) (T, if err := json.Unmarshal(data, casted); err != nil { var zero T - return zero, fmt.Errorf("unmarshal %s: %w", typeName, err) + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) } return casted, nil From 8107a78c200f760bd4dc3c515ac46cd393ba2846 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Wed, 17 Jun 2026 20:21:50 +0200 Subject: [PATCH 05/16] hot fix in reference.go --- reference.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/reference.go b/reference.go index aafc4445..4754445f 100644 --- a/reference.go +++ b/reference.go @@ -7,7 +7,6 @@ package fastbelt import ( "context" "encoding/json" - "errors" "iter" "reflect" "slices" @@ -303,19 +302,22 @@ func (r *Reference[T]) MarshalJSON() ([]byte, error) { return []byte("null"), nil } // We expect at this point that all references have been attempted to resolve. - if !r.resolved.Load() { - return nil, errors.New("reference not resolved") - } + // if !r.resolved.Load() { + // return nil, errors.New("reference not resolved") + // } var uri string var errMsg string if r.err != nil { errMsg = r.err.Msg - } else if doc := r.ref.Document(); doc != nil { - uri = doc.URI.WithFragment("todo-fragment").StringUnencoded() - } else { - return nil, errors.New("unexpected state: resolved reference has no document") + } else if ref := r.ref; any(ref) != nil { + if doc := ref.Document(); doc != nil { + uri = doc.URI.WithFragment("todo-fragment").StringUnencoded() + } } + // else { + // return nil, errors.New("unexpected state: resolved reference has no document") + // } return json.Marshal(struct { RefText string `json:"$refText"` From 63d94b9a06e0e376f2aa4406d95d8d73efc95db2 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Thu, 2 Jul 2026 11:45:23 +0200 Subject: [PATCH 06/16] progress, linking seems to work --- examples/arithmetics/json_gen.go | 57 ++++-- examples/statemachine/json_gen.go | 89 ++++++--- internal/generator/json_generator.go | 119 +++++++----- internal/grammar/json_gen.go | 197 ++++++++++++++------ internal/languages/completion/json_gen.go | 147 ++++++++++++--- internal/languages/lookahead/json_gen.go | 132 +++++++++++++ internal/languages/token_groups/json_gen.go | 33 +++- reference.go | 119 +++++++++--- util/json/json.go | 69 ++++--- 9 files changed, 737 insertions(+), 225 deletions(-) create mode 100644 internal/languages/lookahead/json_gen.go diff --git a/examples/arithmetics/json_gen.go b/examples/arithmetics/json_gen.go index a6824eec..90d4cf28 100644 --- a/examples/arithmetics/json_gen.go +++ b/examples/arithmetics/json_gen.go @@ -4,9 +4,10 @@ package arithmetics import ( "encoding/json" + "fmt" + "reflect" core "typefox.dev/fastbelt" - utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -134,7 +135,7 @@ func (i *ModuleImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.statements = []Statement{} for _, item := range aux.Statements { - node, err := utilJson.Unmarshal[Statement](item, ArithmeticsSyntheticFactories) + node, err := Unmarshal[Statement](item) if err != nil { return err } @@ -172,13 +173,13 @@ func (i *DefinitionImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.args = []DeclaredParameter{} for _, item := range aux.Args { - node, err := utilJson.Unmarshal[DeclaredParameter](item, ArithmeticsSyntheticFactories) + node, err := Unmarshal[DeclaredParameter](item) if err != nil { return err } i.SetArgsItem(node) } - expression, err := utilJson.Unmarshal[Expression](aux.Expression, ArithmeticsSyntheticFactories) + expression, err := Unmarshal[Expression](aux.Expression) if err != nil { return err } @@ -206,7 +207,7 @@ func (i *EvaluationImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - expression, err := utilJson.Unmarshal[Expression](aux.Expression, ArithmeticsSyntheticFactories) + expression, err := Unmarshal[Expression](aux.Expression) if err != nil { return err } @@ -228,13 +229,13 @@ func (i *BinaryExpressionImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - left, err := utilJson.Unmarshal[Expression](aux.Left, ArithmeticsSyntheticFactories) + left, err := Unmarshal[Expression](aux.Left) if err != nil { return err } i.SetLeft(left) i.SetOperator(newToken(Token_ID, aux.Operator)) - right, err := utilJson.Unmarshal[Expression](aux.Right, ArithmeticsSyntheticFactories) + right, err := Unmarshal[Expression](aux.Right) if err != nil { return err } @@ -244,22 +245,26 @@ func (i *BinaryExpressionImpl) UnmarshalJSON(data []byte) error { func (i *FunctionCallImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Args []json.RawMessage `json:"args"` - Callable *core.Reference[AbstractDefinition] `json:"callable"` + T__ string `json:"$type"` + Args []json.RawMessage `json:"args"` + Callable json.RawMessage `json:"callable"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.args = []Expression{} for _, item := range aux.Args { - node, err := utilJson.Unmarshal[Expression](item, ArithmeticsSyntheticFactories) + node, err := Unmarshal[Expression](item) if err != nil { return err } i.SetArgsItem(node) } - i.SetCallable(aux.Callable) + callable := core.NewReference[AbstractDefinition](i, nil, nil) + if err := json.Unmarshal(aux.Callable, &callable); err != nil { + return err + } + i.SetCallable(callable) return nil } @@ -274,3 +279,31 @@ func (i *NumberLiteralImpl) UnmarshalJSON(data []byte) error { i.SetValue(newToken(Token_ID, aux.Value)) return nil } + +// Unmarshal decodes data into an instance of type T by reading the "$type" field, +// selecting a corresponding factory, creating an instance, and unmarshaling its content. +func Unmarshal[T core.AstNode](data []byte) (T, error) { + node := &struct { + Type string `json:"$type"` + }{} + if err := json.Unmarshal(data, node); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal: %w", err) + } + factory, ok := ArithmeticsSyntheticFactories[node.Type] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) + } + instance := factory() + casted, ok := instance.(T) + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + } + if err := json.Unmarshal(data, casted); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + return casted, nil +} diff --git a/examples/statemachine/json_gen.go b/examples/statemachine/json_gen.go index b9ee5c16..705d96f6 100644 --- a/examples/statemachine/json_gen.go +++ b/examples/statemachine/json_gen.go @@ -4,9 +4,10 @@ package statemachine import ( "encoding/json" + "fmt" + "reflect" core "typefox.dev/fastbelt" - utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -80,12 +81,12 @@ func (i *TransitionImpl) MarshalJSON() ([]byte, error) { func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Events []json.RawMessage `json:"events"` - Commands []json.RawMessage `json:"commands"` - Init *core.Reference[State] `json:"init"` - States []json.RawMessage `json:"states"` + T__ string `json:"$type"` + Name string `json:"name"` + Events []json.RawMessage `json:"events"` + Commands []json.RawMessage `json:"commands"` + Init json.RawMessage `json:"init"` + States []json.RawMessage `json:"states"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -93,7 +94,7 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.events = []Event{} for _, item := range aux.Events { - node, err := utilJson.Unmarshal[Event](item, StatemachineModelSyntheticFactories) + node, err := Unmarshal[Event](item) if err != nil { return err } @@ -101,16 +102,20 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { } i.commands = []Command{} for _, item := range aux.Commands { - node, err := utilJson.Unmarshal[Command](item, StatemachineModelSyntheticFactories) + node, err := Unmarshal[Command](item) if err != nil { return err } i.SetCommandsItem(node) } - i.SetInit(aux.Init) + init := core.NewReference[State](i, nil, nil) + if err := json.Unmarshal(aux.Init, &init); err != nil { + return err + } + i.SetInit(init) i.states = []State{} for _, item := range aux.States { - node, err := utilJson.Unmarshal[State](item, StatemachineModelSyntheticFactories) + node, err := Unmarshal[State](item) if err != nil { return err } @@ -145,10 +150,10 @@ func (i *CommandImpl) UnmarshalJSON(data []byte) error { func (i *StateImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Actions []*core.Reference[Command] `json:"actions"` - Transitions []json.RawMessage `json:"transitions"` + T__ string `json:"$type"` + Name string `json:"name"` + Actions []json.RawMessage `json:"actions"` + Transitions []json.RawMessage `json:"transitions"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -156,11 +161,15 @@ func (i *StateImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.actions = []*core.Reference[Command]{} for _, item := range aux.Actions { - i.SetActionsItem(item) + node := core.NewReference[Command](i, nil, nil) + if err := json.Unmarshal(item, &node); err != nil { + return err + } + i.SetActionsItem(node) } i.transitions = []Transition{} for _, item := range aux.Transitions { - node, err := utilJson.Unmarshal[Transition](item, StatemachineModelSyntheticFactories) + node, err := Unmarshal[Transition](item) if err != nil { return err } @@ -171,14 +180,50 @@ func (i *StateImpl) UnmarshalJSON(data []byte) error { func (i *TransitionImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Event *core.Reference[Event] `json:"event"` - State *core.Reference[State] `json:"state"` + T__ string `json:"$type"` + Event json.RawMessage `json:"event"` + State json.RawMessage `json:"state"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetEvent(aux.Event) - i.SetState(aux.State) + event := core.NewReference[Event](i, nil, nil) + if err := json.Unmarshal(aux.Event, &event); err != nil { + return err + } + i.SetEvent(event) + state := core.NewReference[State](i, nil, nil) + if err := json.Unmarshal(aux.State, &state); err != nil { + return err + } + i.SetState(state) return nil } + +// Unmarshal decodes data into an instance of type T by reading the "$type" field, +// selecting a corresponding factory, creating an instance, and unmarshaling its content. +func Unmarshal[T core.AstNode](data []byte) (T, error) { + node := &struct { + Type string `json:"$type"` + }{} + if err := json.Unmarshal(data, node); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal: %w", err) + } + factory, ok := StatemachineModelSyntheticFactories[node.Type] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) + } + instance := factory() + casted, ok := instance.(T) + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + } + if err := json.Unmarshal(data, casted); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + return casted, nil +} diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go index f3b0e989..d8d7f1c4 100644 --- a/internal/generator/json_generator.go +++ b/internal/generator/json_generator.go @@ -5,7 +5,6 @@ package generator import ( - "context" "strings" "typefox.dev/fastbelt/internal/grammar" @@ -19,27 +18,23 @@ func GenerateJSON(grammar grammar.Grammar, packageName string) string { node.AppendLine("package ", packageName) node.AppendLine() - content, requireUtilJson := generateFunctions(grammar) - node.AppendLine("import (") node.Indent(func(n codegen.Node) { n.AppendLine("\"encoding/json\"") + n.AppendLine("\"fmt\"") + n.AppendLine("\"reflect\"") n.AppendLine() n.AppendLine("core \"typefox.dev/fastbelt\"") - if requireUtilJson { - n.AppendLine("utilJson \"typefox.dev/fastbelt/util/json\"") - } }) node.AppendLine(")") node.AppendLine() - node.AppendNode(content) + generateFunctions(grammar, node) return FormatIfPossible(node.String()) } -func generateFunctions(grammar grammar.Grammar) (node codegen.Node, requireUtilJson bool) { - node = codegen.NewNode() +func generateFunctions(grammar grammar.Grammar, node codegen.Node) { node.AppendLine(("func newToken(tokenType *core.TokenType, view string) *core.Token {")) node.Indent(func(n codegen.Node) { n.AppendLine("token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0)") @@ -53,27 +48,10 @@ func generateFunctions(grammar grammar.Grammar) (node codegen.Node, requireUtilJ } for _, iface := range grammar.Interfaces() { - requireUtilJson = generateJSONUnmarshal(node, iface) || requireUtilJson + generateJSONUnmarshal(node, iface) } - return node, requireUtilJson -} - -func collectAllFields(iface grammar.Interface, visited map[string]struct{}) []FieldInfo { - if _, seen := visited[iface.Name()]; seen { - return nil - } - visited[iface.Name()] = struct{}{} - fields := []FieldInfo{} - for _, ext := range iface.Extends() { - if parent := ext.Ref(context.TODO()); parent != nil { - fields = append(fields, collectAllFields(parent, visited)...) - } - } - for _, field := range iface.Fields() { - fields = append(fields, getFieldInfo(field)) - } - return fields + generateDispatchingUnmarshalFunc(node, grammar) } func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { @@ -114,9 +92,7 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { func getAuxFieldType(field FieldInfo) string { var typ string - if field.Reference { - typ = field.GType - } else if field.Boolean { + if field.Boolean { typ = "bool" } else if field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE { typ = "string" @@ -131,7 +107,7 @@ func getAuxFieldType(field FieldInfo) string { } } -func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) (requireUtilJson bool) { +func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { fields := collectAllFields(iface, map[string]struct{}{}) node.AppendLine("func (i *", iface.Name(), "Impl) UnmarshalJSON(data []byte) error {") @@ -145,8 +121,6 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) (requireU return } - factoriesName := iface.Container().(grammar.Grammar).Name() + "SyntheticFactories" - node.Indent(func(n codegen.Node) { n.AppendLine("aux := &struct {") n.Indent(func(n2 codegen.Node) { @@ -175,15 +149,20 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) (requireU n2.AppendLine("i.Set", field.Name, "Item(cn)") default: if field.Reference { - n2.AppendLine("i.Set", field.Name, "Item(item)") + genUnmarshalReference(n2, field, "item", "node", true) } else { - genUnmarshalChild(n2, field, "item", "node", true, factoriesName) - requireUtilJson = true + genUnmarshalChild(n2, field, "item", "node", true) } } }) n.AppendLine("}") - } else if field.Boolean || field.GType == TOKEN_TYPE { + } else if field.Boolean { + n.AppendLine("if aux.", field.Name, "{") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("i.Set", field.Name, "(newToken(Token_ID, \"\"))") + }) + n.AppendLine("}") + } else if field.GType == TOKEN_TYPE { n.AppendLine("i.Set", field.Name, "(newToken(Token_ID, aux.", field.Name, "))") } else if field.GType == COMPOSITE_TYPE { if !hasComposeNodeTempVar { @@ -193,22 +172,31 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) (requireU n.AppendLine("cn.SetToken(newToken(Token_ID, aux.", field.Name, "))") n.AppendLine("i.Set", field.Name, "(cn)") } else if field.Reference { - n.AppendLine("i.Set", field.Name, "(aux.", field.Name, ")") + genUnmarshalReference(n, field, "aux."+field.Name, field.PName, false) } else { - genUnmarshalChild(n, field, "aux."+field.Name, field.PName, false, factoriesName) - requireUtilJson = true + genUnmarshalChild(n, field, "aux."+field.Name, field.PName, false) } } n.AppendLine("return nil") }) node.AppendLine("}") node.AppendLine() +} + +func genUnmarshalReference(node codegen.Node, field FieldInfo, srcName string, targetName string, loopItem bool) { + node.AppendLine(targetName, " := core.NewReference[", strings.Split(field.Type, "[")[1], "(i, nil, nil)") + node.AppendLine("if err := json.Unmarshal(", srcName, ", &", targetName, "); err != nil {") + genReturnErr(node) - return requireUtilJson + if loopItem { + node.AppendLine("i.Set", field.Name, "Item(", targetName, ")") + } else { + node.AppendLine("i.Set", field.Name, "(", targetName, ")") + } } -func genUnmarshalChild(node codegen.Node, field FieldInfo, srcName string, targetName string, loopItem bool, factoriesName string) { - node.AppendLine(targetName, ", err := utilJson.Unmarshal[", field.Type, "](", srcName, ", ", factoriesName, ")") +func genUnmarshalChild(node codegen.Node, field FieldInfo, srcName string, targetName string, loopItem bool) { + node.AppendLine(targetName, ", err := Unmarshal[", field.Type, "](", srcName, ")") node.AppendLine("if err != nil {") genReturnErr(node) @@ -225,3 +213,46 @@ func genReturnErr(node codegen.Node) { }) node.AppendLine("}") } + +func generateDispatchingUnmarshalFunc(node codegen.Node, g grammar.Grammar) { + node.AppendLine(`// Unmarshal decodes data into an instance of type T by reading the "$type" field,`) + node.AppendLine(`// selecting a corresponding factory, creating an instance, and unmarshaling its content.`) + node.AppendLine("func Unmarshal[T core.AstNode](data []byte) (T, error) {") + node.Indent(func(n codegen.Node) { + n.AppendLine("node := &struct {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("Type string `json:\"$type\"`") + }) + n.AppendLine("}{}") + n.AppendLine("if err := json.Unmarshal(data, node); err != nil {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("var zero T") + n2.AppendLine(`return zero, fmt.Errorf("unmarshal: %w", err)`) + }) + n.AppendLine("}") + n.AppendLine("factory, ok := ", g.Name()+"SyntheticFactories", "[node.Type]") + n.AppendLine("if !ok {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("var zero T") + n2.AppendLine(`return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type)`) + }) + n.AppendLine("}") + n.AppendLine("instance := factory()") + n.AppendLine("casted, ok := instance.(T)") + n.AppendLine("if !ok {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("var zero T") + n2.AppendLine(`return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]())`) + }) + n.AppendLine("}") + n.AppendLine("if err := json.Unmarshal(data, casted); err != nil {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("var zero T") + n2.AppendLine(`return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err)`) + }) + n.AppendLine("}") + n.AppendLine("return casted, nil") + }) + node.AppendLine("}") + node.AppendLine() +} diff --git a/internal/grammar/json_gen.go b/internal/grammar/json_gen.go index efcee68a..79bd1601 100644 --- a/internal/grammar/json_gen.go +++ b/internal/grammar/json_gen.go @@ -4,9 +4,10 @@ package grammar import ( "encoding/json" + "fmt" + "reflect" core "typefox.dev/fastbelt" - utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -145,11 +146,13 @@ func (i *ParserRuleImpl) MarshalJSON() ([]byte, error) { T__ string `json:"$type"` Name string `json:"name"` Body Element `json:"body"` + Entry bool `json:"entry"` ReturnType *core.Reference[Interface] `json:"returnType"` }{ T__: "ParserRule", Name: i.Name(), Body: i.Body(), + Entry: i.IsEntry(), ReturnType: i.ReturnType(), }) } @@ -326,7 +329,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.rules = []ParserRule{} for _, item := range aux.Rules { - node, err := utilJson.Unmarshal[ParserRule](item, FastbeltSyntheticFactories) + node, err := Unmarshal[ParserRule](item) if err != nil { return err } @@ -334,7 +337,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.composites = []CompositeRule{} for _, item := range aux.Composites { - node, err := utilJson.Unmarshal[CompositeRule](item, FastbeltSyntheticFactories) + node, err := Unmarshal[CompositeRule](item) if err != nil { return err } @@ -342,7 +345,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.terminals = []Token{} for _, item := range aux.Terminals { - node, err := utilJson.Unmarshal[Token](item, FastbeltSyntheticFactories) + node, err := Unmarshal[Token](item) if err != nil { return err } @@ -350,7 +353,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.tokenGroups = []TokenGroup{} for _, item := range aux.TokenGroups { - node, err := utilJson.Unmarshal[TokenGroup](item, FastbeltSyntheticFactories) + node, err := Unmarshal[TokenGroup](item) if err != nil { return err } @@ -358,7 +361,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.interfaces = []Interface{} for _, item := range aux.Interfaces { - node, err := utilJson.Unmarshal[Interface](item, FastbeltSyntheticFactories) + node, err := Unmarshal[Interface](item) if err != nil { return err } @@ -369,10 +372,10 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Extends []*core.Reference[Interface] `json:"extends"` - Fields []json.RawMessage `json:"fields"` + T__ string `json:"$type"` + Name string `json:"name"` + Extends []json.RawMessage `json:"extends"` + Fields []json.RawMessage `json:"fields"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -380,11 +383,15 @@ func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.extends = []*core.Reference[Interface]{} for _, item := range aux.Extends { - i.SetExtendsItem(item) + node := core.NewReference[Interface](i, nil, nil) + if err := json.Unmarshal(item, &node); err != nil { + return err + } + i.SetExtendsItem(node) } i.fields = []Field{} for _, item := range aux.Fields { - node, err := utilJson.Unmarshal[Field](item, FastbeltSyntheticFactories) + node, err := Unmarshal[Field](item) if err != nil { return err } @@ -403,7 +410,7 @@ func (i *FieldImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - _Type, err := utilJson.Unmarshal[FieldType](aux.Type, FastbeltSyntheticFactories) + _Type, err := Unmarshal[FieldType](aux.Type) if err != nil { return err } @@ -423,7 +430,7 @@ func (i *ArrayTypeImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - internalType, err := utilJson.Unmarshal[FieldType](aux.InternalType, FastbeltSyntheticFactories) + internalType, err := Unmarshal[FieldType](aux.InternalType) if err != nil { return err } @@ -433,25 +440,33 @@ func (i *ArrayTypeImpl) UnmarshalJSON(data []byte) error { func (i *ReferenceTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` + T__ string `json:"$type"` + Type json.RawMessage `json:"type"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetType(aux.Type) + _Type := core.NewReference[Interface](i, nil, nil) + if err := json.Unmarshal(aux.Type, &_Type); err != nil { + return err + } + i.SetType(_Type) return nil } func (i *SimpleTypeImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` + T__ string `json:"$type"` + Type json.RawMessage `json:"type"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetType(aux.Type) + _Type := core.NewReference[Interface](i, nil, nil) + if err := json.Unmarshal(aux.Type, &_Type); err != nil { + return err + } + i.SetType(_Type) return nil } @@ -489,7 +504,7 @@ func (i *AbstractRuleWithBodyImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - body, err := utilJson.Unmarshal[Element](aux.Body, FastbeltSyntheticFactories) + body, err := Unmarshal[Element](aux.Body) if err != nil { return err } @@ -511,21 +526,29 @@ func (i *AbstractTokenRuleImpl) UnmarshalJSON(data []byte) error { func (i *ParserRuleImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - Body json.RawMessage `json:"body"` - ReturnType *core.Reference[Interface] `json:"returnType"` + T__ string `json:"$type"` + Name string `json:"name"` + Body json.RawMessage `json:"body"` + Entry bool `json:"entry"` + ReturnType json.RawMessage `json:"returnType"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetName(newToken(Token_ID, aux.Name)) - body, err := utilJson.Unmarshal[Element](aux.Body, FastbeltSyntheticFactories) + body, err := Unmarshal[Element](aux.Body) if err != nil { return err } i.SetBody(body) - i.SetReturnType(aux.ReturnType) + if aux.Entry { + i.SetEntry(newToken(Token_ID, "")) + } + returnType := core.NewReference[Interface](i, nil, nil) + if err := json.Unmarshal(aux.ReturnType, &returnType); err != nil { + return err + } + i.SetReturnType(returnType) return nil } @@ -547,11 +570,11 @@ func (i *TokenImpl) UnmarshalJSON(data []byte) error { func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Name string `json:"name"` - TokenRefs []*core.Reference[AbstractTokenRule] `json:"tokenRefs"` - Regexps []string `json:"regexps"` - Keywords []json.RawMessage `json:"keywords"` + T__ string `json:"$type"` + Name string `json:"name"` + TokenRefs []json.RawMessage `json:"tokenRefs"` + Regexps []string `json:"regexps"` + Keywords []json.RawMessage `json:"keywords"` }{} if err := json.Unmarshal(data, aux); err != nil { return err @@ -559,7 +582,11 @@ func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { i.SetName(newToken(Token_ID, aux.Name)) i.tokenRefs = []*core.Reference[AbstractTokenRule]{} for _, item := range aux.TokenRefs { - i.SetTokenRefsItem(item) + node := core.NewReference[AbstractTokenRule](i, nil, nil) + if err := json.Unmarshal(item, &node); err != nil { + return err + } + i.SetTokenRefsItem(node) } i.regexps = []*core.Token{} for _, item := range aux.Regexps { @@ -567,7 +594,7 @@ func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { } i.keywords = []Keyword{} for _, item := range aux.Keywords { - node, err := utilJson.Unmarshal[Keyword](item, FastbeltSyntheticFactories) + node, err := Unmarshal[Keyword](item) if err != nil { return err } @@ -600,7 +627,7 @@ func (i *AlternativesImpl) UnmarshalJSON(data []byte) error { i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.alts = []Element{} for _, item := range aux.Alts { - node, err := utilJson.Unmarshal[Element](item, FastbeltSyntheticFactories) + node, err := Unmarshal[Element](item) if err != nil { return err } @@ -621,7 +648,7 @@ func (i *GroupImpl) UnmarshalJSON(data []byte) error { i.SetCardinality(newToken(Token_ID, aux.Cardinality)) i.elements = []Element{} for _, item := range aux.Elements { - node, err := utilJson.Unmarshal[Element](item, FastbeltSyntheticFactories) + node, err := Unmarshal[Element](item) if err != nil { return err } @@ -646,19 +673,23 @@ func (i *KeywordImpl) UnmarshalJSON(data []byte) error { func (i *AssignmentImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Property *core.Reference[Field] `json:"property"` - Operator string `json:"operator"` - Value json.RawMessage `json:"value"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Property json.RawMessage `json:"property"` + Operator string `json:"operator"` + Value json.RawMessage `json:"value"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - i.SetProperty(aux.Property) + property := core.NewReference[Field](i, nil, nil) + if err := json.Unmarshal(aux.Property, &property); err != nil { + return err + } + i.SetProperty(property) i.SetOperator(newToken(Token_ID, aux.Operator)) - value, err := utilJson.Unmarshal[Assignable](aux.Value, FastbeltSyntheticFactories) + value, err := Unmarshal[Assignable](aux.Value) if err != nil { return err } @@ -680,17 +711,21 @@ func (i *AssignableImpl) UnmarshalJSON(data []byte) error { func (i *CrossRefImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Type *core.Reference[Interface] `json:"type"` - Rule json.RawMessage `json:"rule"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Type json.RawMessage `json:"type"` + Rule json.RawMessage `json:"rule"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - i.SetType(aux.Type) - rule, err := utilJson.Unmarshal[RuleCall](aux.Rule, FastbeltSyntheticFactories) + _Type := core.NewReference[Interface](i, nil, nil) + if err := json.Unmarshal(aux.Type, &_Type); err != nil { + return err + } + i.SetType(_Type) + rule, err := Unmarshal[RuleCall](aux.Rule) if err != nil { return err } @@ -700,33 +735,45 @@ func (i *CrossRefImpl) UnmarshalJSON(data []byte) error { func (i *RuleCallImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Rule *core.Reference[AbstractRule] `json:"rule"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Rule json.RawMessage `json:"rule"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - i.SetRule(aux.Rule) + rule := core.NewReference[AbstractRule](i, nil, nil) + if err := json.Unmarshal(aux.Rule, &rule); err != nil { + return err + } + i.SetRule(rule) return nil } func (i *ActionImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Type *core.Reference[Interface] `json:"type"` - Operator string `json:"operator"` - Property *core.Reference[Field] `json:"property"` + T__ string `json:"$type"` + Cardinality string `json:"cardinality"` + Type json.RawMessage `json:"type"` + Operator string `json:"operator"` + Property json.RawMessage `json:"property"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - i.SetType(aux.Type) + _Type := core.NewReference[Interface](i, nil, nil) + if err := json.Unmarshal(aux.Type, &_Type); err != nil { + return err + } + i.SetType(_Type) i.SetOperator(newToken(Token_ID, aux.Operator)) - i.SetProperty(aux.Property) + property := core.NewReference[Field](i, nil, nil) + if err := json.Unmarshal(aux.Property, &property); err != nil { + return err + } + i.SetProperty(property) return nil } @@ -740,10 +787,38 @@ func (i *CompositeRuleImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - body, err := utilJson.Unmarshal[Element](aux.Body, FastbeltSyntheticFactories) + body, err := Unmarshal[Element](aux.Body) if err != nil { return err } i.SetBody(body) return nil } + +// Unmarshal decodes data into an instance of type T by reading the "$type" field, +// selecting a corresponding factory, creating an instance, and unmarshaling its content. +func Unmarshal[T core.AstNode](data []byte) (T, error) { + node := &struct { + Type string `json:"$type"` + }{} + if err := json.Unmarshal(data, node); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal: %w", err) + } + factory, ok := FastbeltSyntheticFactories[node.Type] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) + } + instance := factory() + casted, ok := instance.(T) + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + } + if err := json.Unmarshal(data, casted); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + return casted, nil +} diff --git a/internal/languages/completion/json_gen.go b/internal/languages/completion/json_gen.go index 3c7ea7f1..48d42d5d 100644 --- a/internal/languages/completion/json_gen.go +++ b/internal/languages/completion/json_gen.go @@ -4,9 +4,10 @@ package completion import ( "encoding/json" + "fmt" + "reflect" core "typefox.dev/fastbelt" - utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -138,6 +139,16 @@ func (i *NImpl) MarshalJSON() ([]byte, error) { }) } +func (i *OImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Ref *core.Reference[Declare] `json:"ref"` + }{ + T__: "O", + Ref: i.Ref(), + }) +} + func (i *ObjImpl) UnmarshalJSON(data []byte) error { return nil } @@ -152,7 +163,7 @@ func (i *RootImpl) UnmarshalJSON(data []byte) error { } i.objects = []Obj{} for _, item := range aux.Objects { - node, err := utilJson.Unmarshal[Obj](item, CompletionSyntheticFactories) + node, err := Unmarshal[Obj](item) if err != nil { return err } @@ -176,7 +187,7 @@ func (i *DeclareImpl) UnmarshalJSON(data []byte) error { i.SetName(cn) i.children = []Declare{} for _, item := range aux.Children { - node, err := utilJson.Unmarshal[Declare](item, CompletionSyntheticFactories) + node, err := Unmarshal[Declare](item) if err != nil { return err } @@ -187,13 +198,17 @@ func (i *DeclareImpl) UnmarshalJSON(data []byte) error { func (i *EImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + T__ string `json:"$type"` + Ref json.RawMessage `json:"ref"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetRef(aux.Ref) + ref := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref, &ref); err != nil { + return err + } + i.SetRef(ref) return nil } @@ -207,7 +222,7 @@ func (i *FImpl) UnmarshalJSON(data []byte) error { } i.items = []FItem{} for _, item := range aux.Items { - node, err := utilJson.Unmarshal[FItem](item, CompletionSyntheticFactories) + node, err := Unmarshal[FItem](item) if err != nil { return err } @@ -218,25 +233,33 @@ func (i *FImpl) UnmarshalJSON(data []byte) error { func (i *FItemImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + T__ string `json:"$type"` + Ref json.RawMessage `json:"ref"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetRef(aux.Ref) + ref := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref, &ref); err != nil { + return err + } + i.SetRef(ref) return nil } func (i *GImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + T__ string `json:"$type"` + Ref json.RawMessage `json:"ref"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetRef(aux.Ref) + ref := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref, &ref); err != nil { + return err + } + i.SetRef(ref) return nil } @@ -248,7 +271,7 @@ func (i *HImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - member, err := utilJson.Unmarshal[MemberCall](aux.Member, CompletionSyntheticFactories) + member, err := Unmarshal[MemberCall](aux.Member) if err != nil { return err } @@ -258,15 +281,19 @@ func (i *HImpl) UnmarshalJSON(data []byte) error { func (i *MemberCallImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` - Previous json.RawMessage `json:"previous"` + T__ string `json:"$type"` + Ref json.RawMessage `json:"ref"` + Previous json.RawMessage `json:"previous"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetRef(aux.Ref) - previous, err := utilJson.Unmarshal[MemberCall](aux.Previous, CompletionSyntheticFactories) + ref := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref, &ref); err != nil { + return err + } + i.SetRef(ref) + previous, err := Unmarshal[MemberCall](aux.Previous) if err != nil { return err } @@ -276,38 +303,98 @@ func (i *MemberCallImpl) UnmarshalJSON(data []byte) error { func (i *JImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + T__ string `json:"$type"` + Ref json.RawMessage `json:"ref"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetRef(aux.Ref) + ref := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref, &ref); err != nil { + return err + } + i.SetRef(ref) return nil } func (i *KImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Ref1 *core.Reference[Declare] `json:"ref1"` - Ref2 *core.Reference[Declare] `json:"ref2"` + T__ string `json:"$type"` + Ref1 json.RawMessage `json:"ref1"` + Ref2 json.RawMessage `json:"ref2"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetRef1(aux.Ref1) - i.SetRef2(aux.Ref2) + ref1 := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref1, &ref1); err != nil { + return err + } + i.SetRef1(ref1) + ref2 := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref2, &ref2); err != nil { + return err + } + i.SetRef2(ref2) return nil } func (i *NImpl) UnmarshalJSON(data []byte) error { aux := &struct { - T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + T__ string `json:"$type"` + Ref json.RawMessage `json:"ref"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + ref := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref, &ref); err != nil { + return err + } + i.SetRef(ref) + return nil +} + +func (i *OImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Ref json.RawMessage `json:"ref"` }{} if err := json.Unmarshal(data, aux); err != nil { return err } - i.SetRef(aux.Ref) + ref := core.NewReference[Declare](i, nil, nil) + if err := json.Unmarshal(aux.Ref, &ref); err != nil { + return err + } + i.SetRef(ref) return nil } + +// Unmarshal decodes data into an instance of type T by reading the "$type" field, +// selecting a corresponding factory, creating an instance, and unmarshaling its content. +func Unmarshal[T core.AstNode](data []byte) (T, error) { + node := &struct { + Type string `json:"$type"` + }{} + if err := json.Unmarshal(data, node); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal: %w", err) + } + factory, ok := CompletionSyntheticFactories[node.Type] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) + } + instance := factory() + casted, ok := instance.(T) + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + } + if err := json.Unmarshal(data, casted); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + return casted, nil +} diff --git a/internal/languages/lookahead/json_gen.go b/internal/languages/lookahead/json_gen.go new file mode 100644 index 00000000..16c2f49f --- /dev/null +++ b/internal/languages/lookahead/json_gen.go @@ -0,0 +1,132 @@ +// Code generated by typefox.dev/fastbelt/cmd/fastbelt. DO NOT EDIT. + +package lookahead + +import ( + "encoding/json" + "fmt" + "reflect" + + core "typefox.dev/fastbelt" +) + +func newToken(tokenType *core.TokenType, view string) *core.Token { + token := core.NewToken(tokenType, view, 0, 0, 0, 0, 0, 0) + return &token +} + +func (i *ObjImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Value string `json:"value"` + Node string `json:"node"` + }{ + T__: "Obj", + Value: i.Value(), + Node: i.Node(), + }) +} + +func (i *RootImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Item Obj `json:"item"` + }{ + T__: "Root", + Item: i.Item(), + }) +} + +func (i *BImpl) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + T__ string `json:"$type"` + Value string `json:"value"` + Node string `json:"node"` + Post string `json:"post"` + }{ + T__: "B", + Value: i.Value(), + Node: i.Node(), + Post: i.Post(), + }) +} + +func (i *ObjImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Value string `json:"value"` + Node string `json:"node"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetValue(newToken(Token_ID, aux.Value)) + var cn core.CompositeNode + cn = core.NewCompositeNode() + cn.SetToken(newToken(Token_ID, aux.Node)) + i.SetNode(cn) + return nil +} + +func (i *RootImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Item json.RawMessage `json:"item"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + item, err := Unmarshal[Obj](aux.Item) + if err != nil { + return err + } + i.SetItem(item) + return nil +} + +func (i *BImpl) UnmarshalJSON(data []byte) error { + aux := &struct { + T__ string `json:"$type"` + Value string `json:"value"` + Node string `json:"node"` + Post string `json:"post"` + }{} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + i.SetValue(newToken(Token_ID, aux.Value)) + var cn core.CompositeNode + cn = core.NewCompositeNode() + cn.SetToken(newToken(Token_ID, aux.Node)) + i.SetNode(cn) + i.SetPost(newToken(Token_ID, aux.Post)) + return nil +} + +// Unmarshal decodes data into an instance of type T by reading the "$type" field, +// selecting a corresponding factory, creating an instance, and unmarshaling its content. +func Unmarshal[T core.AstNode](data []byte) (T, error) { + node := &struct { + Type string `json:"$type"` + }{} + if err := json.Unmarshal(data, node); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal: %w", err) + } + factory, ok := LookaheadSyntheticFactories[node.Type] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) + } + instance := factory() + casted, ok := instance.(T) + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + } + if err := json.Unmarshal(data, casted); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + return casted, nil +} diff --git a/internal/languages/token_groups/json_gen.go b/internal/languages/token_groups/json_gen.go index 34a02a93..2afe33b7 100644 --- a/internal/languages/token_groups/json_gen.go +++ b/internal/languages/token_groups/json_gen.go @@ -4,9 +4,10 @@ package token_groups import ( "encoding/json" + "fmt" + "reflect" core "typefox.dev/fastbelt" - utilJson "typefox.dev/fastbelt/util/json" ) func newToken(tokenType *core.TokenType, view string) *core.Token { @@ -56,7 +57,7 @@ func (i *ModelImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - item, err := utilJson.Unmarshal[Item](aux.Item, token_groupsSyntheticFactories) + item, err := Unmarshal[Item](aux.Item) if err != nil { return err } @@ -91,3 +92,31 @@ func (i *RecoveryImpl) UnmarshalJSON(data []byte) error { i.SetSecond(newToken(Token_ID, aux.Second)) return nil } + +// Unmarshal decodes data into an instance of type T by reading the "$type" field, +// selecting a corresponding factory, creating an instance, and unmarshaling its content. +func Unmarshal[T core.AstNode](data []byte) (T, error) { + node := &struct { + Type string `json:"$type"` + }{} + if err := json.Unmarshal(data, node); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal: %w", err) + } + factory, ok := TokenGroupsSyntheticFactories[node.Type] + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) + } + instance := factory() + casted, ok := instance.(T) + if !ok { + var zero T + return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + } + if err := json.Unmarshal(data, casted); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + return casted, nil +} diff --git a/reference.go b/reference.go index 4754445f..b759b49d 100644 --- a/reference.go +++ b/reference.go @@ -7,6 +7,7 @@ package fastbelt import ( "context" "encoding/json" + "errors" "iter" "reflect" "slices" @@ -294,7 +295,9 @@ func NewReferenceDescriptionsFromMap(descriptions collections.MultiMap[AstNode, } } -// MarshalJSON serializes the reference as a JSON object containing the URI of the resolved target's document. +// MarshalJSON serializes the reference as a JSON object containing the URI of the resolved target ast node, +// the cross ref text, as well as the err msg if the reference is unresolvable. +// Returns an error if the reference has not been attempted to resolve (r.resolved == false). // With this function Reference[T] implements json.Marshaler.MarshalJSON() and the method is called by `json.Marshal()` and `json.MarshalIndent()`. func (r *Reference[T]) MarshalJSON() ([]byte, error) { // A nil reference marshals as JSON null. @@ -302,22 +305,35 @@ func (r *Reference[T]) MarshalJSON() ([]byte, error) { return []byte("null"), nil } // We expect at this point that all references have been attempted to resolve. - // if !r.resolved.Load() { - // return nil, errors.New("reference not resolved") - // } + if !r.resolved.Load() { + if path, err := r.Owner().NodePath(); err == nil { + return nil, errors.New("Reference.MarshalJSON(): reference not resolved in node '" + path + "'") + } + return nil, errors.New("Reference.MarshalJSON(): reference not resolved") + } var uri string var errMsg string if r.err != nil { errMsg = r.err.Msg - } else if ref := r.ref; any(ref) != nil { - if doc := ref.Document(); doc != nil { - uri = doc.URI.WithFragment("todo-fragment").StringUnencoded() + + } else if referenced := r.ref; any(referenced) != nil { + refPath, err := referenced.NodePath() + if err != nil { + return nil, errors.New("Reference.MarshalJSON(): failed to compute path fragment of referenced node") + } + + refDoc := referenced.Document() + if refDoc != nil { + if r.Owner() != nil && r.Owner().Document() == refDoc { + uri = "#" + refPath + } else { + uri = refDoc.URI.WithFragment(refPath).StringUnencoded() + } } + } else { + return nil, errors.New("Reference.MarshalJSON(): unexpected state") } - // else { - // return nil, errors.New("unexpected state: resolved reference has no document") - // } return json.Marshal(struct { RefText string `json:"$refText"` @@ -330,48 +346,95 @@ func (r *Reference[T]) MarshalJSON() ([]byte, error) { }) } +// UnmarshalJSON revives the reference as a JSON object. It inspects properties named 'ref', 'refText', and 'err'. +// 'ref' is expected to contain a URI string denoting the target node within the same or another document, may be absent if the reference was unresolvable before serializing. +// 'refText' denotes the cross reference string. +// 'err' contains the error msg if the reference was unresolvable before serializing +// With this function Reference[T] implements json.Unmarshaler.UnmarshalJSON() and the method is called by `json.Unmarshal()`. func (r *Reference[T]) UnmarshalJSON(data []byte) error { aux := &struct { RefText string `json:"$refText"` - Ref string `json:"$ref,omitempty"` - Err string `json:"$error,omitempty"` + Ref string `json:"$ref"` + Err string `json:"$error"` }{} - if err := json.Unmarshal(data, aux); err != nil { return err } - - r.unit = &RefText{ + r.unit = &JsonRefText{ refText: aux.RefText, + owner: r.owner, } + r.getter = NewJsonReferenceGetter[T](aux.Ref) if aux.Err != "" { r.err = NewReferenceError(aux.Err) } - r.getter = func(ctx context.Context, ref *Reference[T]) (*SymbolDescription, *ReferenceError) { - if ref.err != nil { - return nil, ref.err - } - return &SymbolDescription{ - URI: ParseURI(aux.Ref), - }, nil - } - return nil } -type RefText struct { +type JsonLinkingHelper interface { + GetDocument(uri URI) *Document +} + +var jsonLinkingHelperKey = reflect.TypeFor[JsonLinkingHelper]() + +func JsonLinkingHelperKey() any { + return jsonLinkingHelperKey +} + +type JsonRefText struct { owner AstNode refText string } -func (r *RefText) String() string { +func (r *JsonRefText) String() string { return r.refText } -func (r *RefText) Owner() AstNode { +func (r *JsonRefText) Owner() AstNode { return r.owner } -func (r *RefText) Segment() *TextSegment { +func (r *JsonRefText) Segment() *TextSegment { return nil } + +func NewJsonReferenceGetter[T AstNode](uriString string) ReferenceGetter[T] { + return func(ctx context.Context, ref *Reference[T]) (*SymbolDescription, *ReferenceError) { + if ref.err != nil { + return nil, ref.err + } + if uriString == "" { + return nil, NewReferenceError("Reviving reference in Json document failed, 'ref' is absent of empty") + } + helper, ok := ctx.Value(jsonLinkingHelperKey).(JsonLinkingHelper) + if !ok { + return nil, &ReferenceError{ + Msg: "JsonLinkingHelper unavailable", + } + } + var document *Document + uri := ParseURI(uriString) + if uri.Path() == "" { + document = ref.Owner().Document() + + } else if doc := helper.GetDocument(uri); doc != nil { + document = doc + + } else { + return nil, NewReferenceError("Document not found: '" + uri.StringUnencoded() + "'") + } + + if document.Root == nil { + return nil, NewReferenceError("Document is empty: '" + document.URI.StringUnencoded() + "'") + } + + node, err := document.Root.GetByPath(uri.Fragment()) + if err != nil { + return nil, NewReferenceError(err.Error()) + } + return &SymbolDescription{ + URI: uri, + Node: node, + }, nil + } +} diff --git a/util/json/json.go b/util/json/json.go index 17d308ca..13596bdf 100644 --- a/util/json/json.go +++ b/util/json/json.go @@ -2,43 +2,60 @@ // This program and the accompanying materials are made available under the // terms of the MIT License, which is available in the project root. -package json +package fastbelt import ( + "context" "encoding/json" - "fmt" - "reflect" core "typefox.dev/fastbelt" + "typefox.dev/fastbelt/util/service" + "typefox.dev/fastbelt/workspace" ) -// Unmarshal decodes data into T by reading the "$type" field to select a factory from factories. -func Unmarshal[T any](data []byte, factories map[string]func() core.AstNode) (T, error) { - node := &struct { - Type string `json:"$type"` - }{} - if err := json.Unmarshal(data, node); err != nil { - var zero T - return zero, fmt.Errorf("unmarshal: %w", err) +// UnmarshalAndBuildDocument uses the "encoding/json" entry point to unmarshal rootNode based on the given data string and builds the document. +// Similar to parsing-based document loading the ast build up first including the reference, while the resolution of the references is done during the linking phase of the building process. +// For properly linking references to other documents, a helper object is attached to the context given to builder providing access to other documents. +func UnmarshalAndBuildDocument[T core.AstNode](sc *service.Container, document *core.Document, rootNode T, data []byte, ctx context.Context) error { + documents, err := service.Get[workspace.DocumentManager](sc) + if err != nil { + return err } - - factory, ok := factories[node.Type] - if !ok { - var zero T - return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) + builder, err := service.Get[workspace.Builder](sc) + if err != nil { + return err } - instance := factory() - casted, ok := instance.(T) - if !ok { - var zero T - return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) + if err := json.Unmarshal(data, rootNode); err != nil { + return err } - - if err := json.Unmarshal(data, casted); err != nil { - var zero T - return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + core.AssignContainers(document, rootNode) + document.Root = rootNode + document.State = core.DocStateParsed + documents.Set(document) + + if err := builder.Build( + context.WithValue( + ctx, + core.JsonLinkingHelperKey(), + NewJsonLinkingHelper(documents), + ), + []*core.Document{document}, nil, + ); err != nil { + return err } - return casted, nil + return nil +} + +type defaultJsonLinkingHelper struct { + documentManager workspace.DocumentManager +} + +func NewJsonLinkingHelper(docs workspace.DocumentManager) core.JsonLinkingHelper { + return defaultJsonLinkingHelper{docs} +} + +func (h defaultJsonLinkingHelper) GetDocument(uri core.URI) *core.Document { + return h.documentManager.Get(uri) } From bc88da5a59d29dcf45b40c7641e7bfcd0a01cf02 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Thu, 2 Jul 2026 16:31:12 +0200 Subject: [PATCH 07/16] refinements --- examples/arithmetics/json_gen.go | 13 ++++-- examples/statemachine/json_gen.go | 13 ++++-- internal/generator/json_generator.go | 50 +++++++++++++++++---- internal/grammar/json_gen.go | 21 ++++++--- internal/languages/completion/json_gen.go | 13 ++++-- internal/languages/lookahead/json_gen.go | 13 ++++-- internal/languages/token_groups/json_gen.go | 13 ++++-- 7 files changed, 101 insertions(+), 35 deletions(-) diff --git a/examples/arithmetics/json_gen.go b/examples/arithmetics/json_gen.go index 90d4cf28..b500e5a7 100644 --- a/examples/arithmetics/json_gen.go +++ b/examples/arithmetics/json_gen.go @@ -296,14 +296,19 @@ func Unmarshal[T core.AstNode](data []byte) (T, error) { return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) } instance := factory() - casted, ok := instance.(T) + asT, ok := instance.(T) if !ok { var zero T return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) } - if err := json.Unmarshal(data, casted); err != nil { + if unmarshaler, ok := instance.(json.Unmarshaler); ok { + if err := unmarshaler.UnmarshalJSON(data); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + } else { var zero T - return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + return zero, fmt.Errorf("unmarshal: %T is not convertible to type json.Unmarshaler", instance) } - return casted, nil + return asT, nil } diff --git a/examples/statemachine/json_gen.go b/examples/statemachine/json_gen.go index 705d96f6..bf3eacc5 100644 --- a/examples/statemachine/json_gen.go +++ b/examples/statemachine/json_gen.go @@ -216,14 +216,19 @@ func Unmarshal[T core.AstNode](data []byte) (T, error) { return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) } instance := factory() - casted, ok := instance.(T) + asT, ok := instance.(T) if !ok { var zero T return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) } - if err := json.Unmarshal(data, casted); err != nil { + if unmarshaler, ok := instance.(json.Unmarshaler); ok { + if err := unmarshaler.UnmarshalJSON(data); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + } else { var zero T - return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + return zero, fmt.Errorf("unmarshal: %T is not convertible to type json.Unmarshaler", instance) } - return casted, nil + return asT, nil } diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go index d8d7f1c4..963db5da 100644 --- a/internal/generator/json_generator.go +++ b/internal/generator/json_generator.go @@ -59,6 +59,23 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { node.AppendLine("func (i *", iface.Name(), "Impl) MarshalJSON() ([]byte, error) {") node.Indent(func(n codegen.Node) { + // preprocess []string fields: they are stored as []*core.Token internally but shall be serialized as plain strings; + // instead of implementing 'MarshalJSON()' for *core.Token, a preprocessing loop calling '.String()' for each item + // is added for each of such fields + var stringListFields = map[string]string{} + for _, field := range fields { + if field.Array && (field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE) { + varName := strings.ToLower(field.Name[:1]) + field.Name[1:] + stringListFields[field.Name] = varName + + n.AppendLine(varName, " := make([]string, len(i.", field.Name, "()))") + n.AppendLine("for j, item := range i.", field.Name, "() {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine(varName, "[j] = item.String()") + }) + n.AppendLine("}") + } + } n.AppendLine("return json.Marshal(struct {") n.Indent(func(n2 codegen.Node) { n2.AppendLine("T__", " ", "string", " `json:\"$type\"`") @@ -66,7 +83,7 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { jsonTag := strings.ToLower(field.Name[:1]) + field.Name[1:] var typeStr string if field.Array { - typeStr = "[]" + field.GType + typeStr = "[]" + field.Type } else { typeStr = field.Type } @@ -77,11 +94,15 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { n.Indent(func(n2 codegen.Node) { n2.AppendLine("T__: ", "\"", iface.Name(), "\",") for _, field := range fields { - getterName := field.Name - if field.Boolean && !field.Array { - getterName = "Is" + field.Name + if varName, present := stringListFields[field.Name]; present { + n2.AppendLine(field.Name, ": ", varName, ",") + } else { + getterName := field.Name + if field.Boolean && !field.Array { + getterName = "Is" + field.Name + } + n2.AppendLine(field.Name, ": i.", getterName, "(),") } - n2.AppendLine(field.Name, ": i.", getterName, "(),") } }) n.AppendLine("})") @@ -238,20 +259,31 @@ func generateDispatchingUnmarshalFunc(node codegen.Node, g grammar.Grammar) { }) n.AppendLine("}") n.AppendLine("instance := factory()") - n.AppendLine("casted, ok := instance.(T)") + n.AppendLine("asT, ok := instance.(T);") n.AppendLine("if !ok {") n.Indent(func(n2 codegen.Node) { n2.AppendLine("var zero T") n2.AppendLine(`return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]())`) }) n.AppendLine("}") - n.AppendLine("if err := json.Unmarshal(data, casted); err != nil {") + // below we assume that all ast node types implement json.Unmarshaler, + // so we can directly call UnmarshalJSON(data) instead of taking the generic route via json.Unmarshal(data, node) + n.AppendLine("if unmarshaler, ok := instance.(json.Unmarshaler); ok {") + n.Indent(func(n2 codegen.Node) { + n2.AppendLine("if err := unmarshaler.UnmarshalJSON(data); err != nil {") + n2.Indent(func(n3 codegen.Node) { + n3.AppendLine("var zero T") + n3.AppendLine(`return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err)`) + }) + n2.AppendLine("}") + }) + n.AppendLine("} else {") n.Indent(func(n2 codegen.Node) { n2.AppendLine("var zero T") - n2.AppendLine(`return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err)`) + n2.AppendLine(`return zero, fmt.Errorf("unmarshal: %T is not convertible to type json.Unmarshaler", instance)`) }) n.AppendLine("}") - n.AppendLine("return casted, nil") + n.AppendLine("return asT, nil") }) node.AppendLine("}") node.AppendLine() diff --git a/internal/grammar/json_gen.go b/internal/grammar/json_gen.go index 79bd1601..f8542e6e 100644 --- a/internal/grammar/json_gen.go +++ b/internal/grammar/json_gen.go @@ -172,17 +172,21 @@ func (i *TokenImpl) MarshalJSON() ([]byte, error) { } func (i *TokenGroupImpl) MarshalJSON() ([]byte, error) { + regexps := make([]string, len(i.Regexps())) + for j, item := range i.Regexps() { + regexps[j] = item.String() + } return json.Marshal(struct { T__ string `json:"$type"` Name string `json:"name"` TokenRefs []*core.Reference[AbstractTokenRule] `json:"tokenRefs"` - Regexps []*core.Token `json:"regexps"` + Regexps []string `json:"regexps"` Keywords []Keyword `json:"keywords"` }{ T__: "TokenGroup", Name: i.Name(), TokenRefs: i.TokenRefs(), - Regexps: i.Regexps(), + Regexps: regexps, Keywords: i.Keywords(), }) } @@ -811,14 +815,19 @@ func Unmarshal[T core.AstNode](data []byte) (T, error) { return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) } instance := factory() - casted, ok := instance.(T) + asT, ok := instance.(T) if !ok { var zero T return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) } - if err := json.Unmarshal(data, casted); err != nil { + if unmarshaler, ok := instance.(json.Unmarshaler); ok { + if err := unmarshaler.UnmarshalJSON(data); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + } else { var zero T - return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + return zero, fmt.Errorf("unmarshal: %T is not convertible to type json.Unmarshaler", instance) } - return casted, nil + return asT, nil } diff --git a/internal/languages/completion/json_gen.go b/internal/languages/completion/json_gen.go index 48d42d5d..1f1cae05 100644 --- a/internal/languages/completion/json_gen.go +++ b/internal/languages/completion/json_gen.go @@ -387,14 +387,19 @@ func Unmarshal[T core.AstNode](data []byte) (T, error) { return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) } instance := factory() - casted, ok := instance.(T) + asT, ok := instance.(T) if !ok { var zero T return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) } - if err := json.Unmarshal(data, casted); err != nil { + if unmarshaler, ok := instance.(json.Unmarshaler); ok { + if err := unmarshaler.UnmarshalJSON(data); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + } else { var zero T - return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + return zero, fmt.Errorf("unmarshal: %T is not convertible to type json.Unmarshaler", instance) } - return casted, nil + return asT, nil } diff --git a/internal/languages/lookahead/json_gen.go b/internal/languages/lookahead/json_gen.go index 16c2f49f..9284b147 100644 --- a/internal/languages/lookahead/json_gen.go +++ b/internal/languages/lookahead/json_gen.go @@ -119,14 +119,19 @@ func Unmarshal[T core.AstNode](data []byte) (T, error) { return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) } instance := factory() - casted, ok := instance.(T) + asT, ok := instance.(T) if !ok { var zero T return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) } - if err := json.Unmarshal(data, casted); err != nil { + if unmarshaler, ok := instance.(json.Unmarshaler); ok { + if err := unmarshaler.UnmarshalJSON(data); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + } else { var zero T - return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + return zero, fmt.Errorf("unmarshal: %T is not convertible to type json.Unmarshaler", instance) } - return casted, nil + return asT, nil } diff --git a/internal/languages/token_groups/json_gen.go b/internal/languages/token_groups/json_gen.go index 2afe33b7..047661c5 100644 --- a/internal/languages/token_groups/json_gen.go +++ b/internal/languages/token_groups/json_gen.go @@ -109,14 +109,19 @@ func Unmarshal[T core.AstNode](data []byte) (T, error) { return zero, fmt.Errorf("unmarshal: unknown type %q", node.Type) } instance := factory() - casted, ok := instance.(T) + asT, ok := instance.(T) if !ok { var zero T return zero, fmt.Errorf("unmarshal: %T is not convertible to type %s", instance, reflect.TypeFor[T]()) } - if err := json.Unmarshal(data, casted); err != nil { + if unmarshaler, ok := instance.(json.Unmarshaler); ok { + if err := unmarshaler.UnmarshalJSON(data); err != nil { + var zero T + return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + } + } else { var zero T - return zero, fmt.Errorf("unmarshal %s: %w", node.Type, err) + return zero, fmt.Errorf("unmarshal: %T is not convertible to type json.Unmarshaler", instance) } - return casted, nil + return asT, nil } From 470ae50fd39dee825e1a67b48b577a04449781d5 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Thu, 2 Jul 2026 18:04:38 +0200 Subject: [PATCH 08/16] more refinements --- examples/arithmetics/json_gen.go | 78 +++--- examples/statemachine/json_gen.go | 56 +++-- internal/generator/json_generator.go | 39 +-- internal/grammar/json_gen.go | 260 +++++++++++--------- internal/languages/completion/json_gen.go | 140 ++++++----- internal/languages/lookahead/json_gen.go | 22 +- internal/languages/token_groups/json_gen.go | 20 +- reference.go | 35 +-- 8 files changed, 366 insertions(+), 284 deletions(-) diff --git a/examples/arithmetics/json_gen.go b/examples/arithmetics/json_gen.go index b500e5a7..c3140b19 100644 --- a/examples/arithmetics/json_gen.go +++ b/examples/arithmetics/json_gen.go @@ -18,8 +18,8 @@ func newToken(tokenType *core.TokenType, view string) *core.Token { func (i *ModuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Statements []Statement `json:"statements"` + Name string `json:"name,omitempty"` + Statements []Statement `json:"statements,omitempty"` }{ T__: "Module", Name: i.Name(), @@ -38,7 +38,7 @@ func (i *StatementImpl) MarshalJSON() ([]byte, error) { func (i *AbstractDefinitionImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` + Name string `json:"name,omitempty"` }{ T__: "AbstractDefinition", Name: i.Name(), @@ -48,9 +48,9 @@ func (i *AbstractDefinitionImpl) MarshalJSON() ([]byte, error) { func (i *DefinitionImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Args []DeclaredParameter `json:"args"` - Expression Expression `json:"expression"` + Name string `json:"name,omitempty"` + Args []DeclaredParameter `json:"args,omitempty"` + Expression Expression `json:"expression,omitempty"` }{ T__: "Definition", Name: i.Name(), @@ -62,7 +62,7 @@ func (i *DefinitionImpl) MarshalJSON() ([]byte, error) { func (i *DeclaredParameterImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` + Name string `json:"name,omitempty"` }{ T__: "DeclaredParameter", Name: i.Name(), @@ -72,7 +72,7 @@ func (i *DeclaredParameterImpl) MarshalJSON() ([]byte, error) { func (i *EvaluationImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Expression Expression `json:"expression"` + Expression Expression `json:"expression,omitempty"` }{ T__: "Evaluation", Expression: i.Expression(), @@ -90,9 +90,9 @@ func (i *ExpressionImpl) MarshalJSON() ([]byte, error) { func (i *BinaryExpressionImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Left Expression `json:"left"` - Operator string `json:"operator"` - Right Expression `json:"right"` + Left Expression `json:"left,omitempty"` + Operator string `json:"operator,omitempty"` + Right Expression `json:"right,omitempty"` }{ T__: "BinaryExpression", Left: i.Left(), @@ -104,8 +104,8 @@ func (i *BinaryExpressionImpl) MarshalJSON() ([]byte, error) { func (i *FunctionCallImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Args []Expression `json:"args"` - Callable *core.Reference[AbstractDefinition] `json:"callable"` + Args []Expression `json:"args,omitempty"` + Callable *core.Reference[AbstractDefinition] `json:"callable,omitempty"` }{ T__: "FunctionCall", Args: i.Args(), @@ -116,7 +116,7 @@ func (i *FunctionCallImpl) MarshalJSON() ([]byte, error) { func (i *NumberLiteralImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Value string `json:"value"` + Value string `json:"value,omitempty"` }{ T__: "NumberLiteral", Value: i.Value(), @@ -179,11 +179,13 @@ func (i *DefinitionImpl) UnmarshalJSON(data []byte) error { } i.SetArgsItem(node) } - expression, err := Unmarshal[Expression](aux.Expression) - if err != nil { - return err + if aux.Expression != nil { + expression, err := Unmarshal[Expression](aux.Expression) + if err != nil { + return err + } + i.SetExpression(expression) } - i.SetExpression(expression) return nil } @@ -207,11 +209,13 @@ func (i *EvaluationImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - expression, err := Unmarshal[Expression](aux.Expression) - if err != nil { - return err + if aux.Expression != nil { + expression, err := Unmarshal[Expression](aux.Expression) + if err != nil { + return err + } + i.SetExpression(expression) } - i.SetExpression(expression) return nil } @@ -229,17 +233,21 @@ func (i *BinaryExpressionImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - left, err := Unmarshal[Expression](aux.Left) - if err != nil { - return err + if aux.Left != nil { + left, err := Unmarshal[Expression](aux.Left) + if err != nil { + return err + } + i.SetLeft(left) } - i.SetLeft(left) i.SetOperator(newToken(Token_ID, aux.Operator)) - right, err := Unmarshal[Expression](aux.Right) - if err != nil { - return err + if aux.Right != nil { + right, err := Unmarshal[Expression](aux.Right) + if err != nil { + return err + } + i.SetRight(right) } - i.SetRight(right) return nil } @@ -260,11 +268,13 @@ func (i *FunctionCallImpl) UnmarshalJSON(data []byte) error { } i.SetArgsItem(node) } - callable := core.NewReference[AbstractDefinition](i, nil, nil) - if err := json.Unmarshal(aux.Callable, &callable); err != nil { - return err + if aux.Callable != nil { + callable := core.NewReference[AbstractDefinition](i, nil, nil) + if err := callable.UnmarshalJSON(aux.Callable); err != nil { + return err + } + i.SetCallable(callable) } - i.SetCallable(callable) return nil } diff --git a/examples/statemachine/json_gen.go b/examples/statemachine/json_gen.go index bf3eacc5..6969b3b2 100644 --- a/examples/statemachine/json_gen.go +++ b/examples/statemachine/json_gen.go @@ -18,11 +18,11 @@ func newToken(tokenType *core.TokenType, view string) *core.Token { func (i *StatemachineImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Events []Event `json:"events"` - Commands []Command `json:"commands"` - Init *core.Reference[State] `json:"init"` - States []State `json:"states"` + Name string `json:"name,omitempty"` + Events []Event `json:"events,omitempty"` + Commands []Command `json:"commands,omitempty"` + Init *core.Reference[State] `json:"init,omitempty"` + States []State `json:"states,omitempty"` }{ T__: "Statemachine", Name: i.Name(), @@ -36,7 +36,7 @@ func (i *StatemachineImpl) MarshalJSON() ([]byte, error) { func (i *EventImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` + Name string `json:"name,omitempty"` }{ T__: "Event", Name: i.Name(), @@ -46,7 +46,7 @@ func (i *EventImpl) MarshalJSON() ([]byte, error) { func (i *CommandImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` + Name string `json:"name,omitempty"` }{ T__: "Command", Name: i.Name(), @@ -56,9 +56,9 @@ func (i *CommandImpl) MarshalJSON() ([]byte, error) { func (i *StateImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Actions []*core.Reference[Command] `json:"actions"` - Transitions []Transition `json:"transitions"` + Name string `json:"name,omitempty"` + Actions []*core.Reference[Command] `json:"actions,omitempty"` + Transitions []Transition `json:"transitions,omitempty"` }{ T__: "State", Name: i.Name(), @@ -70,8 +70,8 @@ func (i *StateImpl) MarshalJSON() ([]byte, error) { func (i *TransitionImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Event *core.Reference[Event] `json:"event"` - State *core.Reference[State] `json:"state"` + Event *core.Reference[Event] `json:"event,omitempty"` + State *core.Reference[State] `json:"state,omitempty"` }{ T__: "Transition", Event: i.Event(), @@ -108,11 +108,13 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { } i.SetCommandsItem(node) } - init := core.NewReference[State](i, nil, nil) - if err := json.Unmarshal(aux.Init, &init); err != nil { - return err + if aux.Init != nil { + init := core.NewReference[State](i, nil, nil) + if err := init.UnmarshalJSON(aux.Init); err != nil { + return err + } + i.SetInit(init) } - i.SetInit(init) i.states = []State{} for _, item := range aux.States { node, err := Unmarshal[State](item) @@ -162,7 +164,7 @@ func (i *StateImpl) UnmarshalJSON(data []byte) error { i.actions = []*core.Reference[Command]{} for _, item := range aux.Actions { node := core.NewReference[Command](i, nil, nil) - if err := json.Unmarshal(item, &node); err != nil { + if err := node.UnmarshalJSON(item); err != nil { return err } i.SetActionsItem(node) @@ -187,16 +189,20 @@ func (i *TransitionImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - event := core.NewReference[Event](i, nil, nil) - if err := json.Unmarshal(aux.Event, &event); err != nil { - return err + if aux.Event != nil { + event := core.NewReference[Event](i, nil, nil) + if err := event.UnmarshalJSON(aux.Event); err != nil { + return err + } + i.SetEvent(event) } - i.SetEvent(event) - state := core.NewReference[State](i, nil, nil) - if err := json.Unmarshal(aux.State, &state); err != nil { - return err + if aux.State != nil { + state := core.NewReference[State](i, nil, nil) + if err := state.UnmarshalJSON(aux.State); err != nil { + return err + } + i.SetState(state) } - i.SetState(state) return nil } diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go index 963db5da..bc0764c4 100644 --- a/internal/generator/json_generator.go +++ b/internal/generator/json_generator.go @@ -1,4 +1,4 @@ -// Copyright 2025 TypeFox GmbH +// Copyright 2026 TypeFox GmbH // This program and the accompanying materials are made available under the // terms of the MIT License, which is available in the project root. @@ -87,7 +87,7 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { } else { typeStr = field.Type } - n2.AppendLine(field.Name, " ", typeStr, " `json:\"", jsonTag, "\"`") + n2.AppendLine(field.Name, " ", typeStr, " `json:\"", jsonTag, ",omitempty\"`") } }) n.AppendLine("}{") @@ -205,26 +205,35 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { } func genUnmarshalReference(node codegen.Node, field FieldInfo, srcName string, targetName string, loopItem bool) { - node.AppendLine(targetName, " := core.NewReference[", strings.Split(field.Type, "[")[1], "(i, nil, nil)") - node.AppendLine("if err := json.Unmarshal(", srcName, ", &", targetName, "); err != nil {") - genReturnErr(node) - - if loopItem { - node.AppendLine("i.Set", field.Name, "Item(", targetName, ")") - } else { - node.AppendLine("i.Set", field.Name, "(", targetName, ")") - } + genUnmarshalFieldContent(node, field, srcName, targetName, loopItem, func(body codegen.Node) { + body.AppendLine(targetName, " := core.NewReference[", strings.Split(field.Type, "[")[1], "(i, nil, nil)") + // for the sake simplicity and performance we call 'target.UnmarshalJSON()' directly instead of taking the route + // via json.Unmarshal(...), since Reference implements that method + // note: the generic impl has special handling for "RawMessage" being equal "null", sets the target pointer to "nil" + body.AppendLine("if err := ", targetName, ".UnmarshalJSON(", srcName, "); err != nil {") + genReturnErr(body) + }) } func genUnmarshalChild(node codegen.Node, field FieldInfo, srcName string, targetName string, loopItem bool) { - node.AppendLine(targetName, ", err := Unmarshal[", field.Type, "](", srcName, ")") - node.AppendLine("if err != nil {") - genReturnErr(node) + genUnmarshalFieldContent(node, field, srcName, targetName, loopItem, func(body codegen.Node) { + body.AppendLine(targetName, ", err := Unmarshal[", field.Type, "](", srcName, ")") + body.AppendLine("if err != nil {") + genReturnErr(body) + }) +} +func genUnmarshalFieldContent(node codegen.Node, field FieldInfo, srcName string, targetName string, loopItem bool, unmarshalBody func(codegen.Node)) { if loopItem { + unmarshalBody(node) node.AppendLine("i.Set", field.Name, "Item(", targetName, ")") } else { - node.AppendLine("i.Set", field.Name, "(", targetName, ")") + node.AppendLine("if ", srcName, " != nil {") + node.Indent(func(n2 codegen.Node) { + unmarshalBody(node) + node.AppendLine("i.Set", field.Name, "(", targetName, ")") + }) + node.AppendLine("}") } } diff --git a/internal/grammar/json_gen.go b/internal/grammar/json_gen.go index f8542e6e..7496ff7e 100644 --- a/internal/grammar/json_gen.go +++ b/internal/grammar/json_gen.go @@ -18,12 +18,12 @@ func newToken(tokenType *core.TokenType, view string) *core.Token { func (i *GrammarImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Rules []ParserRule `json:"rules"` - Composites []CompositeRule `json:"composites"` - Terminals []Token `json:"terminals"` - TokenGroups []TokenGroup `json:"tokenGroups"` - Interfaces []Interface `json:"interfaces"` + Name string `json:"name,omitempty"` + Rules []ParserRule `json:"rules,omitempty"` + Composites []CompositeRule `json:"composites,omitempty"` + Terminals []Token `json:"terminals,omitempty"` + TokenGroups []TokenGroup `json:"tokenGroups,omitempty"` + Interfaces []Interface `json:"interfaces,omitempty"` }{ T__: "Grammar", Name: i.Name(), @@ -38,9 +38,9 @@ func (i *GrammarImpl) MarshalJSON() ([]byte, error) { func (i *InterfaceImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Extends []*core.Reference[Interface] `json:"extends"` - Fields []Field `json:"fields"` + Name string `json:"name,omitempty"` + Extends []*core.Reference[Interface] `json:"extends,omitempty"` + Fields []Field `json:"fields,omitempty"` }{ T__: "Interface", Name: i.Name(), @@ -52,8 +52,8 @@ func (i *InterfaceImpl) MarshalJSON() ([]byte, error) { func (i *FieldImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Type FieldType `json:"type"` + Name string `json:"name,omitempty"` + Type FieldType `json:"type,omitempty"` }{ T__: "Field", Name: i.Name(), @@ -72,7 +72,7 @@ func (i *FieldTypeImpl) MarshalJSON() ([]byte, error) { func (i *ArrayTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - InternalType FieldType `json:"internalType"` + InternalType FieldType `json:"internalType,omitempty"` }{ T__: "ArrayType", InternalType: i.InternalType(), @@ -82,7 +82,7 @@ func (i *ArrayTypeImpl) MarshalJSON() ([]byte, error) { func (i *ReferenceTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` + Type *core.Reference[Interface] `json:"type,omitempty"` }{ T__: "ReferenceType", Type: i.Type(), @@ -92,7 +92,7 @@ func (i *ReferenceTypeImpl) MarshalJSON() ([]byte, error) { func (i *SimpleTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Type *core.Reference[Interface] `json:"type"` + Type *core.Reference[Interface] `json:"type,omitempty"` }{ T__: "SimpleType", Type: i.Type(), @@ -102,7 +102,7 @@ func (i *SimpleTypeImpl) MarshalJSON() ([]byte, error) { func (i *PrimitiveTypeImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Type string `json:"type"` + Type string `json:"type,omitempty"` }{ T__: "PrimitiveType", Type: i.Type(), @@ -112,7 +112,7 @@ func (i *PrimitiveTypeImpl) MarshalJSON() ([]byte, error) { func (i *AbstractRuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` + Name string `json:"name,omitempty"` }{ T__: "AbstractRule", Name: i.Name(), @@ -122,8 +122,8 @@ func (i *AbstractRuleImpl) MarshalJSON() ([]byte, error) { func (i *AbstractRuleWithBodyImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Body Element `json:"body"` + Name string `json:"name,omitempty"` + Body Element `json:"body,omitempty"` }{ T__: "AbstractRuleWithBody", Name: i.Name(), @@ -134,7 +134,7 @@ func (i *AbstractRuleWithBodyImpl) MarshalJSON() ([]byte, error) { func (i *AbstractTokenRuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` + Name string `json:"name,omitempty"` }{ T__: "AbstractTokenRule", Name: i.Name(), @@ -144,10 +144,10 @@ func (i *AbstractTokenRuleImpl) MarshalJSON() ([]byte, error) { func (i *ParserRuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Body Element `json:"body"` - Entry bool `json:"entry"` - ReturnType *core.Reference[Interface] `json:"returnType"` + Name string `json:"name,omitempty"` + Body Element `json:"body,omitempty"` + Entry bool `json:"entry,omitempty"` + ReturnType *core.Reference[Interface] `json:"returnType,omitempty"` }{ T__: "ParserRule", Name: i.Name(), @@ -160,9 +160,9 @@ func (i *ParserRuleImpl) MarshalJSON() ([]byte, error) { func (i *TokenImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Type string `json:"type"` - Regexp string `json:"regexp"` + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Regexp string `json:"regexp,omitempty"` }{ T__: "Token", Name: i.Name(), @@ -178,10 +178,10 @@ func (i *TokenGroupImpl) MarshalJSON() ([]byte, error) { } return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - TokenRefs []*core.Reference[AbstractTokenRule] `json:"tokenRefs"` - Regexps []string `json:"regexps"` - Keywords []Keyword `json:"keywords"` + Name string `json:"name,omitempty"` + TokenRefs []*core.Reference[AbstractTokenRule] `json:"tokenRefs,omitempty"` + Regexps []string `json:"regexps,omitempty"` + Keywords []Keyword `json:"keywords,omitempty"` }{ T__: "TokenGroup", Name: i.Name(), @@ -194,7 +194,7 @@ func (i *TokenGroupImpl) MarshalJSON() ([]byte, error) { func (i *ElementImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` + Cardinality string `json:"cardinality,omitempty"` }{ T__: "Element", Cardinality: i.Cardinality(), @@ -204,8 +204,8 @@ func (i *ElementImpl) MarshalJSON() ([]byte, error) { func (i *AlternativesImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Alts []Element `json:"alts"` + Cardinality string `json:"cardinality,omitempty"` + Alts []Element `json:"alts,omitempty"` }{ T__: "Alternatives", Cardinality: i.Cardinality(), @@ -216,8 +216,8 @@ func (i *AlternativesImpl) MarshalJSON() ([]byte, error) { func (i *GroupImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Elements []Element `json:"elements"` + Cardinality string `json:"cardinality,omitempty"` + Elements []Element `json:"elements,omitempty"` }{ T__: "Group", Cardinality: i.Cardinality(), @@ -228,8 +228,8 @@ func (i *GroupImpl) MarshalJSON() ([]byte, error) { func (i *KeywordImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Value string `json:"value"` + Cardinality string `json:"cardinality,omitempty"` + Value string `json:"value,omitempty"` }{ T__: "Keyword", Cardinality: i.Cardinality(), @@ -240,10 +240,10 @@ func (i *KeywordImpl) MarshalJSON() ([]byte, error) { func (i *AssignmentImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Property *core.Reference[Field] `json:"property"` - Operator string `json:"operator"` - Value Assignable `json:"value"` + Cardinality string `json:"cardinality,omitempty"` + Property *core.Reference[Field] `json:"property,omitempty"` + Operator string `json:"operator,omitempty"` + Value Assignable `json:"value,omitempty"` }{ T__: "Assignment", Cardinality: i.Cardinality(), @@ -256,7 +256,7 @@ func (i *AssignmentImpl) MarshalJSON() ([]byte, error) { func (i *AssignableImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` + Cardinality string `json:"cardinality,omitempty"` }{ T__: "Assignable", Cardinality: i.Cardinality(), @@ -266,9 +266,9 @@ func (i *AssignableImpl) MarshalJSON() ([]byte, error) { func (i *CrossRefImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Type *core.Reference[Interface] `json:"type"` - Rule RuleCall `json:"rule"` + Cardinality string `json:"cardinality,omitempty"` + Type *core.Reference[Interface] `json:"type,omitempty"` + Rule RuleCall `json:"rule,omitempty"` }{ T__: "CrossRef", Cardinality: i.Cardinality(), @@ -280,8 +280,8 @@ func (i *CrossRefImpl) MarshalJSON() ([]byte, error) { func (i *RuleCallImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Rule *core.Reference[AbstractRule] `json:"rule"` + Cardinality string `json:"cardinality,omitempty"` + Rule *core.Reference[AbstractRule] `json:"rule,omitempty"` }{ T__: "RuleCall", Cardinality: i.Cardinality(), @@ -292,10 +292,10 @@ func (i *RuleCallImpl) MarshalJSON() ([]byte, error) { func (i *ActionImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Cardinality string `json:"cardinality"` - Type *core.Reference[Interface] `json:"type"` - Operator string `json:"operator"` - Property *core.Reference[Field] `json:"property"` + Cardinality string `json:"cardinality,omitempty"` + Type *core.Reference[Interface] `json:"type,omitempty"` + Operator string `json:"operator,omitempty"` + Property *core.Reference[Field] `json:"property,omitempty"` }{ T__: "Action", Cardinality: i.Cardinality(), @@ -308,8 +308,8 @@ func (i *ActionImpl) MarshalJSON() ([]byte, error) { func (i *CompositeRuleImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Body Element `json:"body"` + Name string `json:"name,omitempty"` + Body Element `json:"body,omitempty"` }{ T__: "CompositeRule", Name: i.Name(), @@ -388,7 +388,7 @@ func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { i.extends = []*core.Reference[Interface]{} for _, item := range aux.Extends { node := core.NewReference[Interface](i, nil, nil) - if err := json.Unmarshal(item, &node); err != nil { + if err := node.UnmarshalJSON(item); err != nil { return err } i.SetExtendsItem(node) @@ -414,11 +414,13 @@ func (i *FieldImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - _Type, err := Unmarshal[FieldType](aux.Type) - if err != nil { - return err + if aux.Type != nil { + _Type, err := Unmarshal[FieldType](aux.Type) + if err != nil { + return err + } + i.SetType(_Type) } - i.SetType(_Type) return nil } @@ -434,11 +436,13 @@ func (i *ArrayTypeImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - internalType, err := Unmarshal[FieldType](aux.InternalType) - if err != nil { - return err + if aux.InternalType != nil { + internalType, err := Unmarshal[FieldType](aux.InternalType) + if err != nil { + return err + } + i.SetInternalType(internalType) } - i.SetInternalType(internalType) return nil } @@ -450,11 +454,13 @@ func (i *ReferenceTypeImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - _Type := core.NewReference[Interface](i, nil, nil) - if err := json.Unmarshal(aux.Type, &_Type); err != nil { - return err + if aux.Type != nil { + _Type := core.NewReference[Interface](i, nil, nil) + if err := _Type.UnmarshalJSON(aux.Type); err != nil { + return err + } + i.SetType(_Type) } - i.SetType(_Type) return nil } @@ -466,11 +472,13 @@ func (i *SimpleTypeImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - _Type := core.NewReference[Interface](i, nil, nil) - if err := json.Unmarshal(aux.Type, &_Type); err != nil { - return err + if aux.Type != nil { + _Type := core.NewReference[Interface](i, nil, nil) + if err := _Type.UnmarshalJSON(aux.Type); err != nil { + return err + } + i.SetType(_Type) } - i.SetType(_Type) return nil } @@ -508,11 +516,13 @@ func (i *AbstractRuleWithBodyImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - body, err := Unmarshal[Element](aux.Body) - if err != nil { - return err + if aux.Body != nil { + body, err := Unmarshal[Element](aux.Body) + if err != nil { + return err + } + i.SetBody(body) } - i.SetBody(body) return nil } @@ -540,19 +550,23 @@ func (i *ParserRuleImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - body, err := Unmarshal[Element](aux.Body) - if err != nil { - return err + if aux.Body != nil { + body, err := Unmarshal[Element](aux.Body) + if err != nil { + return err + } + i.SetBody(body) } - i.SetBody(body) if aux.Entry { i.SetEntry(newToken(Token_ID, "")) } - returnType := core.NewReference[Interface](i, nil, nil) - if err := json.Unmarshal(aux.ReturnType, &returnType); err != nil { - return err + if aux.ReturnType != nil { + returnType := core.NewReference[Interface](i, nil, nil) + if err := returnType.UnmarshalJSON(aux.ReturnType); err != nil { + return err + } + i.SetReturnType(returnType) } - i.SetReturnType(returnType) return nil } @@ -587,7 +601,7 @@ func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { i.tokenRefs = []*core.Reference[AbstractTokenRule]{} for _, item := range aux.TokenRefs { node := core.NewReference[AbstractTokenRule](i, nil, nil) - if err := json.Unmarshal(item, &node); err != nil { + if err := node.UnmarshalJSON(item); err != nil { return err } i.SetTokenRefsItem(node) @@ -687,17 +701,21 @@ func (i *AssignmentImpl) UnmarshalJSON(data []byte) error { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - property := core.NewReference[Field](i, nil, nil) - if err := json.Unmarshal(aux.Property, &property); err != nil { - return err + if aux.Property != nil { + property := core.NewReference[Field](i, nil, nil) + if err := property.UnmarshalJSON(aux.Property); err != nil { + return err + } + i.SetProperty(property) } - i.SetProperty(property) i.SetOperator(newToken(Token_ID, aux.Operator)) - value, err := Unmarshal[Assignable](aux.Value) - if err != nil { - return err + if aux.Value != nil { + value, err := Unmarshal[Assignable](aux.Value) + if err != nil { + return err + } + i.SetValue(value) } - i.SetValue(value) return nil } @@ -724,16 +742,20 @@ func (i *CrossRefImpl) UnmarshalJSON(data []byte) error { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - _Type := core.NewReference[Interface](i, nil, nil) - if err := json.Unmarshal(aux.Type, &_Type); err != nil { - return err + if aux.Type != nil { + _Type := core.NewReference[Interface](i, nil, nil) + if err := _Type.UnmarshalJSON(aux.Type); err != nil { + return err + } + i.SetType(_Type) } - i.SetType(_Type) - rule, err := Unmarshal[RuleCall](aux.Rule) - if err != nil { - return err + if aux.Rule != nil { + rule, err := Unmarshal[RuleCall](aux.Rule) + if err != nil { + return err + } + i.SetRule(rule) } - i.SetRule(rule) return nil } @@ -747,11 +769,13 @@ func (i *RuleCallImpl) UnmarshalJSON(data []byte) error { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - rule := core.NewReference[AbstractRule](i, nil, nil) - if err := json.Unmarshal(aux.Rule, &rule); err != nil { - return err + if aux.Rule != nil { + rule := core.NewReference[AbstractRule](i, nil, nil) + if err := rule.UnmarshalJSON(aux.Rule); err != nil { + return err + } + i.SetRule(rule) } - i.SetRule(rule) return nil } @@ -767,17 +791,21 @@ func (i *ActionImpl) UnmarshalJSON(data []byte) error { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - _Type := core.NewReference[Interface](i, nil, nil) - if err := json.Unmarshal(aux.Type, &_Type); err != nil { - return err + if aux.Type != nil { + _Type := core.NewReference[Interface](i, nil, nil) + if err := _Type.UnmarshalJSON(aux.Type); err != nil { + return err + } + i.SetType(_Type) } - i.SetType(_Type) i.SetOperator(newToken(Token_ID, aux.Operator)) - property := core.NewReference[Field](i, nil, nil) - if err := json.Unmarshal(aux.Property, &property); err != nil { - return err + if aux.Property != nil { + property := core.NewReference[Field](i, nil, nil) + if err := property.UnmarshalJSON(aux.Property); err != nil { + return err + } + i.SetProperty(property) } - i.SetProperty(property) return nil } @@ -791,11 +819,13 @@ func (i *CompositeRuleImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - body, err := Unmarshal[Element](aux.Body) - if err != nil { - return err + if aux.Body != nil { + body, err := Unmarshal[Element](aux.Body) + if err != nil { + return err + } + i.SetBody(body) } - i.SetBody(body) return nil } diff --git a/internal/languages/completion/json_gen.go b/internal/languages/completion/json_gen.go index 1f1cae05..c5979490 100644 --- a/internal/languages/completion/json_gen.go +++ b/internal/languages/completion/json_gen.go @@ -26,7 +26,7 @@ func (i *ObjImpl) MarshalJSON() ([]byte, error) { func (i *RootImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Objects []Obj `json:"objects"` + Objects []Obj `json:"objects,omitempty"` }{ T__: "Root", Objects: i.Objects(), @@ -36,8 +36,8 @@ func (i *RootImpl) MarshalJSON() ([]byte, error) { func (i *DeclareImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Name string `json:"name"` - Children []Declare `json:"children"` + Name string `json:"name,omitempty"` + Children []Declare `json:"children,omitempty"` }{ T__: "Declare", Name: i.Name(), @@ -48,7 +48,7 @@ func (i *DeclareImpl) MarshalJSON() ([]byte, error) { func (i *EImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + Ref *core.Reference[Declare] `json:"ref,omitempty"` }{ T__: "E", Ref: i.Ref(), @@ -58,7 +58,7 @@ func (i *EImpl) MarshalJSON() ([]byte, error) { func (i *FImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Items []FItem `json:"items"` + Items []FItem `json:"items,omitempty"` }{ T__: "F", Items: i.Items(), @@ -68,7 +68,7 @@ func (i *FImpl) MarshalJSON() ([]byte, error) { func (i *FItemImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + Ref *core.Reference[Declare] `json:"ref,omitempty"` }{ T__: "FItem", Ref: i.Ref(), @@ -78,7 +78,7 @@ func (i *FItemImpl) MarshalJSON() ([]byte, error) { func (i *GImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + Ref *core.Reference[Declare] `json:"ref,omitempty"` }{ T__: "G", Ref: i.Ref(), @@ -88,7 +88,7 @@ func (i *GImpl) MarshalJSON() ([]byte, error) { func (i *HImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Member MemberCall `json:"member"` + Member MemberCall `json:"member,omitempty"` }{ T__: "H", Member: i.Member(), @@ -98,8 +98,8 @@ func (i *HImpl) MarshalJSON() ([]byte, error) { func (i *MemberCallImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` - Previous MemberCall `json:"previous"` + Ref *core.Reference[Declare] `json:"ref,omitempty"` + Previous MemberCall `json:"previous,omitempty"` }{ T__: "MemberCall", Ref: i.Ref(), @@ -110,7 +110,7 @@ func (i *MemberCallImpl) MarshalJSON() ([]byte, error) { func (i *JImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + Ref *core.Reference[Declare] `json:"ref,omitempty"` }{ T__: "J", Ref: i.Ref(), @@ -120,8 +120,8 @@ func (i *JImpl) MarshalJSON() ([]byte, error) { func (i *KImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref1 *core.Reference[Declare] `json:"ref1"` - Ref2 *core.Reference[Declare] `json:"ref2"` + Ref1 *core.Reference[Declare] `json:"ref1,omitempty"` + Ref2 *core.Reference[Declare] `json:"ref2,omitempty"` }{ T__: "K", Ref1: i.Ref1(), @@ -132,7 +132,7 @@ func (i *KImpl) MarshalJSON() ([]byte, error) { func (i *NImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + Ref *core.Reference[Declare] `json:"ref,omitempty"` }{ T__: "N", Ref: i.Ref(), @@ -142,7 +142,7 @@ func (i *NImpl) MarshalJSON() ([]byte, error) { func (i *OImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Ref *core.Reference[Declare] `json:"ref"` + Ref *core.Reference[Declare] `json:"ref,omitempty"` }{ T__: "O", Ref: i.Ref(), @@ -204,11 +204,13 @@ func (i *EImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref, &ref); err != nil { - return err + if aux.Ref != nil { + ref := core.NewReference[Declare](i, nil, nil) + if err := ref.UnmarshalJSON(aux.Ref); err != nil { + return err + } + i.SetRef(ref) } - i.SetRef(ref) return nil } @@ -239,11 +241,13 @@ func (i *FItemImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref, &ref); err != nil { - return err + if aux.Ref != nil { + ref := core.NewReference[Declare](i, nil, nil) + if err := ref.UnmarshalJSON(aux.Ref); err != nil { + return err + } + i.SetRef(ref) } - i.SetRef(ref) return nil } @@ -255,11 +259,13 @@ func (i *GImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref, &ref); err != nil { - return err + if aux.Ref != nil { + ref := core.NewReference[Declare](i, nil, nil) + if err := ref.UnmarshalJSON(aux.Ref); err != nil { + return err + } + i.SetRef(ref) } - i.SetRef(ref) return nil } @@ -271,11 +277,13 @@ func (i *HImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - member, err := Unmarshal[MemberCall](aux.Member) - if err != nil { - return err + if aux.Member != nil { + member, err := Unmarshal[MemberCall](aux.Member) + if err != nil { + return err + } + i.SetMember(member) } - i.SetMember(member) return nil } @@ -288,16 +296,20 @@ func (i *MemberCallImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref, &ref); err != nil { - return err + if aux.Ref != nil { + ref := core.NewReference[Declare](i, nil, nil) + if err := ref.UnmarshalJSON(aux.Ref); err != nil { + return err + } + i.SetRef(ref) } - i.SetRef(ref) - previous, err := Unmarshal[MemberCall](aux.Previous) - if err != nil { - return err + if aux.Previous != nil { + previous, err := Unmarshal[MemberCall](aux.Previous) + if err != nil { + return err + } + i.SetPrevious(previous) } - i.SetPrevious(previous) return nil } @@ -309,11 +321,13 @@ func (i *JImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref, &ref); err != nil { - return err + if aux.Ref != nil { + ref := core.NewReference[Declare](i, nil, nil) + if err := ref.UnmarshalJSON(aux.Ref); err != nil { + return err + } + i.SetRef(ref) } - i.SetRef(ref) return nil } @@ -326,16 +340,20 @@ func (i *KImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref1 := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref1, &ref1); err != nil { - return err + if aux.Ref1 != nil { + ref1 := core.NewReference[Declare](i, nil, nil) + if err := ref1.UnmarshalJSON(aux.Ref1); err != nil { + return err + } + i.SetRef1(ref1) } - i.SetRef1(ref1) - ref2 := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref2, &ref2); err != nil { - return err + if aux.Ref2 != nil { + ref2 := core.NewReference[Declare](i, nil, nil) + if err := ref2.UnmarshalJSON(aux.Ref2); err != nil { + return err + } + i.SetRef2(ref2) } - i.SetRef2(ref2) return nil } @@ -347,11 +365,13 @@ func (i *NImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref, &ref); err != nil { - return err + if aux.Ref != nil { + ref := core.NewReference[Declare](i, nil, nil) + if err := ref.UnmarshalJSON(aux.Ref); err != nil { + return err + } + i.SetRef(ref) } - i.SetRef(ref) return nil } @@ -363,11 +383,13 @@ func (i *OImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - ref := core.NewReference[Declare](i, nil, nil) - if err := json.Unmarshal(aux.Ref, &ref); err != nil { - return err + if aux.Ref != nil { + ref := core.NewReference[Declare](i, nil, nil) + if err := ref.UnmarshalJSON(aux.Ref); err != nil { + return err + } + i.SetRef(ref) } - i.SetRef(ref) return nil } diff --git a/internal/languages/lookahead/json_gen.go b/internal/languages/lookahead/json_gen.go index 9284b147..f66bce58 100644 --- a/internal/languages/lookahead/json_gen.go +++ b/internal/languages/lookahead/json_gen.go @@ -18,8 +18,8 @@ func newToken(tokenType *core.TokenType, view string) *core.Token { func (i *ObjImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Value string `json:"value"` - Node string `json:"node"` + Value string `json:"value,omitempty"` + Node string `json:"node,omitempty"` }{ T__: "Obj", Value: i.Value(), @@ -30,7 +30,7 @@ func (i *ObjImpl) MarshalJSON() ([]byte, error) { func (i *RootImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Item Obj `json:"item"` + Item Obj `json:"item,omitempty"` }{ T__: "Root", Item: i.Item(), @@ -40,9 +40,9 @@ func (i *RootImpl) MarshalJSON() ([]byte, error) { func (i *BImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Value string `json:"value"` - Node string `json:"node"` - Post string `json:"post"` + Value string `json:"value,omitempty"` + Node string `json:"node,omitempty"` + Post string `json:"post,omitempty"` }{ T__: "B", Value: i.Value(), @@ -76,11 +76,13 @@ func (i *RootImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - item, err := Unmarshal[Obj](aux.Item) - if err != nil { - return err + if aux.Item != nil { + item, err := Unmarshal[Obj](aux.Item) + if err != nil { + return err + } + i.SetItem(item) } - i.SetItem(item) return nil } diff --git a/internal/languages/token_groups/json_gen.go b/internal/languages/token_groups/json_gen.go index 047661c5..1b9dfd8f 100644 --- a/internal/languages/token_groups/json_gen.go +++ b/internal/languages/token_groups/json_gen.go @@ -18,7 +18,7 @@ func newToken(tokenType *core.TokenType, view string) *core.Token { func (i *ModelImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Item Item `json:"item"` + Item Item `json:"item,omitempty"` }{ T__: "Model", Item: i.Item(), @@ -28,7 +28,7 @@ func (i *ModelImpl) MarshalJSON() ([]byte, error) { func (i *ItemImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Value string `json:"value"` + Value string `json:"value,omitempty"` }{ T__: "Item", Value: i.Value(), @@ -38,9 +38,9 @@ func (i *ItemImpl) MarshalJSON() ([]byte, error) { func (i *RecoveryImpl) MarshalJSON() ([]byte, error) { return json.Marshal(struct { T__ string `json:"$type"` - Value string `json:"value"` - First string `json:"first"` - Second string `json:"second"` + Value string `json:"value,omitempty"` + First string `json:"first,omitempty"` + Second string `json:"second,omitempty"` }{ T__: "Recovery", Value: i.Value(), @@ -57,11 +57,13 @@ func (i *ModelImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - item, err := Unmarshal[Item](aux.Item) - if err != nil { - return err + if aux.Item != nil { + item, err := Unmarshal[Item](aux.Item) + if err != nil { + return err + } + i.SetItem(item) } - i.SetItem(item) return nil } diff --git a/reference.go b/reference.go index b759b49d..043e068b 100644 --- a/reference.go +++ b/reference.go @@ -8,6 +8,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "iter" "reflect" "slices" @@ -306,30 +307,32 @@ func (r *Reference[T]) MarshalJSON() ([]byte, error) { } // We expect at this point that all references have been attempted to resolve. if !r.resolved.Load() { - if path, err := r.Owner().NodePath(); err == nil { - return nil, errors.New("Reference.MarshalJSON(): reference not resolved in node '" + path + "'") + if path, err := PathOf(r.Owner()); err == nil { + return nil, errors.New("Reference.MarshalJSON(): reference not resolved in node '" + path.String() + "'") } return nil, errors.New("Reference.MarshalJSON(): reference not resolved") } - var uri string - var errMsg string + var uri, errMsg string if r.err != nil { errMsg = r.err.Msg - } else if referenced := r.ref; any(referenced) != nil { - refPath, err := referenced.NodePath() + refPath, err := PathOf(referenced) if err != nil { - return nil, errors.New("Reference.MarshalJSON(): failed to compute path fragment of referenced node") + referencerPath, _ := PathOf(r.Owner()) + return nil, fmt.Errorf("Reference.MarshalJSON(): failed to compute path fragment of node of type %T referenced by '%s'", referenced, referencerPath) } refDoc := referenced.Document() - if refDoc != nil { - if r.Owner() != nil && r.Owner().Document() == refDoc { - uri = "#" + refPath - } else { - uri = refDoc.URI.WithFragment(refPath).StringUnencoded() - } + if refDoc == nil { + referencerPath, _ := PathOf(r.Owner()) + return nil, fmt.Errorf("Reference.MarshalJSON(): node of type %T referenced by '%s' is not contained in any document", referenced, referencerPath) + } + + if r.Owner() != nil && r.Owner().Document() == refDoc { + uri = "#" + refPath.String() + } else { + uri = refDoc.URI.WithFragment(refPath.String()).StringUnencoded() } } else { return nil, errors.New("Reference.MarshalJSON(): unexpected state") @@ -416,10 +419,8 @@ func NewJsonReferenceGetter[T AstNode](uriString string) ReferenceGetter[T] { uri := ParseURI(uriString) if uri.Path() == "" { document = ref.Owner().Document() - - } else if doc := helper.GetDocument(uri); doc != nil { + } else if doc := helper.GetDocument(uri.WithFragment("") /* zeros the fragment part */); doc != nil { document = doc - } else { return nil, NewReferenceError("Document not found: '" + uri.StringUnencoded() + "'") } @@ -428,7 +429,7 @@ func NewJsonReferenceGetter[T AstNode](uriString string) ReferenceGetter[T] { return nil, NewReferenceError("Document is empty: '" + document.URI.StringUnencoded() + "'") } - node, err := document.Root.GetByPath(uri.Fragment()) + node, err := Resolve(document.Root, uri.Fragment()) if err != nil { return nil, NewReferenceError(err.Error()) } From d5a1707638e8dbcc19aa784f0ead2420fe436510 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Thu, 2 Jul 2026 22:33:39 +0200 Subject: [PATCH 09/16] first json test --- examples/arithmetics/json_test.go | 415 ++++++++++++++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 examples/arithmetics/json_test.go diff --git a/examples/arithmetics/json_test.go b/examples/arithmetics/json_test.go new file mode 100644 index 00000000..d6c2a3f7 --- /dev/null +++ b/examples/arithmetics/json_test.go @@ -0,0 +1,415 @@ +// Copyright 2026 TypeFox GmbH +// This program and the accompanying materials are made available under the +// terms of the MIT License, which is available in the project root. + +package arithmetics + +import ( + "context" + "encoding/json" + "slices" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + core "typefox.dev/fastbelt" + "typefox.dev/fastbelt/test" + utilJson "typefox.dev/fastbelt/util/json" + "typefox.dev/fastbelt/util/service" + "typefox.dev/fastbelt/workspace" +) + +func TestJsonExport(t *testing.T) { + services := CreateServices() + f := test.NewWithContext(t, services, context.WithValue( + context.Background(), core.JsonLinkingHelperKey(), utilJson.NewJsonLinkingHelper( + service.MustGet[workspace.DocumentManager](services), + ), + )) + + cleanup := func() { + f.Clear() + } + + language := service.MustGet[workspace.LanguageID](f.Services()) + documents := service.MustGet[workspace.DocumentManager](f.Services()) + builder := service.MustGet[workspace.Builder](f.Services()) + + t.Run("selfContained", func(t *testing.T) { + t.Cleanup(cleanup) + + doc := f.Parse(` + module test + def two: 2; + def root(x, y): x^(1/y); + def sqrt(x): root(x, two); + sqrt(16); + `) + doc.AssertState(core.DocStateLinked) + doc.AssertNoParseErrors() + doc.AssertNoLinkingErrors() + + res, err := json.MarshalIndent(doc.Root(), "", " ") + require.NoError(t, err) + + assert.Equal(t, selfContainedJson, string(res)) + }) + + t.Run("circular-refs", func(t *testing.T) { + t.Cleanup(cleanup) + + docs := f.ParseAll( + "inmemory:///testA", ` + module A + def root(x, y): two * x^(1/y); + `, + "inmemory:///testB", ` + module B + def two: 2; + def sqrt(x): root(x, two); + sqrt(16); + `, + ) + for _, doc := range docs { + doc.AssertState(core.DocStateLinked) + doc.AssertNoParseErrors() + doc.AssertNoLinkingErrors() + } + + for _, doc := range docs { + res, err := json.MarshalIndent(doc.Root(), "", " ") + require.NoError(t, err) + + switch path := doc.Document.URI.Path(); path { + case "/testA": + assert.Equal(t, circularAJson, string(res)) + case "/testB": + assert.Equal(t, circularBJson, string(res)) + default: + assert.Failf(t, "Unexpected document %s", path) + } + } + }) + + loadJsonDoc := func(uri string, jsonInput string) { + doc, err := core.NewDocumentFromString(uri, string(language), "") + require.NoError(t, err) + mod, err := Unmarshal[Module]([]byte(jsonInput)) + require.NoError(t, err) + doc.Root = mod + doc.State = core.DocStateParsed + core.AssignContainers(doc, mod) + documents.Set(doc) + f.NewDoc(doc, nil, nil) + } + + t.Run("circular-refs-json-text", func(t *testing.T) { + t.Cleanup(cleanup) + + loadJsonDoc("inmemory:///testA.json", circularAJson) + + f.ParseURI(` + module B + def two: 2; + def sqrt(x): root(x, two); + sqrt(16); + `, "inmemory:///testB", + ) + + for doc := range documents.All() { + builder.Reset(doc, core.DocStateParsed) + } + err := builder.Build( + f.Ctx(), slices.Collect(documents.All()), nil, + ) + require.NoError(t, err) + + for _, doc := range f.Documents() { + doc.AssertState(core.DocStateParsed) + doc.AssertState(core.DocStateExportedSymbols) + doc.AssertState(core.DocStateImportedSymbols) + doc.AssertState(core.DocStateLinked) + doc.AssertNoParseErrors() + doc.AssertNoLinkingErrors() + } + + for doc := range documents.All() { + res, err := json.MarshalIndent(doc.Root, "", " ") + require.NoError(t, err) + + switch path := doc.URI.Path(); path { + case "/testA.json": + assert.Equal(t, circularAJson, string(res)) + case "/testB": + assert.Equal(t, strings.ReplaceAll(circularBJson, "inmemory:/testA#", "inmemory:/testA.json#"), string(res)) + default: + assert.Failf(t, "Unexpected document %s", path) + } + } + }) + + t.Run("circular-refs-json-json", func(t *testing.T) { + t.Cleanup(cleanup) + + circularAJsonAdj := strings.ReplaceAll(circularAJson, "inmemory:/testB#", "inmemory:/testB.json#") + loadJsonDoc("inmemory:///testA.json", circularAJsonAdj) + + circularBJsonAdj := strings.ReplaceAll(circularBJson, "inmemory:/testA#", "inmemory:/testA.json#") + loadJsonDoc("inmemory:///testB.json", circularBJsonAdj) + + err := builder.Build( + f.Ctx(), slices.Collect(documents.All()), nil, + ) + require.NoError(t, err) + + for _, doc := range f.Documents() { + doc.AssertState(core.DocStateParsed) + doc.AssertState(core.DocStateExportedSymbols) + doc.AssertState(core.DocStateImportedSymbols) + doc.AssertState(core.DocStateLinked) + doc.AssertNoParseErrors() + doc.AssertNoLinkingErrors() + } + + for doc := range documents.All() { + res, err := json.MarshalIndent(doc.Root, "", " ") + require.NoError(t, err) + + switch path := doc.URI.Path(); path { + case "/testA.json": + assert.Equal(t, circularAJsonAdj, string(res)) + case "/testB.json": + assert.Equal(t, circularBJsonAdj, string(res)) + default: + assert.Failf(t, "Unexpected document %s", path) + } + } + }) +} + +const selfContainedJson = `{ + "$type": "Module", + "name": "test", + "statements": [ + { + "$type": "Definition", + "name": "two", + "expression": { + "$type": "NumberLiteral", + "value": "2" + } + }, + { + "$type": "Definition", + "name": "root", + "args": [ + { + "$type": "DeclaredParameter", + "name": "x" + }, + { + "$type": "DeclaredParameter", + "name": "y" + } + ], + "expression": { + "$type": "BinaryExpression", + "left": { + "$type": "FunctionCall", + "callable": { + "$refText": "x", + "$ref": "#/statements@1/args@0" + } + }, + "operator": "^", + "right": { + "$type": "BinaryExpression", + "left": { + "$type": "NumberLiteral", + "value": "1" + }, + "operator": "/", + "right": { + "$type": "FunctionCall", + "callable": { + "$refText": "y", + "$ref": "#/statements@1/args@1" + } + } + } + } + }, + { + "$type": "Definition", + "name": "sqrt", + "args": [ + { + "$type": "DeclaredParameter", + "name": "x" + } + ], + "expression": { + "$type": "FunctionCall", + "args": [ + { + "$type": "FunctionCall", + "callable": { + "$refText": "x", + "$ref": "#/statements@2/args@0" + } + }, + { + "$type": "FunctionCall", + "callable": { + "$refText": "two", + "$ref": "#/statements@0" + } + } + ], + "callable": { + "$refText": "root", + "$ref": "#/statements@1" + } + } + }, + { + "$type": "Evaluation", + "expression": { + "$type": "FunctionCall", + "args": [ + { + "$type": "NumberLiteral", + "value": "16" + } + ], + "callable": { + "$refText": "sqrt", + "$ref": "#/statements@2" + } + } + } + ] +}` + +const circularAJson = `{ + "$type": "Module", + "name": "A", + "statements": [ + { + "$type": "Definition", + "name": "root", + "args": [ + { + "$type": "DeclaredParameter", + "name": "x" + }, + { + "$type": "DeclaredParameter", + "name": "y" + } + ], + "expression": { + "$type": "BinaryExpression", + "left": { + "$type": "FunctionCall", + "callable": { + "$refText": "two", + "$ref": "inmemory:/testB#/statements@0" + } + }, + "operator": "*", + "right": { + "$type": "BinaryExpression", + "left": { + "$type": "FunctionCall", + "callable": { + "$refText": "x", + "$ref": "#/statements@0/args@0" + } + }, + "operator": "^", + "right": { + "$type": "BinaryExpression", + "left": { + "$type": "NumberLiteral", + "value": "1" + }, + "operator": "/", + "right": { + "$type": "FunctionCall", + "callable": { + "$refText": "y", + "$ref": "#/statements@0/args@1" + } + } + } + } + } + } + ] +}` + +const circularBJson = `{ + "$type": "Module", + "name": "B", + "statements": [ + { + "$type": "Definition", + "name": "two", + "expression": { + "$type": "NumberLiteral", + "value": "2" + } + }, + { + "$type": "Definition", + "name": "sqrt", + "args": [ + { + "$type": "DeclaredParameter", + "name": "x" + } + ], + "expression": { + "$type": "FunctionCall", + "args": [ + { + "$type": "FunctionCall", + "callable": { + "$refText": "x", + "$ref": "#/statements@1/args@0" + } + }, + { + "$type": "FunctionCall", + "callable": { + "$refText": "two", + "$ref": "#/statements@0" + } + } + ], + "callable": { + "$refText": "root", + "$ref": "inmemory:/testA#/statements@0" + } + } + }, + { + "$type": "Evaluation", + "expression": { + "$type": "FunctionCall", + "args": [ + { + "$type": "NumberLiteral", + "value": "16" + } + ], + "callable": { + "$refText": "sqrt", + "$ref": "#/statements@1" + } + } + } + ] +}` From 69b036593d811384cffb30baabb942681b8071e2 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Fri, 3 Jul 2026 15:27:04 +0200 Subject: [PATCH 10/16] fix in symbols handling in 'symbols.go' --- symbols.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/symbols.go b/symbols.go index 3ac23f87..7192d3dd 100644 --- a/symbols.go +++ b/symbols.go @@ -125,6 +125,12 @@ func (c *mergedSymbolContainer) All() SymbolSeq { func (c *mergedSymbolContainer) ForType(targetType reflect.Type) SymbolSeq { return extiter.FlatMap(c.containers, func(container SymbolContainer) SymbolSeq { + if container == nil { + // in case a document in the document manager hasn't been build yet or has been reset + // and is not included in the current build cycle its exported symbols container may be absent; + // map 'nil' to an empty seq in such cases + return func(yield func(*SymbolDescription) bool) {} + } return container.ForType(targetType) }) } From 009da0d3cc337cb894ad1e90e7904013d5ef2adf Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Fri, 3 Jul 2026 10:44:05 +0200 Subject: [PATCH 11/16] some supplements to 'fixture.go' --- test/fixture.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/fixture.go b/test/fixture.go index 5a6cc5ee..2777fdfb 100644 --- a/test/fixture.go +++ b/test/fixture.go @@ -147,7 +147,7 @@ func (f *Fixture) ParseURI(content, uri string) *Doc { if err := builder.Build(f.ctx, []*core.Document{doc}, nil); err != nil { f.t.Fatalf("fbtest: build failed: %v", err) } - return f.newDoc(doc, ranges, indices) + return f.NewDoc(doc, ranges, indices) } // ParseAll builds multiple documents together, enabling cross-document reference @@ -174,7 +174,7 @@ func (f *Fixture) ParseAll(uriContentPairs ...string) []*Doc { documents := service.MustGet[workspace.DocumentManager](f.sc) documents.Set(doc) coreDocs = append(coreDocs, doc) - results = append(results, f.newDoc(doc, ranges, indices)) + results = append(results, f.NewDoc(doc, ranges, indices)) } builder := service.MustGet[workspace.Builder](f.sc) if err := builder.Build(f.ctx, coreDocs, nil); err != nil { @@ -183,12 +183,22 @@ func (f *Fixture) ParseAll(uriContentPairs ...string) []*Doc { return results } -func (f *Fixture) newDoc(doc *core.Document, ranges []RangeMarker, indices []IndexMarker) *Doc { +func (f *Fixture) NewDoc(doc *core.Document, ranges []RangeMarker, indices []IndexMarker) *Doc { testDoc := &Doc{Document: doc, Ranges: ranges, Indices: indices, fixture: f} f.docs = append(f.docs, testDoc) return testDoc } +func (f *Fixture) Delete(uri core.URI) { + documents := service.MustGet[workspace.DocumentManager](f.sc) + documents.Delete(uri) +} + +func (f *Fixture) Clear() { + documents := service.MustGet[workspace.DocumentManager](f.sc) + documents.Clear() +} + // extractMarkers scans content for embedded position markers, removes them, and // records their locations relative to the cleaned text. Range markers are matched // before index markers when they share the same opening delimiter. From fd301e36f27c22c13accd7e0808adba7610ceb3f Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Thu, 9 Jul 2026 14:32:28 +0200 Subject: [PATCH 12/16] json roundtrip test with/for grammar language --- internal/grammar/json_test.go | 236 ++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 internal/grammar/json_test.go diff --git a/internal/grammar/json_test.go b/internal/grammar/json_test.go new file mode 100644 index 00000000..6d984ece --- /dev/null +++ b/internal/grammar/json_test.go @@ -0,0 +1,236 @@ +// Copyright 2026 TypeFox GmbH +// This program and the accompanying materials are made available under the +// terms of the MIT License, which is available in the project root. + +package grammar + +import ( + "context" + "encoding/json" + "iter" + "os" + "reflect" + "slices" + "strings" + "testing" + "unique" + + "github.com/stretchr/testify/require" + core "typefox.dev/fastbelt" + "typefox.dev/fastbelt/test" + utilJson "typefox.dev/fastbelt/util/json" + "typefox.dev/fastbelt/util/service" + "typefox.dev/fastbelt/workspace" +) + +func TestJsonRoundtrip(t *testing.T) { + tests := []struct { + name string + path string + }{ + {"Fastbelt Grammar language", "grammar.fb"}, + {"Completion test language", "../languages/completion/completion.fb"}, + {"Lookahead test language", "../languages/lookahead/lookahead.fb"}, + {"TokenGroups test language", "../languages/token_groups/token_groups.fb"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + grammmar, err := os.ReadFile(tt.path) + require.NoError(t, err) + + exported, imported := parseExportImportGrammar(t, grammmar) + assertEqualAst(t, exported, imported) // expected := exported, actual := imported + }) + } +} + +func BenchmarkJsonRoundtrip(b *testing.B) { + tests := []struct { + name string + path string + }{ + {"Fastbelt Grammar language", "grammar.fb"}, + {"Completion test language", "../languages/completion/completion.fb"}, + {"Lookahead test language", "../languages/lookahead/lookahead.fb"}, + {"TokenGroups test language", "../languages/token_groups/token_groups.fb"}, + } + for _, tt := range tests { + grammar, err := os.ReadFile(tt.path) + if err != nil { + b.Fatal(err) + } + b.Run(tt.name, func(b *testing.B) { + for b.Loop() { + exported, imported := parseExportImportGrammar(b, grammar) + assertEqualAst(b, exported, imported) + } + }) + } +} + +func parseExportImportGrammar(t testing.TB, grammar []byte) (core.AstNode, core.AstNode) { + // Step 1: First container — parse grammar.fb from disk + services1 := CreateServices() + f1 := test.New(t, services1) + defer f1.Clear() + doc1 := f1.Parse(string(grammar)) + doc1.AssertNoParseErrors() + doc1.AssertNoLinkingErrors() + + // Step 2: Marshal to JSON via encoding/json + jsonData, err := json.Marshal(doc1.Root()) + require.NoError(t, err) + + // Step 3: Second container — independent services instance + services2 := CreateServices() + f2 := test.NewWithContext(t, services2, context.WithValue( + context.Background(), core.JsonLinkingHelperKey(), utilJson.NewJsonLinkingHelper( + service.MustGet[workspace.DocumentManager](services2), + ), + )) + defer f2.Clear() + + language2 := service.MustGet[workspace.LanguageID](f2.Services()) + documents2 := service.MustGet[workspace.DocumentManager](f2.Services()) + builder2 := service.MustGet[workspace.Builder](f2.Services()) + + // Step 4: Unmarshal via encoding/json and wire up the document + grammar2 := NewGrammar() + err = json.Unmarshal(jsonData, grammar2) + require.NoError(t, err) + + doc2, err := core.NewDocumentFromString("inmemory:///grammar.fb", string(language2), "") + require.NoError(t, err) + doc2.Root = grammar2 + doc2.State = core.DocStateParsed + core.AssignContainers(doc2, grammar2) + documents2.Set(doc2) + f2.NewDoc(doc2, nil, nil) + + // Step 5: Run the full build pipeline on the unmarshaled document + err = builder2.Build(f2.Ctx(), slices.Collect(documents2.All()), nil) + require.NoError(t, err) + + return doc1.Root(), doc2.Root +} + +var REF_TOKEN_TYPE = reflect.TypeFor[*core.Token]() + +// assertEqualAst recursively compares two AST subtrees node by node. Diffs are +// reported via t.Errorf with the node's document path from core.PathOf. +// Returns true when at least one difference was detected. +func assertEqualAst(t testing.TB, expected, actual core.AstNode) bool { + t.Helper() + + // 1. Concrete type must match. + if reflect.TypeOf(expected) != reflect.TypeOf(actual) { + p, _ := core.PathOf(expected) + t.Errorf("at %q: type mismatch: expected %T, got %T", p, expected, actual) + return false + } + + // 2. If named names must match. + type named interface{ Name() string } + expectedNamed, expectedIsNamed := expected.(named) + actualNamed, actualIsNamed := actual.(named) + if expectedIsNamed && actualIsNamed && expectedNamed.Name() != actualNamed.Name() { + p, _ := core.PathOf(expected) + t.Errorf("at %q: Name mismatch: expected %s, got %s", p, expectedNamed.Name(), actualNamed.Name()) + } + + // 3. Primitive field values must match + // create a reflect.Value of the AstNode: + // * fetch child (1) that is the specific '...Data' struct, child (0) is the 'AstNodeBase' struct + // * the derive a reference value via '.Addr()', and + // * wrap it into an array being used as argument while calling the getters, they're defined with pointer receivers + expectedValue := reflect.ValueOf(expected).Elem().FieldByIndex([]int{1}) + expectedMethodArg := []reflect.Value{expectedValue.Addr()} + + actualValue := reflect.ValueOf(actual).Elem().FieldByIndex([]int{1}) + actualMethodArg := []reflect.Value{actualValue.Addr()} + + // * iterate the '...Data' type fields and consider those of the types 'bool' and '*core.Token' + + for _, field := range slices.Collect(expectedValue.Type().Fields()) { + kind := field.Type.Kind() + switch { + case kind == reflect.Bool: + p, _ := core.PathOf(expected) + getter, exists := expectedValue.Addr().Type().MethodByName(strings.ToUpper(field.Name[0:1]) + field.Name[1:]) + if !exists { + t.Errorf("at %q, type %T: string value getter for field %s missing", p, expectedValue, field.Name) + } + exp := getter.Func.Call(expectedMethodArg)[0].Bool() + act := getter.Func.Call(actualMethodArg)[0].Bool() + if exp != act { + t.Errorf("at %q: primitive bool field '%s' mismatch\n expected: %t\n actual: %t", p, field.Name, exp, act) + } + case kind == reflect.Pointer && field.Type == REF_TOKEN_TYPE: + p, _ := core.PathOf(expected) + getter, exists := expectedValue.Addr().Type().MethodByName(strings.ToUpper(field.Name[0:1]) + field.Name[1:]) + if !exists { + t.Errorf("at %q, type %T: string value getter for field %s missing", p, expectedValue, field.Name) + } + exp := getter.Func.Call(expectedMethodArg)[0].String() + act := getter.Func.Call(actualMethodArg)[0].String() + if exp != act { + t.Errorf("at %q: primitive string field '%s' mismatch\n expected: %s\n actual: %s", p, field.Name, exp, act) + } + } + } + + // Collect child nodes from both sides. + type child struct { + node core.AstNode + feature unique.Handle[string] + index int + } + var ( + expectedChildren = make([]child, 0, 10) + actualChildren = make([]child, 0, 10) + ) + expected.ForEachNode(func(node core.AstNode, feature unique.Handle[string], index int) { + expectedChildren = append(expectedChildren, child{node, feature, index}) + }) + actual.ForEachNode(func(node core.AstNode, feature unique.Handle[string], index int) { + actualChildren = append(actualChildren, child{node, feature, index}) + }) + + // 4. Recurse into each corresponding child pair. + // * check presence (amount) of children on each side + // * check containment (feature, index) + // * check deep equality of child nodes + expectedIter, stopE := iter.Pull(slices.Values(expectedChildren)) + defer stopE() + actualIter, stopA := iter.Pull(slices.Values(actualChildren)) + defer stopA() + for { + itemE, validE := expectedIter() + itemA, validA := actualIter() + + if !validE && !validA { + break + } + if validE != validA { + if validE { + pChild, _ := core.PathOf(itemE.node) + pActual, _ := core.PathOf(actual) + t.Errorf("at %q: child element mismatch\n no counter part for %s of expected (field: %s, index: %d) in actual ", pActual, pChild, itemE.feature.Value(), itemE.index) + } else { + pChild, _ := core.PathOf(itemA.node) + pExpected, _ := core.PathOf(expected) + t.Errorf("at %q: child element mismatch\n no counter part for %s of actual (field: %s, index: %d) in expected ", pExpected, pChild, itemA.feature.Value(), itemA.index) + } + } else if itemE.feature != itemA.feature { + p, _ := core.PathOf(expected) + t.Errorf("at %q: child feature mismatch\n child contained in %s (expected, index: %d) vs %s (actual, index: %d)", p, itemE.feature.Value(), itemE.index, itemA.feature.Value(), itemA.index) + + // no need to check for index diff/equality, as position mismatches will cause other diffs being checked above or below, like container feature diffs or child type/primitive field value diffs + // therefore, continue with comparing the individual children + } else if !assertEqualAst(t, itemE.node, itemA.node) { + return false + } + } + + return true +} From 058f2235a60916da41587c0426eaedd8129ac639 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Thu, 9 Jul 2026 17:01:21 +0200 Subject: [PATCH 13/16] refined benchmark to capture relevant measures only --- internal/grammar/json_test.go | 40 +++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/internal/grammar/json_test.go b/internal/grammar/json_test.go index 6d984ece..2e91c9af 100644 --- a/internal/grammar/json_test.go +++ b/internal/grammar/json_test.go @@ -54,15 +54,43 @@ func BenchmarkJsonRoundtrip(b *testing.B) { {"Lookahead test language", "../languages/lookahead/lookahead.fb"}, {"TokenGroups test language", "../languages/token_groups/token_groups.fb"}, } + services1 := CreateServices() + f1 := test.New(b, services1) for _, tt := range tests { grammar, err := os.ReadFile(tt.path) - if err != nil { - b.Fatal(err) - } - b.Run(tt.name, func(b *testing.B) { + require.NoError(b, err) + + doc1 := f1.Parse(string(grammar)) + doc1.AssertNoParseErrors() + doc1.AssertNoLinkingErrors() + grammar1 := doc1.Root() + f1.Clear() + inJson, err := json.Marshal(grammar1) + require.NoError(b, err) + + b.Run(tt.name+"/marshal", func(b *testing.B) { + b.ResetTimer() + for b.Loop() { + if _, err := json.Marshal(grammar1); err != nil { + b.Fatal(err) + } + } + }) + b.Run(tt.name+"/unmarshal", func(b *testing.B) { + b.ResetTimer() + for b.Loop() { + grammar2 := NewGrammar() + if err := json.Unmarshal(inJson, grammar2); err != nil { + b.Fatal(err) + } + } + }) + b.Run(tt.name+"/unmarshal-self", func(b *testing.B) { + b.ResetTimer() for b.Loop() { - exported, imported := parseExportImportGrammar(b, grammar) - assertEqualAst(b, exported, imported) + if _, err := Unmarshal[Grammar](inJson); err != nil { + b.Fatal(err) + } } }) } From 666503a0dcc1850eddd59ef45825c2e7bf368852 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Thu, 9 Jul 2026 17:03:16 +0200 Subject: [PATCH 14/16] optimization of slice allocation during unmarshaling --- examples/arithmetics/json_gen.go | 6 +++--- examples/statemachine/json_gen.go | 10 +++++----- internal/generator/json_generator.go | 2 +- internal/grammar/json_gen.go | 24 +++++++++++------------ internal/languages/completion/json_gen.go | 6 +++--- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/arithmetics/json_gen.go b/examples/arithmetics/json_gen.go index c3140b19..6feac590 100644 --- a/examples/arithmetics/json_gen.go +++ b/examples/arithmetics/json_gen.go @@ -133,7 +133,7 @@ func (i *ModuleImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.statements = []Statement{} + i.statements = make([]Statement, 0, len(aux.Statements)) for _, item := range aux.Statements { node, err := Unmarshal[Statement](item) if err != nil { @@ -171,7 +171,7 @@ func (i *DefinitionImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.args = []DeclaredParameter{} + i.args = make([]DeclaredParameter, 0, len(aux.Args)) for _, item := range aux.Args { node, err := Unmarshal[DeclaredParameter](item) if err != nil { @@ -260,7 +260,7 @@ func (i *FunctionCallImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.args = []Expression{} + i.args = make([]Expression, 0, len(aux.Args)) for _, item := range aux.Args { node, err := Unmarshal[Expression](item) if err != nil { diff --git a/examples/statemachine/json_gen.go b/examples/statemachine/json_gen.go index 6969b3b2..c26815bd 100644 --- a/examples/statemachine/json_gen.go +++ b/examples/statemachine/json_gen.go @@ -92,7 +92,7 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.events = []Event{} + i.events = make([]Event, 0, len(aux.Events)) for _, item := range aux.Events { node, err := Unmarshal[Event](item) if err != nil { @@ -100,7 +100,7 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { } i.SetEventsItem(node) } - i.commands = []Command{} + i.commands = make([]Command, 0, len(aux.Commands)) for _, item := range aux.Commands { node, err := Unmarshal[Command](item) if err != nil { @@ -115,7 +115,7 @@ func (i *StatemachineImpl) UnmarshalJSON(data []byte) error { } i.SetInit(init) } - i.states = []State{} + i.states = make([]State, 0, len(aux.States)) for _, item := range aux.States { node, err := Unmarshal[State](item) if err != nil { @@ -161,7 +161,7 @@ func (i *StateImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.actions = []*core.Reference[Command]{} + i.actions = make([]*core.Reference[Command], 0, len(aux.Actions)) for _, item := range aux.Actions { node := core.NewReference[Command](i, nil, nil) if err := node.UnmarshalJSON(item); err != nil { @@ -169,7 +169,7 @@ func (i *StateImpl) UnmarshalJSON(data []byte) error { } i.SetActionsItem(node) } - i.transitions = []Transition{} + i.transitions = make([]Transition, 0, len(aux.Transitions)) for _, item := range aux.Transitions { node, err := Unmarshal[Transition](item) if err != nil { diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go index bc0764c4..41593b2f 100644 --- a/internal/generator/json_generator.go +++ b/internal/generator/json_generator.go @@ -158,7 +158,7 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { hasComposeNodeTempVar := false for _, field := range fields { if field.Array { - n.AppendLine("i.", field.PName, " = []", field.GType, "{}") + n.AppendLine("i.", field.PName, " = make([]", field.GType, ", 0, len(aux."+field.Name+"))") n.AppendLine("for _, item := range aux.", field.Name, " {") n.Indent(func(n2 codegen.Node) { switch field.GType { diff --git a/internal/grammar/json_gen.go b/internal/grammar/json_gen.go index 7496ff7e..fb849981 100644 --- a/internal/grammar/json_gen.go +++ b/internal/grammar/json_gen.go @@ -331,7 +331,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.rules = []ParserRule{} + i.rules = make([]ParserRule, 0, len(aux.Rules)) for _, item := range aux.Rules { node, err := Unmarshal[ParserRule](item) if err != nil { @@ -339,7 +339,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.SetRulesItem(node) } - i.composites = []CompositeRule{} + i.composites = make([]CompositeRule, 0, len(aux.Composites)) for _, item := range aux.Composites { node, err := Unmarshal[CompositeRule](item) if err != nil { @@ -347,7 +347,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.SetCompositesItem(node) } - i.terminals = []Token{} + i.terminals = make([]Token, 0, len(aux.Terminals)) for _, item := range aux.Terminals { node, err := Unmarshal[Token](item) if err != nil { @@ -355,7 +355,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.SetTerminalsItem(node) } - i.tokenGroups = []TokenGroup{} + i.tokenGroups = make([]TokenGroup, 0, len(aux.TokenGroups)) for _, item := range aux.TokenGroups { node, err := Unmarshal[TokenGroup](item) if err != nil { @@ -363,7 +363,7 @@ func (i *GrammarImpl) UnmarshalJSON(data []byte) error { } i.SetTokenGroupsItem(node) } - i.interfaces = []Interface{} + i.interfaces = make([]Interface, 0, len(aux.Interfaces)) for _, item := range aux.Interfaces { node, err := Unmarshal[Interface](item) if err != nil { @@ -385,7 +385,7 @@ func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.extends = []*core.Reference[Interface]{} + i.extends = make([]*core.Reference[Interface], 0, len(aux.Extends)) for _, item := range aux.Extends { node := core.NewReference[Interface](i, nil, nil) if err := node.UnmarshalJSON(item); err != nil { @@ -393,7 +393,7 @@ func (i *InterfaceImpl) UnmarshalJSON(data []byte) error { } i.SetExtendsItem(node) } - i.fields = []Field{} + i.fields = make([]Field, 0, len(aux.Fields)) for _, item := range aux.Fields { node, err := Unmarshal[Field](item) if err != nil { @@ -598,7 +598,7 @@ func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { return err } i.SetName(newToken(Token_ID, aux.Name)) - i.tokenRefs = []*core.Reference[AbstractTokenRule]{} + i.tokenRefs = make([]*core.Reference[AbstractTokenRule], 0, len(aux.TokenRefs)) for _, item := range aux.TokenRefs { node := core.NewReference[AbstractTokenRule](i, nil, nil) if err := node.UnmarshalJSON(item); err != nil { @@ -606,11 +606,11 @@ func (i *TokenGroupImpl) UnmarshalJSON(data []byte) error { } i.SetTokenRefsItem(node) } - i.regexps = []*core.Token{} + i.regexps = make([]*core.Token, 0, len(aux.Regexps)) for _, item := range aux.Regexps { i.SetRegexpsItem(newToken(Token_ID, item)) } - i.keywords = []Keyword{} + i.keywords = make([]Keyword, 0, len(aux.Keywords)) for _, item := range aux.Keywords { node, err := Unmarshal[Keyword](item) if err != nil { @@ -643,7 +643,7 @@ func (i *AlternativesImpl) UnmarshalJSON(data []byte) error { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - i.alts = []Element{} + i.alts = make([]Element, 0, len(aux.Alts)) for _, item := range aux.Alts { node, err := Unmarshal[Element](item) if err != nil { @@ -664,7 +664,7 @@ func (i *GroupImpl) UnmarshalJSON(data []byte) error { return err } i.SetCardinality(newToken(Token_ID, aux.Cardinality)) - i.elements = []Element{} + i.elements = make([]Element, 0, len(aux.Elements)) for _, item := range aux.Elements { node, err := Unmarshal[Element](item) if err != nil { diff --git a/internal/languages/completion/json_gen.go b/internal/languages/completion/json_gen.go index c5979490..e2f8dba2 100644 --- a/internal/languages/completion/json_gen.go +++ b/internal/languages/completion/json_gen.go @@ -161,7 +161,7 @@ func (i *RootImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.objects = []Obj{} + i.objects = make([]Obj, 0, len(aux.Objects)) for _, item := range aux.Objects { node, err := Unmarshal[Obj](item) if err != nil { @@ -185,7 +185,7 @@ func (i *DeclareImpl) UnmarshalJSON(data []byte) error { cn = core.NewCompositeNode() cn.SetToken(newToken(Token_ID, aux.Name)) i.SetName(cn) - i.children = []Declare{} + i.children = make([]Declare, 0, len(aux.Children)) for _, item := range aux.Children { node, err := Unmarshal[Declare](item) if err != nil { @@ -222,7 +222,7 @@ func (i *FImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, aux); err != nil { return err } - i.items = []FItem{} + i.items = make([]FItem, 0, len(aux.Items)) for _, item := range aux.Items { node, err := Unmarshal[FItem](item) if err != nil { From 2ffe69ee0771a4cb29186f78a19a8b402071a49a Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Mon, 20 Jul 2026 13:28:47 +0200 Subject: [PATCH 15/16] updates after rebase, moved 'AssertEqualAst' into '/fastbelt/test' enabling re-use --- examples/arithmetics/json_test.go | 2 +- internal/grammar/json_test.go | 129 +--------------------------- test/doc_fixture.go | 4 + test/doc_fixture_util.go | 138 ++++++++++++++++++++++++++++++ util/json/json.go | 2 +- 5 files changed, 146 insertions(+), 129 deletions(-) create mode 100644 test/doc_fixture_util.go diff --git a/examples/arithmetics/json_test.go b/examples/arithmetics/json_test.go index d6c2a3f7..6d4fa6c2 100644 --- a/examples/arithmetics/json_test.go +++ b/examples/arithmetics/json_test.go @@ -99,7 +99,7 @@ func TestJsonExport(t *testing.T) { require.NoError(t, err) doc.Root = mod doc.State = core.DocStateParsed - core.AssignContainers(doc, mod) + core.AssignContainers(doc) documents.Set(doc) f.NewDoc(doc, nil, nil) } diff --git a/internal/grammar/json_test.go b/internal/grammar/json_test.go index 2e91c9af..4b74269d 100644 --- a/internal/grammar/json_test.go +++ b/internal/grammar/json_test.go @@ -7,13 +7,9 @@ package grammar import ( "context" "encoding/json" - "iter" "os" - "reflect" "slices" - "strings" "testing" - "unique" "github.com/stretchr/testify/require" core "typefox.dev/fastbelt" @@ -39,7 +35,7 @@ func TestJsonRoundtrip(t *testing.T) { require.NoError(t, err) exported, imported := parseExportImportGrammar(t, grammmar) - assertEqualAst(t, exported, imported) // expected := exported, actual := imported + test.AssertEqualAst(t, exported, imported) // expected := exported, actual := imported }) } } @@ -131,7 +127,7 @@ func parseExportImportGrammar(t testing.TB, grammar []byte) (core.AstNode, core. require.NoError(t, err) doc2.Root = grammar2 doc2.State = core.DocStateParsed - core.AssignContainers(doc2, grammar2) + core.AssignContainers(doc2) documents2.Set(doc2) f2.NewDoc(doc2, nil, nil) @@ -141,124 +137,3 @@ func parseExportImportGrammar(t testing.TB, grammar []byte) (core.AstNode, core. return doc1.Root(), doc2.Root } - -var REF_TOKEN_TYPE = reflect.TypeFor[*core.Token]() - -// assertEqualAst recursively compares two AST subtrees node by node. Diffs are -// reported via t.Errorf with the node's document path from core.PathOf. -// Returns true when at least one difference was detected. -func assertEqualAst(t testing.TB, expected, actual core.AstNode) bool { - t.Helper() - - // 1. Concrete type must match. - if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - p, _ := core.PathOf(expected) - t.Errorf("at %q: type mismatch: expected %T, got %T", p, expected, actual) - return false - } - - // 2. If named names must match. - type named interface{ Name() string } - expectedNamed, expectedIsNamed := expected.(named) - actualNamed, actualIsNamed := actual.(named) - if expectedIsNamed && actualIsNamed && expectedNamed.Name() != actualNamed.Name() { - p, _ := core.PathOf(expected) - t.Errorf("at %q: Name mismatch: expected %s, got %s", p, expectedNamed.Name(), actualNamed.Name()) - } - - // 3. Primitive field values must match - // create a reflect.Value of the AstNode: - // * fetch child (1) that is the specific '...Data' struct, child (0) is the 'AstNodeBase' struct - // * the derive a reference value via '.Addr()', and - // * wrap it into an array being used as argument while calling the getters, they're defined with pointer receivers - expectedValue := reflect.ValueOf(expected).Elem().FieldByIndex([]int{1}) - expectedMethodArg := []reflect.Value{expectedValue.Addr()} - - actualValue := reflect.ValueOf(actual).Elem().FieldByIndex([]int{1}) - actualMethodArg := []reflect.Value{actualValue.Addr()} - - // * iterate the '...Data' type fields and consider those of the types 'bool' and '*core.Token' - - for _, field := range slices.Collect(expectedValue.Type().Fields()) { - kind := field.Type.Kind() - switch { - case kind == reflect.Bool: - p, _ := core.PathOf(expected) - getter, exists := expectedValue.Addr().Type().MethodByName(strings.ToUpper(field.Name[0:1]) + field.Name[1:]) - if !exists { - t.Errorf("at %q, type %T: string value getter for field %s missing", p, expectedValue, field.Name) - } - exp := getter.Func.Call(expectedMethodArg)[0].Bool() - act := getter.Func.Call(actualMethodArg)[0].Bool() - if exp != act { - t.Errorf("at %q: primitive bool field '%s' mismatch\n expected: %t\n actual: %t", p, field.Name, exp, act) - } - case kind == reflect.Pointer && field.Type == REF_TOKEN_TYPE: - p, _ := core.PathOf(expected) - getter, exists := expectedValue.Addr().Type().MethodByName(strings.ToUpper(field.Name[0:1]) + field.Name[1:]) - if !exists { - t.Errorf("at %q, type %T: string value getter for field %s missing", p, expectedValue, field.Name) - } - exp := getter.Func.Call(expectedMethodArg)[0].String() - act := getter.Func.Call(actualMethodArg)[0].String() - if exp != act { - t.Errorf("at %q: primitive string field '%s' mismatch\n expected: %s\n actual: %s", p, field.Name, exp, act) - } - } - } - - // Collect child nodes from both sides. - type child struct { - node core.AstNode - feature unique.Handle[string] - index int - } - var ( - expectedChildren = make([]child, 0, 10) - actualChildren = make([]child, 0, 10) - ) - expected.ForEachNode(func(node core.AstNode, feature unique.Handle[string], index int) { - expectedChildren = append(expectedChildren, child{node, feature, index}) - }) - actual.ForEachNode(func(node core.AstNode, feature unique.Handle[string], index int) { - actualChildren = append(actualChildren, child{node, feature, index}) - }) - - // 4. Recurse into each corresponding child pair. - // * check presence (amount) of children on each side - // * check containment (feature, index) - // * check deep equality of child nodes - expectedIter, stopE := iter.Pull(slices.Values(expectedChildren)) - defer stopE() - actualIter, stopA := iter.Pull(slices.Values(actualChildren)) - defer stopA() - for { - itemE, validE := expectedIter() - itemA, validA := actualIter() - - if !validE && !validA { - break - } - if validE != validA { - if validE { - pChild, _ := core.PathOf(itemE.node) - pActual, _ := core.PathOf(actual) - t.Errorf("at %q: child element mismatch\n no counter part for %s of expected (field: %s, index: %d) in actual ", pActual, pChild, itemE.feature.Value(), itemE.index) - } else { - pChild, _ := core.PathOf(itemA.node) - pExpected, _ := core.PathOf(expected) - t.Errorf("at %q: child element mismatch\n no counter part for %s of actual (field: %s, index: %d) in expected ", pExpected, pChild, itemA.feature.Value(), itemA.index) - } - } else if itemE.feature != itemA.feature { - p, _ := core.PathOf(expected) - t.Errorf("at %q: child feature mismatch\n child contained in %s (expected, index: %d) vs %s (actual, index: %d)", p, itemE.feature.Value(), itemE.index, itemA.feature.Value(), itemA.index) - - // no need to check for index diff/equality, as position mismatches will cause other diffs being checked above or below, like container feature diffs or child type/primitive field value diffs - // therefore, continue with comparing the individual children - } else if !assertEqualAst(t, itemE.node, itemA.node) { - return false - } - } - - return true -} diff --git a/test/doc_fixture.go b/test/doc_fixture.go index 6c28eff9..e8b87ea6 100644 --- a/test/doc_fixture.go +++ b/test/doc_fixture.go @@ -1,3 +1,7 @@ +// Copyright 2026 TypeFox GmbH +// This program and the accompanying materials are made available under the +// terms of the MIT License, which is available in the project root. + package test import ( diff --git a/test/doc_fixture_util.go b/test/doc_fixture_util.go new file mode 100644 index 00000000..04b5e8fa --- /dev/null +++ b/test/doc_fixture_util.go @@ -0,0 +1,138 @@ +// Copyright 2026 TypeFox GmbH +// This program and the accompanying materials are made available under the +// terms of the MIT License, which is available in the project root. + +package test + +import ( + "iter" + "reflect" + "slices" + "strings" + "testing" + "unique" + + core "typefox.dev/fastbelt" +) + +var REF_TOKEN_TYPE = reflect.TypeFor[*core.Token]() + +// AssertEqualAst recursively compares two AST subtrees node by node. Diffs are +// reported via t.Errorf with the node's document path from core.PathOf. +// Will early exit and return false if concrete types of expected and actual mismatch. +// Will return true otherwise. +func AssertEqualAst(t testing.TB, expected, actual core.AstNode) bool { + t.Helper() + + // 1. Concrete type must match. + if reflect.TypeOf(expected) != reflect.TypeOf(actual) { + p, _ := core.PathOf(expected) + t.Errorf("at %q: type mismatch: expected %T, got %T", p, expected, actual) + return false + } + + // 2. If named names must match. + type named interface{ Name() string } + expectedNamed, expectedIsNamed := expected.(named) + actualNamed, actualIsNamed := actual.(named) + if expectedIsNamed && actualIsNamed && expectedNamed.Name() != actualNamed.Name() { + p, _ := core.PathOf(expected) + t.Errorf("at %q: Name mismatch: expected %s, got %s", p, expectedNamed.Name(), actualNamed.Name()) + } + + // 3. Primitive field values must match + // create a reflect.Value of the AstNode: + // * fetch child (1) that is the specific '...Data' struct, child (0) is the 'AstNodeBase' struct + // * then derive a reference value via '.Addr()', and + // * wrap it into an array being used as argument while calling the getters, they're defined with pointer receivers + expectedValue := reflect.ValueOf(expected).Elem().FieldByIndex([]int{1}) + expectedMethodArg := []reflect.Value{expectedValue.Addr()} + + actualValue := reflect.ValueOf(actual).Elem().FieldByIndex([]int{1}) + actualMethodArg := []reflect.Value{actualValue.Addr()} + + // * iterate the '...Data' type fields and consider those of the types 'bool' and '*core.Token' + + for _, field := range slices.Collect(expectedValue.Type().Fields()) { + kind := field.Type.Kind() + switch { + case kind == reflect.Bool: + p, _ := core.PathOf(expected) + getter, exists := expectedValue.Addr().Type().MethodByName(strings.ToUpper(field.Name[0:1]) + field.Name[1:]) + if !exists { + t.Errorf("at %q, type %T: string value getter for field %s missing", p, expectedValue, field.Name) + } + exp := getter.Func.Call(expectedMethodArg)[0].Bool() + act := getter.Func.Call(actualMethodArg)[0].Bool() + if exp != act { + t.Errorf("at %q: primitive bool field '%s' mismatch\n expected: %t\n actual: %t", p, field.Name, exp, act) + } + case kind == reflect.Pointer && field.Type == REF_TOKEN_TYPE: + p, _ := core.PathOf(expected) + getter, exists := expectedValue.Addr().Type().MethodByName(strings.ToUpper(field.Name[0:1]) + field.Name[1:]) + if !exists { + t.Errorf("at %q, type %T: string value getter for field %s missing", p, expectedValue, field.Name) + } + exp := getter.Func.Call(expectedMethodArg)[0].String() + act := getter.Func.Call(actualMethodArg)[0].String() + if exp != act { + t.Errorf("at %q: primitive string field '%s' mismatch\n expected: %s\n actual: %s", p, field.Name, exp, act) + } + } + } + + // Collect child nodes from both sides. + type child struct { + node core.AstNode + feature unique.Handle[string] + index int + } + var ( + expectedChildren = make([]child, 0, 10) + actualChildren = make([]child, 0, 10) + ) + expected.ForEachNode(func(node core.AstNode, feature unique.Handle[string], index int) { + expectedChildren = append(expectedChildren, child{node, feature, index}) + }) + actual.ForEachNode(func(node core.AstNode, feature unique.Handle[string], index int) { + actualChildren = append(actualChildren, child{node, feature, index}) + }) + + // 4. Recurse into each corresponding child pair. + // * check presence (amount) of children on each side + // * check containment (feature, index) + // * check deep equality of child nodes + expectedIter, stopE := iter.Pull(slices.Values(expectedChildren)) + defer stopE() + actualIter, stopA := iter.Pull(slices.Values(actualChildren)) + defer stopA() + for { + itemE, validE := expectedIter() + itemA, validA := actualIter() + + if !validE && !validA { + break + } + if validE != validA { + if validE { + pChild, _ := core.PathOf(itemE.node) + pActual, _ := core.PathOf(actual) + t.Errorf("at %q: child element mismatch\n no counter part for %s of expected (field: %s, index: %d) in actual ", pActual, pChild, itemE.feature.Value(), itemE.index) + } else { + pChild, _ := core.PathOf(itemA.node) + pExpected, _ := core.PathOf(expected) + t.Errorf("at %q: child element mismatch\n no counter part for %s of actual (field: %s, index: %d) in expected ", pExpected, pChild, itemA.feature.Value(), itemA.index) + } + } else if itemE.feature != itemA.feature { + p, _ := core.PathOf(expected) + t.Errorf("at %q: child feature mismatch\n child contained in %s (expected, index: %d) vs %s (actual, index: %d)", p, itemE.feature.Value(), itemE.index, itemA.feature.Value(), itemA.index) + + // no need to check for index diff/equality, as position mismatches will cause other diffs being checked above or below, like container feature diffs or child type/primitive field value diffs + // therefore, continue with comparing the individual children + } else if !AssertEqualAst(t, itemE.node, itemA.node) { + return false + } + } + + return true +} diff --git a/util/json/json.go b/util/json/json.go index 13596bdf..02d2d2ca 100644 --- a/util/json/json.go +++ b/util/json/json.go @@ -29,7 +29,7 @@ func UnmarshalAndBuildDocument[T core.AstNode](sc *service.Container, document * if err := json.Unmarshal(data, rootNode); err != nil { return err } - core.AssignContainers(document, rootNode) + core.AssignContainers(document) document.Root = rootNode document.State = core.DocStateParsed documents.Set(document) From c7832b925ff9b6611985a6e26d5cd1ce0925ae76 Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Mon, 20 Jul 2026 14:31:55 +0200 Subject: [PATCH 16/16] some consolidation suggested in review --- internal/generator/json_generator.go | 14 +++++--------- internal/generator/type_generator.go | 7 ++++++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/internal/generator/json_generator.go b/internal/generator/json_generator.go index 41593b2f..a8253c8f 100644 --- a/internal/generator/json_generator.go +++ b/internal/generator/json_generator.go @@ -65,7 +65,7 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { var stringListFields = map[string]string{} for _, field := range fields { if field.Array && (field.GType == TOKEN_TYPE || field.GType == COMPOSITE_TYPE) { - varName := strings.ToLower(field.Name[:1]) + field.Name[1:] + varName := field.PName stringListFields[field.Name] = varName n.AppendLine(varName, " := make([]string, len(i.", field.Name, "()))") @@ -80,14 +80,11 @@ func generateJSONMarshal(node codegen.Node, iface grammar.Interface) { n.Indent(func(n2 codegen.Node) { n2.AppendLine("T__", " ", "string", " `json:\"$type\"`") for _, field := range fields { - jsonTag := strings.ToLower(field.Name[:1]) + field.Name[1:] - var typeStr string + typeStr := field.Type if field.Array { - typeStr = "[]" + field.Type - } else { - typeStr = field.Type + typeStr = "[]" + typeStr } - n2.AppendLine(field.Name, " ", typeStr, " `json:\"", jsonTag, ",omitempty\"`") + n2.AppendLine(field.Name, " ", typeStr, " `json:\"", field.JsonPropName, ",omitempty\"`") } }) n.AppendLine("}{") @@ -147,8 +144,7 @@ func generateJSONUnmarshal(node codegen.Node, iface grammar.Interface) { n.Indent(func(n2 codegen.Node) { n2.AppendLine("T__", " ", "string", " `json:\"$type\"`") for _, field := range fields { - jsonTag := strings.ToLower(field.Name[:1]) + field.Name[1:] - n2.AppendLine(field.Name, " ", getAuxFieldType(field), " `json:\"", jsonTag, "\"`") + n2.AppendLine(field.Name, " ", getAuxFieldType(field), " `json:\"", field.JsonPropName, "\"`") } }) n.AppendLine("}{}") diff --git a/internal/generator/type_generator.go b/internal/generator/type_generator.go index bb766039..64ad2e98 100644 --- a/internal/generator/type_generator.go +++ b/internal/generator/type_generator.go @@ -72,7 +72,10 @@ var reservedKeywords = map[string]bool{ type FieldInfo struct { Name string + // variant of Name with first char in lower case + JsonPropName string // Private name, used to avoid conflicts with reserved keywords + // mostly equal to 'JsonTagName' except for reserved keywords PName string Array bool @@ -88,7 +91,8 @@ type FieldInfo struct { func getFieldInfo(field grammar.Field) FieldInfo { name := field.Name() - pname := strings.ToLower(name[0:1]) + name[1:] + jsonPropName := strings.ToLower(name[0:1]) + name[1:] + pname := jsonPropName if reservedKeywords[pname] { pname = "_" + name } @@ -114,6 +118,7 @@ func getFieldInfo(field grammar.Field) FieldInfo { return FieldInfo{ Name: name, PName: pname, + JsonPropName: jsonPropName, Array: array, Reference: ref, Type: typ,