Test new approach with AST (AI assisted)
diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go
new file mode 100644
index 0000000..be11da9
--- /dev/null
+++ b/pkg/parser/parser.go
@@ -0,0 +1,134 @@
+package parser
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/KorAP/KoralPipe-TermMapper2/pkg/ast"
+)
+
+// rawNode represents the raw JSON structure
+type rawNode struct {
+ Type string `json:"@type"`
+ Wrap json.RawMessage `json:"wrap,omitempty"`
+ Operands []rawNode `json:"operands,omitempty"`
+ Relation string `json:"relation,omitempty"`
+ Foundry string `json:"foundry,omitempty"`
+ Key string `json:"key,omitempty"`
+ Layer string `json:"layer,omitempty"`
+ Match string `json:"match,omitempty"`
+ Value string `json:"value,omitempty"`
+}
+
+// ParseJSON parses a JSON string into our AST representation
+func ParseJSON(data []byte) (ast.Node, error) {
+ var raw rawNode
+ if err := json.Unmarshal(data, &raw); err != nil {
+ return nil, fmt.Errorf("failed to parse JSON: %w", err)
+ }
+ return parseNode(raw)
+}
+
+// parseNode converts a raw node into an AST node
+func parseNode(raw rawNode) (ast.Node, error) {
+ switch raw.Type {
+ case "koral:token":
+ if raw.Wrap == nil {
+ return nil, fmt.Errorf("token node missing wrap field")
+ }
+ var wrapRaw rawNode
+ if err := json.Unmarshal(raw.Wrap, &wrapRaw); err != nil {
+ return nil, fmt.Errorf("failed to parse wrap: %w", err)
+ }
+ wrap, err := parseNode(wrapRaw)
+ if err != nil {
+ return nil, err
+ }
+ return &ast.Token{Wrap: wrap}, nil
+
+ case "koral:termGroup":
+ operands := make([]ast.Node, len(raw.Operands))
+ for i, op := range raw.Operands {
+ node, err := parseNode(op)
+ if err != nil {
+ return nil, err
+ }
+ operands[i] = node
+ }
+
+ relation := ast.AndRelation
+ if strings.HasSuffix(raw.Relation, "or") {
+ relation = ast.OrRelation
+ }
+
+ return &ast.TermGroup{
+ Operands: operands,
+ Relation: relation,
+ }, nil
+
+ case "koral:term":
+ match := ast.MatchEqual
+ if strings.HasSuffix(raw.Match, "ne") {
+ match = ast.MatchNotEqual
+ }
+
+ return &ast.Term{
+ Foundry: raw.Foundry,
+ Key: raw.Key,
+ Layer: raw.Layer,
+ Match: match,
+ Value: raw.Value,
+ }, nil
+
+ default:
+ return nil, fmt.Errorf("unknown node type: %s", raw.Type)
+ }
+}
+
+// SerializeToJSON converts an AST node back to JSON
+func SerializeToJSON(node ast.Node) ([]byte, error) {
+ raw := nodeToRaw(node)
+ return json.MarshalIndent(raw, "", " ")
+}
+
+// nodeToRaw converts an AST node to a raw node for JSON serialization
+func nodeToRaw(node ast.Node) rawNode {
+ switch n := node.(type) {
+ case *ast.Token:
+ return rawNode{
+ Type: "koral:token",
+ Wrap: json.RawMessage(nodeToRaw(n.Wrap).toJSON()),
+ }
+
+ case *ast.TermGroup:
+ operands := make([]rawNode, len(n.Operands))
+ for i, op := range n.Operands {
+ operands[i] = nodeToRaw(op)
+ }
+ return rawNode{
+ Type: "koral:termGroup",
+ Operands: operands,
+ Relation: "relation:" + string(n.Relation),
+ }
+
+ case *ast.Term:
+ return rawNode{
+ Type: "koral:term",
+ Foundry: n.Foundry,
+ Key: n.Key,
+ Layer: n.Layer,
+ Match: "match:" + string(n.Match),
+ Value: n.Value,
+ }
+
+ default:
+ return rawNode{}
+ }
+}
+
+// toJSON converts a raw node to JSON bytes
+func (r rawNode) toJSON() []byte {
+ data, _ := json.Marshal(r)
+ return data
+}