Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
parser.go8.74 kB
// Package cypher provides Cypher query parsing and execution for NornicDB. package cypher import ( "context" "fmt" "strings" ) // QueryType represents the type of Cypher query. type QueryType int const ( QueryMatch QueryType = iota QueryCreate QueryMerge QueryDelete QuerySet QueryReturn QueryWith ) // Parser parses Cypher queries into AST. type Parser struct{} // NewParser creates a new Cypher parser. func NewParser() *Parser { return &Parser{} } // Query represents a parsed Cypher query. type Query struct { Type QueryType Clauses []Clause Parameters map[string]any } // Clause represents a query clause. type Clause interface { clauseMarker() } // MatchClause represents a MATCH clause. type MatchClause struct { Pattern Pattern Optional bool Where *WhereClause } func (c *MatchClause) clauseMarker() {} // CreateClause represents a CREATE clause. type CreateClause struct { Pattern Pattern } func (c *CreateClause) clauseMarker() {} // ReturnClause represents a RETURN clause. type ReturnClause struct { Items []ReturnItem OrderBy []OrderItem Skip *int Limit *int } func (c *ReturnClause) clauseMarker() {} // WhereClause represents a WHERE clause. type WhereClause struct { Expression Expression } func (c *WhereClause) clauseMarker() {} // SetClause represents a SET clause. type SetClause struct { Items []SetItem } func (c *SetClause) clauseMarker() {} // DeleteClause represents a DELETE clause. type DeleteClause struct { Variables []string Detach bool } func (c *DeleteClause) clauseMarker() {} // Pattern represents a graph pattern. type Pattern struct { Nodes []NodePattern Edges []EdgePattern } // NodePattern represents a node in a pattern. type NodePattern struct { Variable string Labels []string Properties map[string]any } // EdgePattern represents an edge in a pattern. type EdgePattern struct { Variable string Type string Direction EdgeDirection Properties map[string]any MinHops *int MaxHops *int } // EdgeDirection represents edge direction. type EdgeDirection int const ( EdgeBoth EdgeDirection = iota EdgeOutgoing EdgeIncoming ) // Expression represents a Cypher expression. type Expression interface { exprMarker() } // PropertyAccess represents property access (e.g., n.name). type PropertyAccess struct { Variable string Property string } func (e *PropertyAccess) exprMarker() {} // Comparison represents a comparison expression. type Comparison struct { Left Expression Operator string Right Expression } func (e *Comparison) exprMarker() {} // Literal represents a literal value. type Literal struct { Value any } func (e *Literal) exprMarker() {} // Parameter represents a query parameter ($name). type Parameter struct { Name string } func (e *Parameter) exprMarker() {} // FunctionCall represents a function call. type FunctionCall struct { Name string Args []Expression } func (e *FunctionCall) exprMarker() {} // ReturnItem represents an item in a RETURN clause. type ReturnItem struct { Expression Expression Alias string } // OrderItem represents an ORDER BY item. type OrderItem struct { Expression Expression Descending bool } // SetItem represents a SET operation. type SetItem struct { Variable string Property string Value Expression } // Parse parses a Cypher query string into a Query AST. func (p *Parser) Parse(cypher string) (*Query, error) { // Normalize whitespace cypher = strings.TrimSpace(cypher) query := &Query{ Clauses: make([]Clause, 0), Parameters: make(map[string]any), } // Tokenize and parse tokens := tokenize(cypher) if len(tokens) == 0 { return nil, fmt.Errorf("empty query") } // Simple top-down parser pos := 0 for pos < len(tokens) { token := strings.ToUpper(tokens[pos]) switch token { case "MATCH": clause, newPos, err := p.parseMatch(tokens, pos) if err != nil { return nil, err } query.Clauses = append(query.Clauses, clause) pos = newPos query.Type = QueryMatch case "OPTIONAL": if pos+1 < len(tokens) && strings.EqualFold(tokens[pos+1], "MATCH") { clause, newPos, err := p.parseMatch(tokens, pos+1) if err != nil { return nil, err } clause.Optional = true query.Clauses = append(query.Clauses, clause) pos = newPos } case "CREATE": clause, newPos, err := p.parseCreate(tokens, pos) if err != nil { return nil, err } query.Clauses = append(query.Clauses, clause) pos = newPos query.Type = QueryCreate case "RETURN": clause, newPos, err := p.parseReturn(tokens, pos) if err != nil { return nil, err } query.Clauses = append(query.Clauses, clause) pos = newPos case "WHERE": clause, newPos, err := p.parseWhere(tokens, pos) if err != nil { return nil, err } query.Clauses = append(query.Clauses, clause) pos = newPos case "SET": clause, newPos, err := p.parseSet(tokens, pos) if err != nil { return nil, err } query.Clauses = append(query.Clauses, clause) pos = newPos query.Type = QuerySet case "DELETE", "DETACH": clause, newPos, err := p.parseDelete(tokens, pos) if err != nil { return nil, err } query.Clauses = append(query.Clauses, clause) pos = newPos query.Type = QueryDelete default: pos++ } } return query, nil } // parseMatch parses a MATCH clause. func (p *Parser) parseMatch(tokens []string, pos int) (*MatchClause, int, error) { // Skip MATCH keyword pos++ clause := &MatchClause{} // Parse pattern (simplified) // TODO: Full pattern parsing return clause, pos, nil } // parseCreate parses a CREATE clause. func (p *Parser) parseCreate(tokens []string, pos int) (*CreateClause, int, error) { pos++ clause := &CreateClause{} return clause, pos, nil } // parseReturn parses a RETURN clause. func (p *Parser) parseReturn(tokens []string, pos int) (*ReturnClause, int, error) { pos++ clause := &ReturnClause{ Items: make([]ReturnItem, 0), } return clause, pos, nil } // parseWhere parses a WHERE clause. func (p *Parser) parseWhere(tokens []string, pos int) (*WhereClause, int, error) { pos++ clause := &WhereClause{} return clause, pos, nil } // parseSet parses a SET clause. func (p *Parser) parseSet(tokens []string, pos int) (*SetClause, int, error) { pos++ clause := &SetClause{ Items: make([]SetItem, 0), } return clause, pos, nil } // parseDelete parses a DELETE clause. func (p *Parser) parseDelete(tokens []string, pos int) (*DeleteClause, int, error) { clause := &DeleteClause{} if strings.EqualFold(tokens[pos], "DETACH") { clause.Detach = true pos++ } pos++ // Skip DELETE return clause, pos, nil } // tokenize splits Cypher into tokens. func tokenize(cypher string) []string { // Simple tokenizer - will need improvement tokens := make([]string, 0) current := strings.Builder{} inString := false stringChar := byte(0) for i := 0; i < len(cypher); i++ { c := cypher[i] if inString { current.WriteByte(c) if c == stringChar { inString = false tokens = append(tokens, current.String()) current.Reset() } continue } switch c { case '"', '\'': inString = true stringChar = c current.WriteByte(c) case ' ', '\t', '\n', '\r': if current.Len() > 0 { tokens = append(tokens, current.String()) current.Reset() } case '(', ')', '[', ']', '{', '}', ':', ',', '.', '=', '<', '>', '-', '+', '*', '/': if current.Len() > 0 { tokens = append(tokens, current.String()) current.Reset() } tokens = append(tokens, string(c)) default: current.WriteByte(c) } } if current.Len() > 0 { tokens = append(tokens, current.String()) } return tokens } // Executor executes Cypher queries. type Executor struct { parser *Parser // storage reference will be added } // NewExecutor creates a new Cypher executor. func NewExecutor() *Executor { return &Executor{ parser: NewParser(), } } // Execute executes a Cypher query. func (e *Executor) Execute(ctx context.Context, cypher string, params map[string]any) (*Result, error) { // Parse query query, err := e.parser.Parse(cypher) if err != nil { return nil, fmt.Errorf("parse error: %w", err) } // Add parameters for k, v := range params { query.Parameters[k] = v } // Execute based on query type result := &Result{ Columns: make([]string, 0), Rows: make([]map[string]any, 0), } // TODO: Implement execution _ = query return result, nil } // Result holds query results. type Result struct { Columns []string Rows []map[string]any } // RowCount returns the number of rows. func (r *Result) RowCount() int { return len(r.Rows) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server