// Package compile lowers a bound expression into a CExpr whose leaves // carry resolved row slots and typed Snapshot column readers, so per-row // evaluation does no string hashing (the var->slot and key->column lookups // happen once at stage compile). Evaluation mirrors the interpreter // exactly; nodes not worth specializing fall back to it. Port of the Rust // compile.rs -- the one package besides the graph seam that touches the // engine directly.
package compile import ( chickpeas ) // typedColKind discriminates a typedCol. type typedColKind uint8 const ( colI64 typedColKind = iota colF64 colBool colStr colKeyed ) // typedCol is a column reader resolved once (the key->column lookup // hoisted out of the row loop). A missing column falls back to the keyed // per-row read. type typedCol struct { kind typedColKind i64 chickpeas.I64Col f64 chickpeas.F64Col bool chickpeas.BoolCol str chickpeas.StrCol } // newTypedCol narrows a resolved column by dtype. func newTypedCol( chickpeas.Col, bool) typedCol { if ! { return typedCol{kind: colKeyed} } switch .Dtype() { case chickpeas.DtypeI64: return typedCol{kind: colI64, i64: .I64()} case chickpeas.DtypeF64: return typedCol{kind: colF64, f64: .F64()} case chickpeas.DtypeBool: return typedCol{kind: colBool, bool: .Bool()} default: return typedCol{kind: colStr, str: .Str()} } } // propReader is a resolved reader for a property key on whichever a slot // holds -- a node or a relationship -- both typed columns hoisted at // compile time. type propReader struct { g *chickpeas.Snapshot key string node typedCol rel typedCol } // newPropReader hoists the node and rel columns for key once. func newPropReader( *chickpeas.Snapshot, string) propReader { , := .ColIndexed() , := .RelColIndexed() return propReader{g: , key: , node: newTypedCol(, ), rel: newTypedCol(, )} } // readNode reads the node column at id; absent (including the engine's // empty-string-means-missing convention) is Null, identical to the seam's // NodeProp. func ( *propReader) ( chickpeas.NodeID) value.Value { return .read(&.node, uint32(), true) } // readRel reads the rel column at a CSR position. func ( *propReader) ( uint32) value.Value { return .read(&.rel, , false) } // read reads one typed column position, folding absents to Null. func ( *propReader) ( *typedCol, uint32, bool) value.Value { switch .kind { case colI64: if , := .i64.Get(); { return value.Int() } case colF64: if , := .f64.Get(); { return value.Float() } case colBool: if , := .bool.Get(); { return value.Bool() } case colStr: // Atom 0 in a dense column means missing (the raw reader does not // fold it); an empty or unresolvable text is also absent, matching // the engine's Prop convention. if , := .str.ID(); && != 0 { if , := .g.ResolveString(); && != "" { return value.Str() } } default: // No resolved column: the keyed per-row fallback. var chickpeas.Prop if { = .g.Prop(chickpeas.NodeID(), .key) } else { = .g.RelProp(, .key) } return propValue() } return value.Null() } // propValue converts an engine property read to a runtime value (the same // folding as the graph seam's NodeProp). func propValue( chickpeas.Prop) value.Value { , := .Value() if ! { return value.Value{} } switch .Kind() { case chickpeas.KindI64: , := .I64() return value.Int() case chickpeas.KindF64: , := .F64() return value.Float() case chickpeas.KindBool: , := .Bool() return value.Bool() default: if , := .Str(); { return value.Str() } return value.Value{} } }