libertaria-stack/l1-identity/qvl/gql/ast.zig

315 lines
7.7 KiB
Zig

//! GQL (Graph Query Language) Parser
//!
//! ISO/IEC 39075:2024 compliant parser for Libertaria QVL.
//! Transpiles GQL queries to Zig programmatic API calls.
const std = @import("std");
// ============================================================================
// AST TYPES
// ============================================================================
/// Root node of a GQL query
pub const Query = struct {
allocator: std.mem.Allocator,
statements: []Statement,
pub fn deinit(self: *Query) void {
for (self.statements) |*stmt| {
stmt.deinit();
}
self.allocator.free(self.statements);
}
};
/// Statement types (GQL is statement-based)
pub const Statement = union(enum) {
match: MatchStatement,
create: CreateStatement,
delete: DeleteStatement,
return_stmt: ReturnStatement,
pub fn deinit(self: *Statement) void {
switch (self.*) {
inline else => |*s| s.deinit(),
}
}
};
/// MATCH statement: pattern matching for graph traversal
pub const MatchStatement = struct {
allocator: std.mem.Allocator,
pattern: GraphPattern,
where: ?Expression,
pub fn deinit(self: *MatchStatement) void {
self.pattern.deinit();
if (self.where) |*w| w.deinit();
}
};
/// CREATE statement: insert nodes/edges
pub const CreateStatement = struct {
allocator: std.mem.Allocator,
pattern: GraphPattern,
pub fn deinit(self: *CreateStatement) void {
self.pattern.deinit();
}
};
/// DELETE statement: remove nodes/edges
pub const DeleteStatement = struct {
allocator: std.mem.Allocator,
targets: []Identifier,
pub fn deinit(self: *DeleteStatement) void {
for (self.targets) |*t| t.deinit();
self.allocator.free(self.targets);
}
};
/// RETURN statement: projection of results
pub const ReturnStatement = struct {
allocator: std.mem.Allocator,
items: []ReturnItem,
pub fn deinit(self: *ReturnStatement) void {
for (self.items) |*item| item.deinit();
self.allocator.free(self.items);
}
};
/// Graph pattern: sequence of path patterns
pub const GraphPattern = struct {
allocator: std.mem.Allocator,
paths: []PathPattern,
pub fn deinit(self: *GraphPattern) void {
for (self.paths) |*p| p.deinit();
self.allocator.free(self.paths);
}
};
/// Path pattern: node -edge-> node -edge-> ...
pub const PathPattern = struct {
allocator: std.mem.Allocator,
elements: []PathElement, // Alternating Node and Edge
pub fn deinit(self: *PathPattern) void {
for (self.elements) |*e| e.deinit();
self.allocator.free(self.elements);
}
};
/// Element in a path (node or edge)
pub const PathElement = union(enum) {
node: NodePattern,
edge: EdgePattern,
pub fn deinit(self: *PathElement) void {
switch (self.*) {
inline else => |*e| e.deinit(),
}
}
};
/// Node pattern: (n:Label {props})
pub const NodePattern = struct {
allocator: std.mem.Allocator,
variable: ?Identifier,
labels: []Identifier,
properties: ?PropertyMap,
pub fn deinit(self: *NodePattern) void {
if (self.variable) |*v| v.deinit();
for (self.labels) |*l| l.deinit();
self.allocator.free(self.labels);
if (self.properties) |*p| p.deinit();
}
};
/// Edge pattern: -[r:TYPE {props}]-> or <-[...]-
pub const EdgePattern = struct {
allocator: std.mem.Allocator,
direction: EdgeDirection,
variable: ?Identifier,
types: []Identifier,
properties: ?PropertyMap,
quantifier: ?Quantifier, // *1..3 for variable length
pub fn deinit(self: *EdgePattern) void {
if (self.variable) |*v| v.deinit();
for (self.types) |*t| t.deinit();
self.allocator.free(self.types);
if (self.properties) |*p| p.deinit();
if (self.quantifier) |*q| q.deinit();
}
};
pub const EdgeDirection = enum {
outgoing, // -
incoming, // <-
any, // -
};
/// Quantifier for variable-length paths: *min..max
pub const Quantifier = struct {
min: ?u32,
max: ?u32, // null = unlimited
pub fn deinit(self: *Quantifier) void {
_ = self;
}
};
/// Property map: {key: value, ...}
pub const PropertyMap = struct {
allocator: std.mem.Allocator,
entries: []PropertyEntry,
pub fn deinit(self: *PropertyMap) void {
for (self.entries) |*e| e.deinit();
self.allocator.free(self.entries);
}
};
pub const PropertyEntry = struct {
key: Identifier,
value: Expression,
pub fn deinit(self: *PropertyEntry) void {
self.key.deinit();
self.value.deinit();
}
};
/// Return item: expression [AS alias]
pub const ReturnItem = struct {
expression: Expression,
alias: ?Identifier,
pub fn deinit(self: *ReturnItem) void {
self.expression.deinit();
if (self.alias) |*a| a.deinit();
}
};
// ============================================================================
// EXPRESSIONS
// ============================================================================
pub const Expression = union(enum) {
literal: Literal,
identifier: Identifier,
property_access: PropertyAccess,
binary_op: BinaryOp,
comparison: Comparison,
function_call: FunctionCall,
list: ListExpression,
pub fn deinit(self: *Expression) void {
switch (self.*) {
inline else => |*e| e.deinit(),
}
}
};
pub const Literal = union(enum) {
string: []const u8,
integer: i64,
float: f64,
boolean: bool,
null: void,
pub fn deinit(self: *Literal) void {
// Strings are slices into source - no cleanup needed
_ = self;
}
};
/// Identifier (variable, label, property name)
pub const Identifier = struct {
name: []const u8,
pub fn deinit(self: *Identifier) void {
// No allocator needed - name is a slice into source
_ = self;
}
};
/// Property access: node.property or edge.property
pub const PropertyAccess = struct {
object: Identifier,
property: Identifier,
pub fn deinit(self: *PropertyAccess) void {
self.object.deinit();
self.property.deinit();
}
};
/// Binary operation: a + b, a - b, etc.
pub const BinaryOp = struct {
left: *Expression,
op: BinaryOperator,
right: *Expression,
pub fn deinit(self: *BinaryOp) void {
self.left.deinit();
// Note: Can't free self.left/right without allocator
// Memory managed by arena or leaked for now
}
};
pub const BinaryOperator = enum {
add, sub, mul, div, mod,
and_op, or_op,
};
/// Comparison: a = b, a < b, etc.
pub const Comparison = struct {
left: *Expression,
op: ComparisonOperator,
right: *Expression,
pub fn deinit(self: *Comparison) void {
self.left.deinit();
self.right.deinit();
// Note: Can't free self.left/right without allocator
}
};
pub const ComparisonOperator = enum {
eq, // =
neq, // <>
lt, // <
lte, // <=
gt, // >
gte, // >=
};
/// Function call: function(arg1, arg2, ...)
pub const FunctionCall = struct {
allocator: std.mem.Allocator,
name: Identifier,
args: []Expression,
pub fn deinit(self: *FunctionCall) void {
self.name.deinit();
for (self.args) |*a| a.deinit();
self.allocator.free(self.args);
}
};
/// List literal: [1, 2, 3]
pub const ListExpression = struct {
allocator: std.mem.Allocator,
elements: []Expression,
pub fn deinit(self: *ListExpression) void {
for (self.elements) |*e| e.deinit();
self.allocator.free(self.elements);
}
};