// propositions.el - the READ primitive over the engram's OWN memories, native el. // // Free memory text -> structured PROPOSITIONS (triples): // (subject, predicate, object, modifiers, polarity, tense, source, confidence) // // This is comprehension turned inward: the Python reference (propositions.py) ran // spaCy's dependency parser over each memory sentence and walked the arcs. Here // the spaCy role is filled by the el-native parser (comprehend.el / parse_spec): // each sentence is parsed to a meaning-spec, and the spec's roles ARE the triple. // Nothing generates text. NEGATION IS SACRED: polarity flows straight from the // spec's polarity field and is never dropped or inverted. // // Depends on: comprehend (parse_spec / parse_spec_lang), grammar (slots_get). // ── sentence segmentation ───────────────────────────────────────────────────── // Split on sentence-final punctuation (. ! ?) and hard newlines. Markdown/long // memories are handled shallowly (the reference caps + ranks by query overlap; // that ranking belongs to the dialogue layer, not here). fn prop_is_boundary(c: String) -> Bool { if str_eq(c, ".") { return true } if str_eq(c, "!") { return true } if str_eq(c, "?") { return true } if str_eq(c, "\n") { return true } return false } fn prop_split_sentences(text: String) -> [String] { let out: [String] = native_list_empty() let n: Int = str_len(text) let start: Int = 0 let i: Int = 0 while i < n { let c: String = str_slice(text, i, i + 1) if prop_is_boundary(c) { let seg: String = str_slice(text, start, i + 1) let trimmed: String = cp_trim_punct(seg) if !str_eq(trimmed, "") { let out = native_list_append(out, seg) } let start = i + 1 } let i = i + 1 } if start < n { let seg: String = str_slice(text, start, n) let trimmed: String = cp_trim_punct(seg) if !str_eq(trimmed, "") { let out = native_list_append(out, seg) } } return out } // ── spec -> proposition record ──────────────────────────────────────────────── // A proposition is a slot map (same [String] shape as the spec) with the READ // contract keys. Modifiers fold the spec's location + iobj adjuncts. fn prop_confidence(subject: String, predicate: String, object: String) -> String { if str_eq(predicate, "") { return "0.0" } if str_eq(subject, "") { return "0.4" } if str_eq(object, "") { return "0.7" } return "1.0" } fn prop_modifiers(spec: [String]) -> String { let loc: String = slots_get(spec, "location") let iobj: String = slots_get(spec, "iobj") let parts: [String] = native_list_empty() if !str_eq(loc, "") { let parts = native_list_append(parts, loc) } if !str_eq(iobj, "") { let parts = native_list_append(parts, "to " + iobj) } return str_join(parts, "; ") } fn prop_from_spec(spec: [String], source_id: String) -> [String] { let subject: String = slots_get(spec, "agent") let predicate: String = slots_get(spec, "predicate") let object: String = slots_get(spec, "patient") let polarity: String = slots_get(spec, "polarity") let tense: String = slots_get(spec, "tense") let mods: String = prop_modifiers(spec) let conf: String = prop_confidence(subject, predicate, object) let p: [String] = native_list_empty() let p = native_list_append(p, "subject"); let p = native_list_append(p, subject) let p = native_list_append(p, "predicate"); let p = native_list_append(p, predicate) let p = native_list_append(p, "object"); let p = native_list_append(p, object) let p = native_list_append(p, "modifiers"); let p = native_list_append(p, mods) let p = native_list_append(p, "polarity"); let p = native_list_append(p, polarity) let p = native_list_append(p, "tense"); let p = native_list_append(p, tense) let p = native_list_append(p, "source"); let p = native_list_append(p, source_id) let p = native_list_append(p, "confidence"); let p = native_list_append(p, conf) return p } // Extract one proposition from a single sentence (given language). fn prop_extract_one_lang(sentence: String, lang: String, source_id: String) -> [String] { let spec: [String] = parse_spec_lang(sentence, lang) return prop_from_spec(spec, source_id) } fn prop_extract_one(sentence: String, source_id: String) -> [String] { return prop_extract_one_lang(sentence, "en", source_id) } // Render a proposition as a compact trace line (repr parity with propositions.py). fn prop_repr(p: [String]) -> String { let neg: String = "" if str_eq(slots_get(p, "polarity"), "neg") { let neg = "NOT " } let mods: String = slots_get(p, "modifiers") let modstr: String = "" if !str_eq(mods, "") { let modstr = " [" + mods + "]" } let s: String = "(" + slots_get(p, "subject") + " -" + neg + slots_get(p, "predicate") let s = s + "-> " + slots_get(p, "object") + modstr let s = s + " conf=" + slots_get(p, "confidence") + ")" return s } // Extract all propositions from a memory's text (one per sentence). Returns a // flat [String] whose entries are the prop_repr trace lines, in reading order. fn prop_extract_lang(text: String, lang: String, source_id: String) -> [String] { let sents: [String] = prop_split_sentences(text) let m: Int = native_list_len(sents) let out: [String] = native_list_empty() let i: Int = 0 while i < m { let sent: String = native_list_get(sents, i) let p: [String] = prop_extract_one_lang(sent, lang, source_id) // drop empty parses (no predicate recovered): honest partial, not noise. if !str_eq(slots_get(p, "predicate"), "") { let out = native_list_append(out, prop_repr(p)) } let i = i + 1 } return out } fn prop_extract(text: String, source_id: String) -> [String] { return prop_extract_lang(text, "en", source_id) }