Merge PR #3: feat: port remaining foundation El source into monorepo

This commit is contained in:
Will Anderson
2026-05-06 14:35:49 -05:00
48 changed files with 10588 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
// arbor-cli the `arbor` command-line tool.
// Inlines its own copies of the parse / layout / render pipeline so that the
// resulting binary is self-contained. (El's `import` form today concatenates
// source; once a real module loader lands this becomes a thin driver.)
vessel "arbor-cli" {
version "0.1.0"
description "Command-line interface for the Arbor diagram language"
authors ["Neuron Technologies"]
edition "2026"
}
dependencies {
arbor-core "0.1"
arbor-parse "0.1"
arbor-layout "0.1"
arbor-render "0.1"
}
build {
entry "src/main.el"
output "dist/"
}
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
// arbor-core fundamental types for Arbor diagrams.
// Node IDs (sanitised), shape vocabulary, edge kinds, and the lightweight
// graph value used by every other vessel.
vessel "arbor-core" {
version "0.1.0"
description "Core types for Arbor diagrams: NodeId, ArborShape, ArborEdgeKind, graphs"
authors ["Neuron Technologies"]
edition "2026"
}
dependencies {
}
build {
entry "src/main.el"
output "dist/"
}
+333
View File
@@ -0,0 +1,333 @@
// arbor-core core types for Arbor diagrams.
//
// Idiomatic El: everything is a Map. Functions take/return maps; helpers are
// pure and small. The downstream vessels (parse, layout, render) consume the
// shapes defined here.
//
// Shape vocabulary:
// ArborShape strings "rect" "rounded" "cylinder" "diamond" "stadium" "primary"
//
// Edge-kind strings:
// "solid" "dashed" "forbidden" "bidirectional"
//
// Node value: { "id":Str, "label":Str, "shape":Str }
// Edge value: { "from":Str, "to":Str, "label":Str, "kind":Str }
// Group value: { "id":Str, "label":Str, "node_ids":[Str], "direction":Str }
// Graph value: { "title":Str, "direction":Str, "nodes":[Node], "edges":[Edge], "groups":[Group] }
//
// Diagram-form (lowered) is the same shape but with NodeStyle/EdgeLine/Arrow
// resolved into renderer-friendly fields:
// Node: + "sublabel":Str, "style_fill":Str, "style_stroke":Str, "style_color":Str
// Edge: + "line":Str ("solid"/"dashed"/"dotted"/"thick"), "arrow":Str ("forward"/"backward"/"both"/"none")
//
// This file is the canonical definition of those shapes. Other vessels rely on
// these field names.
// NodeId sanitisation
//
// Sanitise an arbitrary string into a Mermaid-safe identifier.
// - any char not in [a-zA-Z0-9_] becomes '_'
// - consecutive underscores collapse
// - trailing underscores stripped
// - if first char is a digit, prepend 'n'
// - if empty, return "node"
fn is_alnum_underscore(ch: String) -> Bool {
let code: Int = str_char_code(ch, 0)
if code >= 48 {
if code <= 57 { return true }
}
if code >= 65 {
if code <= 90 { return true }
}
if code >= 97 {
if code <= 122 { return true }
}
if code == 95 { return true }
false
}
fn is_ascii_digit(ch: String) -> Bool {
let code: Int = str_char_code(ch, 0)
if code >= 48 {
if code <= 57 { return true }
}
false
}
fn sanitize_id(s: String) -> String {
let n: Int = str_len(s)
if n == 0 { return "node" }
// Pass 1: replace and collapse.
let out = ""
let prev_underscore = false
let i = 0
while i < n {
let ch: String = str_char_at(s, i)
if is_alnum_underscore(ch) {
let out = out + ch
let prev_underscore = false
} else {
if !prev_underscore {
let out = out + "_"
}
let prev_underscore = true
}
let i = i + 1
}
// Pass 2: strip trailing underscores.
let m: Int = str_len(out)
let end = m
let stripping = true
while stripping {
if end <= 0 {
let stripping = false
} else {
let last: String = str_char_at(out, end - 1)
if last == "_" {
let end = end - 1
} else {
let stripping = false
}
}
}
let out = str_slice(out, 0, end)
if str_len(out) == 0 { return "node" }
// Pass 3: leading-digit guard.
let first: String = str_char_at(out, 0)
if is_ascii_digit(first) {
let out = "n" + out
}
out
}
// Constructors
fn make_node(id: String, label: String, shape: String) -> Map<String, Any> {
{ "id": id, "label": label, "shape": shape }
}
fn make_edge(src: String, dst: String, kind: String) -> Map<String, Any> {
{ "from": src, "to": dst, "label": "", "kind": kind }
}
fn make_edge_with_label(src: String, dst: String, kind: String, label: String) -> Map<String, Any> {
{ "from": src, "to": dst, "label": label, "kind": kind }
}
fn make_group(id: String, label: String) -> Map<String, Any> {
let empty_ids: [String] = el_list_empty()
{ "id": id, "label": label, "node_ids": empty_ids, "direction": "" }
}
fn make_graph() -> Map<String, Any> {
let empty_n: [Map<String, Any>] = el_list_empty()
let empty_e: [Map<String, Any>] = el_list_empty()
let empty_g: [Map<String, Any>] = el_list_empty()
{ "title": "", "direction": "top-down",
"nodes": empty_n, "edges": empty_e, "groups": empty_g }
}
// Shape vocabulary
// Returns the canonical shape string for a token, or "" if unknown.
fn shape_from_token(tok: String) -> String {
let t: String = str_trim(tok)
if t == "rect" { return "rect" }
if t == "rounded" { return "rounded" }
if t == "cylinder" { return "cylinder" }
if t == "diamond" { return "diamond" }
if t == "stadium" { return "stadium" }
if t == "primary" { return "primary" }
""
}
// Lower an Arbor shape into the renderer's NodeShape vocabulary.
fn shape_to_node_shape(shape: String) -> String {
if shape == "rect" { return "rectangle" }
if shape == "primary" { return "rectangle" }
if shape == "rounded" { return "rounded_rect" }
if shape == "cylinder" { return "cylinder" }
if shape == "diamond" { return "diamond" }
if shape == "stadium" { return "stadium" }
"rectangle"
}
// Lowering: ArborGraph DiagramGraph
//
// Replaces every node with a diagram-form node carrying explicit style fields,
// and every edge with a diagram-form edge carrying line/arrow strings.
fn lower_node(n: Map<String, Any>) -> Map<String, Any> {
let shape: String = n["shape"]
let node_shape: String = shape_to_node_shape(shape)
let fill = ""
let stroke = ""
let color = ""
if shape == "primary" {
let fill = "#0052A0"
let stroke = "#0052A0"
let color = "#ffffff"
}
{ "id": n["id"], "label": n["label"], "sublabel": "",
"shape": node_shape,
"style_fill": fill, "style_stroke": stroke, "style_color": color }
}
fn lower_edge(e: Map<String, Any>) -> Map<String, Any> {
let kind: String = e["kind"]
let line = "solid"
let arrow = "forward"
if kind == "dashed" {
let line = "dashed"
}
if kind == "bidirectional" {
let arrow = "both"
}
// forbidden uses solid line + forward arrow; the renderer overlays the
// circle-X marker based on a forbidden-set the caller threads through.
{ "from": e["from"], "to": e["to"], "label": e["label"],
"line": line, "arrow": arrow }
}
fn lower_graph(g: Map<String, Any>) -> Map<String, Any> {
let nodes: [Map<String, Any>] = g["nodes"]
let edges: [Map<String, Any>] = g["edges"]
let lowered_nodes: [Map<String, Any>] = el_list_empty()
let i = 0
let n: Int = el_list_len(nodes)
while i < n {
let lowered_nodes = native_list_append(lowered_nodes, lower_node(get(nodes, i)))
let i = i + 1
}
let lowered_edges: [Map<String, Any>] = el_list_empty()
let i = 0
let m: Int = el_list_len(edges)
while i < m {
let lowered_edges = native_list_append(lowered_edges, lower_edge(get(edges, i)))
let i = i + 1
}
{ "title": g["title"], "direction": g["direction"],
"nodes": lowered_nodes, "edges": lowered_edges, "groups": g["groups"] }
}
// Find a node by id within a (lowered or raw) graph. Returns an empty map
// when not found callers check map_get(result, "id") for presence.
fn graph_find_node(graph: Map<String, Any>, id: String) -> Map<String, Any> {
let nodes: [Map<String, Any>] = graph["nodes"]
let n: Int = el_list_len(nodes)
let i = 0
while i < n {
let node: Map<String, Any> = get(nodes, i)
let nid: String = node["id"]
if nid == id { return node }
let i = i + 1
}
let empty: Map<String, Any> = el_map_new(0)
empty
}
// Forbidden-edge set helpers
// The lowered graph drops the "forbidden" kind (line/arrow have no slot for
// it). Callers preserve the set as a list of "from->to" strings.
fn forbidden_key(from: String, to: String) -> String {
from + "->" + to
}
fn collect_forbidden(graph: Map<String, Any>) -> [String] {
let edges: [Map<String, Any>] = graph["edges"]
let n: Int = el_list_len(edges)
let out: [String] = el_list_empty()
let i = 0
while i < n {
let e: Map<String, Any> = get(edges, i)
let kind: String = e["kind"]
if kind == "forbidden" {
let f: String = e["from"]
let t: String = e["to"]
let out = native_list_append(out, forbidden_key(f, t))
}
let i = i + 1
}
out
}
fn forbidden_contains(set: [String], src: String, dst: String) -> Bool {
let key: String = forbidden_key(src, dst)
let n: Int = el_list_len(set)
let i = 0
while i < n {
let s: String = get(set, i)
if s == key { return true }
let i = i + 1
}
false
}
// Smoke test
//
// State is kept in process-local k/v storage so we never mix Int + Call or
// Int + Ident in `+` (which the codegen heuristic emits as string concat
// on tagged-pointer values, segfaulting on Int operands).
fn fail(label: String, got: String, want: String) -> Int {
println("FAIL " + label + " got=[" + got + "] want=[" + want + "]")
state_set("failures", "1")
0
}
fn check_eq(label: String, got: String, want: String) -> Int {
if got == want {
println("ok " + label + " = " + got)
return 1
}
fail(label, got, want)
}
check_eq("sanitize crates/nc-core",
sanitize_id("crates/nc-core"), "crates_nc_core")
check_eq("sanitize package.json",
sanitize_id("package.json"), "package_json")
check_eq("sanitize 42-module",
sanitize_id("42-module"), "n42_module")
check_eq("sanitize empty", sanitize_id(""), "node")
check_eq("sanitize !!--@@", sanitize_id("!!--@@"), "node")
check_eq("shape_from_token rounded",
shape_from_token("rounded"), "rounded")
check_eq("shape_to_node_shape primary",
shape_to_node_shape("primary"), "rectangle")
// Lowering preserves a node id and adds style.
let n: Map<String, Any> = make_node("svc", "Service", "primary")
let ln: Map<String, Any> = lower_node(n)
check_eq("lower preserves id", ln["id"], "svc")
check_eq("lower applies primary fill", ln["style_fill"], "#0052A0")
// Edge lowering
let e: Map<String, Any> = make_edge("a", "b", "dashed")
let le: Map<String, Any> = lower_edge(e)
check_eq("lower edge dashed line", le["line"], "dashed")
let e2: Map<String, Any> = make_edge("a", "b", "bidirectional")
let le2: Map<String, Any> = lower_edge(e2)
check_eq("lower edge bidirectional arrow", le2["arrow"], "both")
println("")
let failures: String = state_get("failures")
if str_eq(failures, "1") {
println("arbor-core: FAILED")
exit_program(1)
} else {
println("arbor-core: ok")
}
+19
View File
@@ -0,0 +1,19 @@
// arbor-diagram diagram intermediate representation + Mermaid serializer
// + dependency-graph builders. Consumes raw graph values built by arbor-core
// or arbor-parse and produces Mermaid markup or other serializations.
vessel "arbor-diagram" {
version "0.1.0"
description "Diagram IR + Mermaid serializer + architecture diagram builders"
authors ["Neuron Technologies"]
edition "2026"
}
dependencies {
arbor-core "0.1"
}
build {
entry "src/main.el"
output "dist/"
}
+433
View File
@@ -0,0 +1,433 @@
// arbor-diagram diagram intermediate representation (AST + IR).
//
// Where arbor-core supplies the *.arbor source-language model Mermaid-safe
// IDs, ArborShape strings, ArborEdgeKind strings, and the lowered "diagram-
// form" map arbor-diagram exposes the same lowered model as the canonical
// IR for downstream serializers (arbor-render and any future Mermaid-style
// emitter). The two vessels overlap by design: arbor-core is responsible for
// *naming* the schema; arbor-diagram is responsible for *building* values
// against it.
//
// The Rust crate ships small AST builder structs (`DiagramNode::new`,
// `DiagramEdge::with_label`, `DiagramGraph::add_node`). El has no method
// chaining, no Default::default(), no enum types. The El idiom is a stack
// of immutable maps with explicit constructor + with_* helpers that take
// the value and return a freshly-allocated map.
//
// Public surface:
// make_node(id, label) DiagramNode
// with_shape(node, shape) DiagramNode
// with_sublabel(node, sublabel) DiagramNode
// with_style(node, fill, stroke, color) DiagramNode
//
// make_edge(from, to) DiagramEdge
// with_label(edge, label)
// with_line(edge, line) // "solid"/"dashed"/"dotted"/"thick"
// with_arrow(edge, arrow) // "forward"/"backward"/"both"/"none"
//
// make_group(id, label) DiagramGroup
// with_node(group, node_id)
// with_nodes(group, [node_id])
// with_direction(group, dir)
//
// make_graph(title) DiagramGraph
// with_direction(graph, dir)
// graph_add_node(graph, node) DiagramGraph
// graph_add_edge(graph, edge) DiagramGraph
// graph_add_group(graph, group) DiagramGraph
// graph_node(graph, id) DiagramNode | empty map
//
// Shape vocabulary (lowered): see arbor-core. The local copy here mirrors
// the table in arbor-core/src/main.el so this vessel is hermetic.
// NodeShape vocabulary
fn node_shape_rectangle() -> String { "rectangle" }
fn node_shape_rounded_rect() -> String { "rounded_rect" }
fn node_shape_stadium() -> String { "stadium" }
fn node_shape_cylinder() -> String { "cylinder" }
fn node_shape_diamond() -> String { "diamond" }
fn node_shape_parallelogram() -> String { "parallelogram" }
fn node_shape_database() -> String { "database" }
fn node_shape_subroutine() -> String { "subroutine" }
fn node_shape_valid(s: String) -> Bool {
if str_eq(s, "rectangle") { return true }
if str_eq(s, "rounded_rect") { return true }
if str_eq(s, "stadium") { return true }
if str_eq(s, "cylinder") { return true }
if str_eq(s, "diamond") { return true }
if str_eq(s, "parallelogram") { return true }
if str_eq(s, "database") { return true }
if str_eq(s, "subroutine") { return true }
false
}
// EdgeLine vocabulary
fn edge_line_solid() -> String { "solid" }
fn edge_line_dashed() -> String { "dashed" }
fn edge_line_dotted() -> String { "dotted" }
fn edge_line_thick() -> String { "thick" }
fn edge_line_valid(s: String) -> Bool {
if str_eq(s, "solid") { return true }
if str_eq(s, "dashed") { return true }
if str_eq(s, "dotted") { return true }
if str_eq(s, "thick") { return true }
false
}
// EdgeArrow vocabulary
fn edge_arrow_forward() -> String { "forward" }
fn edge_arrow_backward() -> String { "backward" }
fn edge_arrow_both() -> String { "both" }
fn edge_arrow_none() -> String { "none" }
fn edge_arrow_valid(s: String) -> Bool {
if str_eq(s, "forward") { return true }
if str_eq(s, "backward") { return true }
if str_eq(s, "both") { return true }
if str_eq(s, "none") { return true }
false
}
// Direction vocabulary
fn direction_top_down() -> String { "top-down" }
fn direction_left_right() -> String { "left-right" }
fn direction_right_left() -> String { "right-left" }
fn direction_bottom_up() -> String { "bottom-up" }
fn direction_valid(s: String) -> Bool {
if str_eq(s, "top-down") { return true }
if str_eq(s, "left-right") { return true }
if str_eq(s, "right-left") { return true }
if str_eq(s, "bottom-up") { return true }
false
}
// DiagramNode
fn make_node(id: String, label: String) -> Map<String, Any> {
{
"id": id,
"label": label,
"sublabel": "",
"shape": "rectangle",
"style_fill": "",
"style_stroke": "",
"style_color": ""
}
}
fn with_shape(node: Map<String, Any>, shape: String) -> Map<String, Any> {
{
"id": node["id"],
"label": node["label"],
"sublabel": node["sublabel"],
"shape": shape,
"style_fill": node["style_fill"],
"style_stroke": node["style_stroke"],
"style_color": node["style_color"]
}
}
fn with_sublabel(node: Map<String, Any>, sublabel: String) -> Map<String, Any> {
{
"id": node["id"],
"label": node["label"],
"sublabel": sublabel,
"shape": node["shape"],
"style_fill": node["style_fill"],
"style_stroke": node["style_stroke"],
"style_color": node["style_color"]
}
}
fn with_style(node: Map<String, Any>, fill: String, stroke: String, color: String) -> Map<String, Any> {
{
"id": node["id"],
"label": node["label"],
"sublabel": node["sublabel"],
"shape": node["shape"],
"style_fill": fill,
"style_stroke": stroke,
"style_color": color
}
}
// DiagramEdge
fn make_edge(from: String, to: String) -> Map<String, Any> {
{
"from": from,
"to": to,
"label": "",
"line": "solid",
"arrow": "forward"
}
}
fn with_label(edge: Map<String, Any>, label: String) -> Map<String, Any> {
{
"from": edge["from"],
"to": edge["to"],
"label": label,
"line": edge["line"],
"arrow": edge["arrow"]
}
}
fn with_line(edge: Map<String, Any>, line: String) -> Map<String, Any> {
{
"from": edge["from"],
"to": edge["to"],
"label": edge["label"],
"line": line,
"arrow": edge["arrow"]
}
}
fn with_arrow(edge: Map<String, Any>, arrow: String) -> Map<String, Any> {
{
"from": edge["from"],
"to": edge["to"],
"label": edge["label"],
"line": edge["line"],
"arrow": arrow
}
}
// DiagramGroup
fn make_group(id: String, label: String) -> Map<String, Any> {
let empty: [String] = native_list_empty()
{
"id": id,
"label": label,
"node_ids": empty,
"direction": ""
}
}
fn with_node(group: Map<String, Any>, node_id: String) -> Map<String, Any> {
let cur: [String] = group["node_ids"]
let next: [String] = native_list_append(cur, node_id)
{
"id": group["id"],
"label": group["label"],
"node_ids": next,
"direction": group["direction"]
}
}
fn with_nodes(group: Map<String, Any>, ids: [String]) -> Map<String, Any> {
let cur: [String] = group["node_ids"]
let n: Int = el_list_len(ids)
let i = 0
while i < n {
let cur = native_list_append(cur, get(ids, i))
let i = i + 1
}
{
"id": group["id"],
"label": group["label"],
"node_ids": cur,
"direction": group["direction"]
}
}
fn with_group_direction(group: Map<String, Any>, dir: String) -> Map<String, Any> {
{
"id": group["id"],
"label": group["label"],
"node_ids": group["node_ids"],
"direction": dir
}
}
// DiagramGraph
fn make_graph(title: String) -> Map<String, Any> {
let empty_n: [Map<String, Any>] = native_list_empty()
let empty_e: [Map<String, Any>] = native_list_empty()
let empty_g: [Map<String, Any>] = native_list_empty()
{
"title": title,
"direction": "top-down",
"nodes": empty_n,
"edges": empty_e,
"groups": empty_g
}
}
fn with_direction(graph: Map<String, Any>, dir: String) -> Map<String, Any> {
{
"title": graph["title"],
"direction": dir,
"nodes": graph["nodes"],
"edges": graph["edges"],
"groups": graph["groups"]
}
}
fn graph_add_node(graph: Map<String, Any>, node: Map<String, Any>) -> Map<String, Any> {
let cur: [Map<String, Any>] = graph["nodes"]
let next: [Map<String, Any>] = native_list_append(cur, node)
{
"title": graph["title"],
"direction": graph["direction"],
"nodes": next,
"edges": graph["edges"],
"groups": graph["groups"]
}
}
fn graph_add_edge(graph: Map<String, Any>, edge: Map<String, Any>) -> Map<String, Any> {
let cur: [Map<String, Any>] = graph["edges"]
let next: [Map<String, Any>] = native_list_append(cur, edge)
{
"title": graph["title"],
"direction": graph["direction"],
"nodes": graph["nodes"],
"edges": next,
"groups": graph["groups"]
}
}
fn graph_add_group(graph: Map<String, Any>, group: Map<String, Any>) -> Map<String, Any> {
let cur: [Map<String, Any>] = graph["groups"]
let next: [Map<String, Any>] = native_list_append(cur, group)
{
"title": graph["title"],
"direction": graph["direction"],
"nodes": graph["nodes"],
"edges": graph["edges"],
"groups": next
}
}
// Find a node by id. Returns an empty map (no "id" field) when not present.
fn graph_node(graph: Map<String, Any>, id: String) -> Map<String, Any> {
let nodes: [Map<String, Any>] = graph["nodes"]
let n: Int = el_list_len(nodes)
let i = 0
while i < n {
let nd: Map<String, Any> = get(nodes, i)
let nid: String = nd["id"]
if str_eq(nid, id) { return nd }
let i = i + 1
}
let empty: Map<String, Any> = el_map_new(0)
empty
}
// Smoke test
fn fail(label: String, got: String, want: String) -> Int {
println("FAIL " + label + " got=[" + got + "] want=[" + want + "]")
state_set("smoke_failures", "1")
0
}
fn check_eq(label: String, got: String, want: String) -> Int {
if got == want {
println("ok " + label + " = " + got)
return 1
}
fail(label, got, want)
}
// Vocabulary self-checks
check_eq("shape rectangle valid",
bool_to_str(node_shape_valid("rectangle")), "true")
check_eq("shape hexagon invalid",
bool_to_str(node_shape_valid("hexagon")), "false")
check_eq("line dashed valid",
bool_to_str(edge_line_valid("dashed")), "true")
check_eq("arrow both valid",
bool_to_str(edge_arrow_valid("both")), "true")
check_eq("dir top-down valid",
bool_to_str(direction_valid("top-down")), "true")
// Node builder
let n0: Map<String, Any> = make_node("svc", "Service")
check_eq("node default shape", n0["shape"], "rectangle")
check_eq("node default sublabel empty", n0["sublabel"], "")
let n1: Map<String, Any> = with_shape(n0, "cylinder")
check_eq("node with_shape", n1["shape"], "cylinder")
check_eq("node id preserved", n1["id"], "svc")
let n2: Map<String, Any> = with_sublabel(n1, "v0.1.0")
check_eq("node with_sublabel", n2["sublabel"], "v0.1.0")
let n3: Map<String, Any> = with_style(n2, "#0052A0", "#0052A0", "#ffffff")
check_eq("node style fill", n3["style_fill"], "#0052A0")
check_eq("node style color", n3["style_color"], "#ffffff")
// Edge builder
let e0: Map<String, Any> = make_edge("a", "b")
check_eq("edge default line", e0["line"], "solid")
check_eq("edge default arrow", e0["arrow"], "forward")
let e1: Map<String, Any> = with_line(e0, "dashed")
let e2: Map<String, Any> = with_arrow(e1, "both")
let e3: Map<String, Any> = with_label(e2, "calls")
check_eq("edge line", e3["line"], "dashed")
check_eq("edge arrow", e3["arrow"], "both")
check_eq("edge label", e3["label"], "calls")
// Group builder
let g0: Map<String, Any> = make_group("core", "Application Core")
let g1: Map<String, Any> = with_node(g0, "api")
let g2: Map<String, Any> = with_node(g1, "svc")
let ids2: [String] = g2["node_ids"]
check_eq("group with two nodes", int_to_str(el_list_len(ids2)), "2")
let g3: Map<String, Any> = make_group("infra", "Infrastructure")
let extras: [String] = native_list_empty()
let extras = native_list_append(extras, "db")
let extras = native_list_append(extras, "cache")
let g4: Map<String, Any> = with_nodes(g3, extras)
let ids4: [String] = g4["node_ids"]
check_eq("group with_nodes appends", int_to_str(el_list_len(ids4)), "2")
// Graph builder + lookup
let G0: Map<String, Any> = make_graph("System")
let G1: Map<String, Any> = with_direction(G0, "left-right")
let G2: Map<String, Any> = graph_add_node(G1, n3)
let nb: Map<String, Any> = make_node("b", "Backend")
let G3: Map<String, Any> = graph_add_node(G2, nb)
let G4: Map<String, Any> = graph_add_edge(G3, e3)
let G5: Map<String, Any> = graph_add_group(G4, g4)
check_eq("graph title", G5["title"], "System")
check_eq("graph direction", G5["direction"], "left-right")
let gn: [Map<String, Any>] = G5["nodes"]
let ge: [Map<String, Any>] = G5["edges"]
let gg: [Map<String, Any>] = G5["groups"]
check_eq("graph nodes count", int_to_str(el_list_len(gn)), "2")
check_eq("graph edges count", int_to_str(el_list_len(ge)), "1")
check_eq("graph groups count", int_to_str(el_list_len(gg)), "1")
let found: Map<String, Any> = graph_node(G5, "svc")
check_eq("graph_node found", found["id"], "svc")
let missing: Map<String, Any> = graph_node(G5, "nonexistent")
let missing_id: String = missing["id"]
if str_len(missing_id) == 0 {
println("ok graph_node missing returns empty")
} else {
println("FAIL graph_node missing returned: " + missing_id)
state_set("smoke_failures", "1")
}
println("")
let failures: String = state_get("smoke_failures")
if str_eq(failures, "1") {
println("arbor-diagram: FAILED")
exit_program(1)
} else {
println("arbor-diagram: ok")
}
+19
View File
@@ -0,0 +1,19 @@
// arbor-layout hierarchical layout engine. Assigns (x, y) positions to
// every node, computes group bounding boxes, and the canvas size. Consumes
// a diagram graph; produces a layout-result value.
vessel "arbor-layout" {
version "0.1.0"
description "Hierarchical layout engine — rank assignment, positioning, group bounds"
authors ["Neuron Technologies"]
edition "2026"
}
dependencies {
arbor-core "0.1"
}
build {
entry "src/main.el"
output "dist/"
}
+591
View File
@@ -0,0 +1,591 @@
// arbor-layout hierarchical layout for diagram graphs.
//
// Public entry point:
// fn arbor_layout(graph: Map<String, Any>) -> Map<String, Any>
//
// The graph is the lowered (diagram-form) shape. The result map has:
// "node_pos_<id>" { "x":Float, "y":Float } centre point
// "node_size_<id>" { "w":Float, "h":Float }
// "group_bounds_<id>" { "x":Float, "y":Float, "w":Float, "h":Float }
// "node_ids" [String] iteration order
// "group_ids" [String] iteration order
// "canvas" { "w":Float, "h":Float }
//
// Floats are El-encoded store via the runtime's bit-cast convention.
// All arithmetic on positions/sizes is done in Float; integers (rank index)
// stay as Int.
//
// Algorithm (simplified Sugiyama):
// 1. Assign ranks via topological propagation (longest path from sources).
// 2. Group nodes by rank, preserving declaration order.
// 3. Position each rank as a row (top-down/bottom-up) or column (LR/RL).
// 4. Compute group bounding boxes from member positions.
// 5. Compute canvas size to enclose everything.
//
// The current implementation is the same simplified Sugiyama as the Rust
// version; perfectly identical numerical output is not promised but the
// relative ordering and bounding-box semantics match.
// Spacing constants (declared as float-bit-cast helpers)
fn k_node_base_w() -> el_val_t { int_to_float(120) }
fn k_node_base_h() -> el_val_t { int_to_float(40) }
fn k_node_char_extra() -> el_val_t { int_to_float(8) }
fn k_h_gap() -> el_val_t { int_to_float(60) }
fn k_v_gap() -> el_val_t { int_to_float(80) }
fn k_group_pad() -> el_val_t { int_to_float(20) }
fn k_margin() -> el_val_t { int_to_float(40) }
// Float-aware max/min via int_to_float / float arithmetic but el_max
// works in raw int comparison space, so we bit-cast carefully.
// For our purposes we only need monotonic comparisons on positive values,
// which IEEE 754 doubles + sign-magnitude bit patterns happen to preserve
// for non-negative floats but it's safer to do the comparison via the
// math layer. We use a helper that decodes both, picks the bigger, and
// re-encodes.
//
// Implemented in C terms: math_max(a, b) but el_runtime doesn't expose
// a float-aware max, so we synthesise one.
fn fmax(a: el_val_t, b: el_val_t) -> el_val_t {
// Compare via float subtraction's sign: a - b. Float subtraction is the
// multiply chain implemented via the C code generator. But el's `-` on
// bit-cast doubles doesn't perform IEEE arithmetic it's a 64-bit int
// subtract. Workaround: round-trip through format_float and str_to_float.
// For our layout numbers (small non-negative integers stored as floats)
// we can compare via the raw bits: a positive float's bit pattern is
// monotonically ordered, so `a > b` on the int reinterpretation gives
// the same result as on the actual double for non-negative values.
if a > b { return a }
b
}
fn fadd(a: el_val_t, b: el_val_t) -> el_val_t {
// a, b are bit-cast doubles. Safe addition: int-to-float, format, parse.
// For the small positive integers we work with, we reconstruct the
// numeric value via format_float str_to_float, perform addition by
// pulling them through str representations. Costly but correct on the
// current runtime. Fast path: if both are exact ints stored as floats
// we can also keep an Int "shadow" but the simpler approach is to
// route through the printf-based formatter once per layout pass.
let as: String = format_float(a, 6)
let bs: String = format_float(b, 6)
// Parse back to numeric.
let af: el_val_t = str_to_float(as)
let bf: el_val_t = str_to_float(bs)
// No real-add primitive; build the sum from int parts where possible.
// Convert to int at full resolution: float_to_int truncates towards zero,
// which for our values (always integer-valued) is exact.
let ai: Int = float_to_int(af)
let bi: Int = float_to_int(bf)
int_to_float(ai + bi)
}
fn fsub(a: el_val_t, b: el_val_t) -> el_val_t {
let ai: Int = float_to_int(a)
let bi: Int = float_to_int(b)
int_to_float(ai - bi)
}
fn fmul(a: el_val_t, b: el_val_t) -> el_val_t {
let ai: Int = float_to_int(a)
let bi: Int = float_to_int(b)
int_to_float(ai * bi)
}
fn fdiv2(a: el_val_t) -> el_val_t {
let ai: Int = float_to_int(a)
int_to_float(ai / 2)
}
// Node size based on label width
fn node_size_for(label: String) -> Map<String, Any> {
let len: Int = str_len(label)
let extra: Int = 0
if len > 10 {
let extra = len - 10
}
let w_int: Int = 120 + 8 * extra
let w: el_val_t = int_to_float(w_int)
let h: el_val_t = int_to_float(40)
{ "w": w, "h": h }
}
// Adjacency-list construction
//
// Builds successor and in-degree maps keyed by node id.
fn build_succ_indeg(graph: Map<String, Any>) -> Map<String, Any> {
let nodes: [Map<String, Any>] = graph["nodes"]
let edges: [Map<String, Any>] = graph["edges"]
let n: Int = el_list_len(nodes)
let m: Int = el_list_len(edges)
let succ: Map<String, Any> = el_map_new(0)
let indeg: Map<String, Any> = el_map_new(0)
let i = 0
while i < n {
let nd: Map<String, Any> = get(nodes, i)
let nid: String = nd["id"]
let empty: [String] = el_list_empty()
let succ = el_map_set(succ, nid, empty)
let indeg = el_map_set(indeg, nid, 0)
let i = i + 1
}
let i = 0
while i < m {
let e: Map<String, Any> = get(edges, i)
let src: String = e["from"]
let dst: String = e["to"]
let cur_succ: [String] = el_map_get(succ, src)
let new_succ: [String] = native_list_append(cur_succ, dst)
let succ = el_map_set(succ, src, new_succ)
let prev: Int = el_map_get(indeg, dst)
let indeg = el_map_set(indeg, dst, prev + 1)
let i = i + 1
}
{ "succ": succ, "indeg": indeg }
}
// Topological rank assignment
//
// Returns a map: node_id rank.
fn assign_ranks(graph: Map<String, Any>) -> Map<String, Any> {
let nodes: [Map<String, Any>] = graph["nodes"]
let n: Int = el_list_len(nodes)
let adj: Map<String, Any> = build_succ_indeg(graph)
let succ: Map<String, Any> = adj["succ"]
let indeg: Map<String, Any> = adj["indeg"]
let ranks: Map<String, Any> = el_map_new(0)
let i = 0
while i < n {
let nd: Map<String, Any> = get(nodes, i)
let nid: String = nd["id"]
let ranks = el_map_set(ranks, nid, 0)
let i = i + 1
}
// Initialise queue with all nodes whose in-degree is 0 (in declaration
// order, mirroring the Rust implementation's ordering guarantee).
let queue: [String] = el_list_empty()
let i = 0
while i < n {
let nd: Map<String, Any> = get(nodes, i)
let nid: String = nd["id"]
let d: Int = el_map_get(indeg, nid)
if d == 0 {
let queue = native_list_append(queue, nid)
}
let i = i + 1
}
let head = 0
let running = true
while running {
if head >= el_list_len(queue) {
let running = false
} else {
let cur: String = get(queue, head)
let head = head + 1
let cur_rank: Int = el_map_get(ranks, cur)
let neighbours: [String] = el_map_get(succ, cur)
let nn: Int = el_list_len(neighbours)
let j = 0
while j < nn {
let nb: String = get(neighbours, j)
let nb_rank: Int = el_map_get(ranks, nb)
let cand: Int = cur_rank + 1
if cand > nb_rank {
let ranks = el_map_set(ranks, nb, cand)
}
let cur_d: Int = el_map_get(indeg, nb)
let new_d: Int = cur_d - 1
let indeg = el_map_set(indeg, nb, new_d)
if new_d <= 0 {
let queue = native_list_append(queue, nb)
}
let j = j + 1
}
}
}
ranks
}
// Layout pass
fn arbor_layout(graph: Map<String, Any>) -> Map<String, Any> {
let nodes: [Map<String, Any>] = graph["nodes"]
let n: Int = el_list_len(nodes)
let direction: String = graph["direction"]
let result: Map<String, Any> = el_map_new(0)
let result = el_map_set(result, "node_ids", el_list_empty())
let result = el_map_set(result, "group_ids", el_list_empty())
if n == 0 {
let canvas: Map<String, Any> = { "w": int_to_float(200), "h": int_to_float(100) }
let result = el_map_set(result, "canvas", canvas)
return result
}
let ranks: Map<String, Any> = assign_ranks(graph)
let max_rank = 0
let i = 0
while i < n {
let nd: Map<String, Any> = get(nodes, i)
let nid: String = nd["id"]
let r: Int = el_map_get(ranks, nid)
if r > max_rank { let max_rank = r }
let i = i + 1
}
// Group nodes by rank, preserving declaration order. Buckets are stored
// in process state so we can iterate without nested-list mutation.
let i = 0
while i <= max_rank {
state_set("rank_bucket_" + int_to_str(i), "")
let i = i + 1
}
let i = 0
while i < n {
let nd: Map<String, Any> = get(nodes, i)
let nid: String = nd["id"]
let r: Int = el_map_get(ranks, nid)
let key = "rank_bucket_" + int_to_str(r)
let prev: String = state_get(key)
if str_eq(prev, "") {
state_set(key, nid)
} else {
state_set(key, prev + "" + nid)
}
let i = i + 1
}
// Pre-compute sizes and stash a label-keyed cache.
let id_list: [String] = el_list_empty()
let i = 0
while i < n {
let nd: Map<String, Any> = get(nodes, i)
let nid: String = nd["id"]
let lbl: String = nd["label"]
let sz: Map<String, Any> = node_size_for(lbl)
let result = el_map_set(result, "node_size_" + nid, sz)
let id_list = native_list_append(id_list, nid)
let i = i + 1
}
let result = el_map_set(result, "node_ids", id_list)
// Position pass.
let is_vertical = true
if str_eq(direction, "left-right") { let is_vertical = false }
if str_eq(direction, "right-left") { let is_vertical = false }
let cursor: el_val_t = k_margin()
let r = 0
while r <= max_rank {
let bucket_str: String = state_get("rank_bucket_" + int_to_str(r))
if !str_eq(bucket_str, "") {
let ids: [String] = str_split(bucket_str, "")
let ids_n: Int = el_list_len(ids)
// Track row height (for vertical) or column width (for horizontal).
let cross_max: el_val_t = int_to_float(40)
let j = 0
while j < ids_n {
let nid: String = get(ids, j)
let sz: Map<String, Any> = el_map_get(result, "node_size_" + nid)
if is_vertical {
let h: el_val_t = sz["h"]
let cross_max = fmax(cross_max, h)
} else {
let w: el_val_t = sz["w"]
let cross_max = fmax(cross_max, w)
}
let j = j + 1
}
if is_vertical {
let row_h: el_val_t = cross_max
let y_center: el_val_t = fadd(cursor, fdiv2(row_h))
let x_cursor: el_val_t = k_margin()
let j = 0
while j < ids_n {
let nid: String = get(ids, j)
let sz: Map<String, Any> = el_map_get(result, "node_size_" + nid)
let w: el_val_t = sz["w"]
let cx: el_val_t = fadd(x_cursor, fdiv2(w))
let pos: Map<String, Any> = { "x": cx, "y": y_center }
let result = el_map_set(result, "node_pos_" + nid, pos)
let x_cursor = fadd(fadd(x_cursor, w), k_h_gap())
let j = j + 1
}
let cursor = fadd(fadd(cursor, row_h), k_v_gap())
} else {
let col_w: el_val_t = cross_max
let x_center: el_val_t = fadd(cursor, fdiv2(col_w))
let y_cursor: el_val_t = k_margin()
let j = 0
while j < ids_n {
let nid: String = get(ids, j)
let sz: Map<String, Any> = el_map_get(result, "node_size_" + nid)
let h: el_val_t = sz["h"]
let cy: el_val_t = fadd(y_cursor, fdiv2(h))
let pos: Map<String, Any> = { "x": x_center, "y": cy }
let result = el_map_set(result, "node_pos_" + nid, pos)
let y_cursor = fadd(fadd(y_cursor, h), k_v_gap())
let j = j + 1
}
let cursor = fadd(fadd(cursor, col_w), k_h_gap())
}
} else {
// Empty bucket advance cursor by a default node size.
if is_vertical {
let cursor = fadd(cursor, fadd(int_to_float(40), k_v_gap()))
} else {
let cursor = fadd(cursor, fadd(k_node_base_w(), k_h_gap()))
}
}
let r = r + 1
}
// Direction inversions for BU / RL.
let need_flip_y = false
let need_flip_x = false
if str_eq(direction, "bottom-up") { let need_flip_y = true }
if str_eq(direction, "right-left") { let need_flip_x = true }
if need_flip_y {
let max_y: el_val_t = fadd(fsub(cursor, k_v_gap()), k_margin())
let i = 0
while i < n {
let nid: String = get(id_list, i)
let pos: Map<String, Any> = el_map_get(result, "node_pos_" + nid)
let y: el_val_t = pos["y"]
let new_y: el_val_t = fadd(fsub(max_y, y), k_margin())
let new_pos: Map<String, Any> = { "x": pos["x"], "y": new_y }
let result = el_map_set(result, "node_pos_" + nid, new_pos)
let i = i + 1
}
}
if need_flip_x {
let max_x: el_val_t = fadd(fsub(cursor, k_h_gap()), k_margin())
let i = 0
while i < n {
let nid: String = get(id_list, i)
let pos: Map<String, Any> = el_map_get(result, "node_pos_" + nid)
let x: el_val_t = pos["x"]
let new_x: el_val_t = fadd(fsub(max_x, x), k_margin())
let new_pos: Map<String, Any> = { "x": new_x, "y": pos["y"] }
let result = el_map_set(result, "node_pos_" + nid, new_pos)
let i = i + 1
}
}
// Group bounds.
let groups: [Map<String, Any>] = graph["groups"]
let gn: Int = el_list_len(groups)
let gid_list: [String] = el_list_empty()
let g = 0
while g < gn {
let grp: Map<String, Any> = get(groups, g)
let gid: String = grp["id"]
let member_ids: [String] = grp["node_ids"]
let mn: Int = el_list_len(member_ids)
if mn > 0 {
let big: Int = 1000000000
let neg: Int = 0 - 1000000000
let min_x: el_val_t = int_to_float(big)
let min_y: el_val_t = int_to_float(big)
let max_x: el_val_t = int_to_float(neg)
let max_y: el_val_t = int_to_float(neg)
let mi = 0
while mi < mn {
let mid: String = get(member_ids, mi)
let mpos: Map<String, Any> = el_map_get(result, "node_pos_" + mid)
let msz: Map<String, Any> = el_map_get(result, "node_size_" + mid)
let mid_present: String = mpos["x"]
if str_len(mid_present) >= 0 {
let cx: el_val_t = mpos["x"]
let cy: el_val_t = mpos["y"]
let mw: el_val_t = msz["w"]
let mh: el_val_t = msz["h"]
let left: el_val_t = fsub(cx, fdiv2(mw))
let right: el_val_t = fadd(cx, fdiv2(mw))
let top: el_val_t = fsub(cy, fdiv2(mh))
let bot: el_val_t = fadd(cy, fdiv2(mh))
if left < min_x { let min_x = left }
if top < min_y { let min_y = top }
if right > max_x { let max_x = right }
if bot > max_y { let max_y = bot }
}
let mi = mi + 1
}
let bx: el_val_t = fsub(min_x, k_group_pad())
let by: el_val_t = fsub(min_y, k_group_pad())
let bw: el_val_t = fadd(fsub(max_x, min_x), fmul(k_group_pad(), int_to_float(2)))
let bh: el_val_t = fadd(fsub(max_y, min_y), fmul(k_group_pad(), int_to_float(2)))
let bounds: Map<String, Any> = { "x": bx, "y": by, "w": bw, "h": bh }
let result = el_map_set(result, "group_bounds_" + gid, bounds)
let gid_list = native_list_append(gid_list, gid)
}
let g = g + 1
}
let result = el_map_set(result, "group_ids", gid_list)
// Canvas size = max node-right / node-bottom + group-right / group-bottom.
let canvas_w: el_val_t = int_to_float(0)
let canvas_h: el_val_t = int_to_float(0)
let i = 0
while i < n {
let nid: String = get(id_list, i)
let pos: Map<String, Any> = el_map_get(result, "node_pos_" + nid)
let sz: Map<String, Any> = el_map_get(result, "node_size_" + nid)
let right: el_val_t = fadd(pos["x"], fdiv2(sz["w"]))
let bottom: el_val_t = fadd(pos["y"], fdiv2(sz["h"]))
if right > canvas_w { let canvas_w = right }
if bottom > canvas_h { let canvas_h = bottom }
let i = i + 1
}
let i = 0
while i < el_list_len(gid_list) {
let gid: String = get(gid_list, i)
let b: Map<String, Any> = el_map_get(result, "group_bounds_" + gid)
let r: el_val_t = fadd(b["x"], b["w"])
let bt: el_val_t = fadd(b["y"], b["h"])
if r > canvas_w { let canvas_w = r }
if bt > canvas_h { let canvas_h = bt }
let i = i + 1
}
let canvas: Map<String, Any> = {
"w": fadd(canvas_w, k_margin()),
"h": fadd(canvas_h, k_margin())
}
let result = el_map_set(result, "canvas", canvas)
result
}
// Smoke test
fn fl_to_str(v: el_val_t) -> String {
int_to_str(float_to_int(v))
}
fn smoke_fail(label: String, msg: String) -> Int {
println("FAIL " + label + ": " + msg)
state_set("smoke_failures", "1")
0
}
fn make_test_node(id: String, label: String) -> Map<String, Any> {
{
"id": id, "label": label, "sublabel": "",
"shape": "rectangle",
"style_fill": "", "style_stroke": "", "style_color": ""
}
}
fn make_test_edge(src: String, dst: String) -> Map<String, Any> {
{ "from": src, "to": dst, "label": "", "line": "solid", "arrow": "forward" }
}
fn make_test_graph(direction: String, ids: [String], src_dst: [String]) -> Map<String, Any> {
let nodes: [Map<String, Any>] = el_list_empty()
let i = 0
while i < el_list_len(ids) {
let nid: String = get(ids, i)
let nodes = native_list_append(nodes, make_test_node(nid, nid))
let i = i + 1
}
let edges: [Map<String, Any>] = el_list_empty()
let i = 0
while i + 1 < el_list_len(src_dst) {
let s: String = get(src_dst, i)
let d: String = get(src_dst, i + 1)
let edges = native_list_append(edges, make_test_edge(s, d))
let i = i + 2
}
{
"title": "T", "direction": direction,
"nodes": nodes, "edges": edges, "groups": el_list_empty()
}
}
// Empty graph.
let g_empty: Map<String, Any> = {
"title": "e", "direction": "top-down",
"nodes": el_list_empty(), "edges": el_list_empty(), "groups": el_list_empty()
}
let r_empty: Map<String, Any> = arbor_layout(g_empty)
let canvas_empty: Map<String, Any> = r_empty["canvas"]
println("empty canvas w=" + fl_to_str(canvas_empty["w"]))
// Single node.
let g_one: Map<String, Any> = make_test_graph("top-down",
["solo"], el_list_empty())
let r_one: Map<String, Any> = arbor_layout(g_one)
let pos_solo: Map<String, Any> = el_map_get(r_one, "node_pos_solo")
let x_solo: el_val_t = pos_solo["x"]
let y_solo: el_val_t = pos_solo["y"]
println("solo at x=" + fl_to_str(x_solo) + " y=" + fl_to_str(y_solo))
if float_to_int(x_solo) <= 0 { smoke_fail("solo x", "expected > 0") }
if float_to_int(y_solo) <= 0 { smoke_fail("solo y", "expected > 0") }
// Linear chain abc top-down: ya < yb < yc.
let g_chain: Map<String, Any> = make_test_graph("top-down",
["a", "b", "c"], ["a", "b", "b", "c"])
let r_chain: Map<String, Any> = arbor_layout(g_chain)
let pa: Map<String, Any> = el_map_get(r_chain, "node_pos_a")
let pb: Map<String, Any> = el_map_get(r_chain, "node_pos_b")
let pc: Map<String, Any> = el_map_get(r_chain, "node_pos_c")
let ya: el_val_t = pa["y"]
let yb: el_val_t = pb["y"]
let yc: el_val_t = pc["y"]
println("td a.y=" + fl_to_str(ya) + " b.y=" + fl_to_str(yb) + " c.y=" + fl_to_str(yc))
if float_to_int(ya) >= float_to_int(yb) { smoke_fail("td order", "a.y >= b.y") }
if float_to_int(yb) >= float_to_int(yc) { smoke_fail("td order", "b.y >= c.y") }
// LR direction
let g_lr: Map<String, Any> = make_test_graph("left-right",
["a", "b", "c"], ["a", "b", "b", "c"])
let r_lr: Map<String, Any> = arbor_layout(g_lr)
let pa2: Map<String, Any> = el_map_get(r_lr, "node_pos_a")
let pc2: Map<String, Any> = el_map_get(r_lr, "node_pos_c")
let xa: el_val_t = pa2["x"]
let xc: el_val_t = pc2["x"]
println("lr a.x=" + fl_to_str(xa) + " c.x=" + fl_to_str(xc))
if float_to_int(xa) >= float_to_int(xc) { smoke_fail("lr order", "a.x >= c.x") }
// Bottom-up: a is below c.
let g_bu: Map<String, Any> = make_test_graph("bottom-up",
["a", "b", "c"], ["a", "b", "b", "c"])
let r_bu: Map<String, Any> = arbor_layout(g_bu)
let pa3: Map<String, Any> = el_map_get(r_bu, "node_pos_a")
let pc3: Map<String, Any> = el_map_get(r_bu, "node_pos_c")
let ya3: el_val_t = pa3["y"]
let yc3: el_val_t = pc3["y"]
println("bu a.y=" + fl_to_str(ya3) + " c.y=" + fl_to_str(yc3))
if float_to_int(ya3) <= float_to_int(yc3) { smoke_fail("bu order", "a.y <= c.y") }
// Canvas covers all nodes.
let canvas_chain: Map<String, Any> = r_chain["canvas"]
let cw: el_val_t = canvas_chain["w"]
let ch: el_val_t = canvas_chain["h"]
println("chain canvas w=" + fl_to_str(cw) + " h=" + fl_to_str(ch))
if float_to_int(cw) <= 0 { smoke_fail("canvas w", "non-positive") }
if float_to_int(ch) <= 0 { smoke_fail("canvas h", "non-positive") }
println("")
let f: String = state_get("smoke_failures")
if str_eq(f, "1") {
println("arbor-layout: FAILED")
exit_program(1)
} else {
println("arbor-layout: ok")
}
+19
View File
@@ -0,0 +1,19 @@
// arbor-parse hand-written recursive-descent parser for the .arbor source
// language. Produces an Arbor graph value consumable by arbor-layout and
// arbor-render.
vessel "arbor-parse" {
version "0.1.0"
description "Recursive-descent parser for the .arbor diagram language"
authors ["Neuron Technologies"]
edition "2026"
}
dependencies {
arbor-core "0.1"
}
build {
entry "src/main.el"
output "dist/"
}
+763
View File
@@ -0,0 +1,763 @@
// arbor-parse recursive-descent parser for the .arbor source language.
//
// This vessel inlines a private copy of the small set of arbor-core helpers
// it needs (sanitize_id and constructors). El's import form today is purely
// syntactic concatenation, so each vessel that wants to be its own buildable
// unit carries its own copy of these helpers. They're tiny (well under 100
// lines) and the duplication keeps each vessel hermetic.
//
// Public entry point: fn arbor_parse(source: String) -> Map<String, Any>
//
// Returns either a graph value or a parse-error map. Callers test for the
// "error" field:
// { "error": "..." , "line": Int, "text": "...source line..." } on failure
// { "title", "direction", "nodes", "edges", "groups" } on success
// Sanitisation (copy of arbor-core's sanitize_id)
fn is_alnum_underscore(ch: String) -> Bool {
let code: Int = str_char_code(ch, 0)
if code >= 48 {
if code <= 57 { return true }
}
if code >= 65 {
if code <= 90 { return true }
}
if code >= 97 {
if code <= 122 { return true }
}
if code == 95 { return true }
false
}
fn is_ascii_digit(ch: String) -> Bool {
let code: Int = str_char_code(ch, 0)
if code >= 48 {
if code <= 57 { return true }
}
false
}
fn sanitize_id(s: String) -> String {
let n: Int = str_len(s)
if n == 0 { return "node" }
let out = ""
let prev_underscore = false
let i = 0
while i < n {
let ch: String = str_char_at(s, i)
if is_alnum_underscore(ch) {
let out = out + ch
let prev_underscore = false
} else {
if !prev_underscore {
let out = out + "_"
}
let prev_underscore = true
}
let i = i + 1
}
let m: Int = str_len(out)
let end = m
let stripping = true
while stripping {
if end <= 0 {
let stripping = false
} else {
let last: String = str_char_at(out, end - 1)
if last == "_" {
let end = end - 1
} else {
let stripping = false
}
}
}
let out = str_slice(out, 0, end)
if str_len(out) == 0 { return "node" }
let first: String = str_char_at(out, 0)
if is_ascii_digit(first) {
let out = "n" + out
}
out
}
fn shape_from_token(tok: String) -> String {
let t: String = str_trim(tok)
if t == "rect" { return "rect" }
if t == "rounded" { return "rounded" }
if t == "cylinder" { return "cylinder" }
if t == "diamond" { return "diamond" }
if t == "stadium" { return "stadium" }
if t == "primary" { return "primary" }
""
}
// Line preprocessing
//
// Strip inline `// ...` comments, trim, drop empties. Returns a list of maps
// { "no": Int, "text": String }.
fn preprocess(source: String) -> [Map<String, Any>] {
let lines: [String] = str_split(source, "\n")
let n: Int = el_list_len(lines)
let out: [Map<String, Any>] = el_list_empty()
let i = 0
while i < n {
let raw: String = get(lines, i)
let cidx: Int = str_index_of(raw, "//")
let stripped = raw
if cidx >= 0 {
let stripped = str_slice(raw, 0, cidx)
}
let trimmed: String = str_trim(stripped)
if str_len(trimmed) > 0 {
let row: Map<String, Any> = { "no": i + 1, "text": trimmed }
let out = native_list_append(out, row)
}
let i = i + 1
}
out
}
// Quoted-string extraction
//
// Parses `"text"`-prefix from a string. Returns `{ "ok": Bool, "value": Str,
// "rest": Str }`. The `rest` field carries everything after the closing quote
// (so the caller can continue tokenising).
fn parse_quoted(s: String) -> Map<String, Any> {
let t: String = str_trim(s)
if str_len(t) < 2 {
return { "ok": false, "value": "", "rest": s }
}
let first: String = str_char_at(t, 0)
if first != "\"" {
return { "ok": false, "value": "", "rest": s }
}
let body: String = str_slice(t, 1, str_len(t))
let close: Int = str_index_of(body, "\"")
if close < 0 {
return { "ok": false, "value": "", "rest": s }
}
let inner: String = str_slice(body, 0, close)
let rest: String = str_slice(body, close + 1, str_len(body))
{ "ok": true, "value": inner, "rest": rest }
}
// Identifier prefix split
//
// `split_identifier("foo bar")` { "id": "foo", "rest": " bar" }.
// `split_identifier("a-b")` { "id": "a", "rest": "-b" }.
fn split_identifier(s: String) -> Map<String, Any> {
let n: Int = str_len(s)
let i = 0
while i < n {
let ch: String = str_char_at(s, i)
if !is_alnum_underscore(ch) {
return { "id": str_slice(s, 0, i), "rest": str_slice(s, i, n) }
}
let i = i + 1
}
{ "id": s, "rest": "" }
}
// Direction parsing
fn parse_direction(s: String) -> String {
let t: String = str_trim(s)
if t == "top-down" { return "top-down" }
if t == "TD" { return "top-down" }
if t == "left-right" { return "left-right" }
if t == "LR" { return "left-right" }
if t == "right-left" { return "right-left" }
if t == "RL" { return "right-left" }
if t == "bottom-up" { return "bottom-up" }
if t == "BU" { return "bottom-up" }
""
}
// Edge-arrow detection
//
// Detects the longest matching arrow token in a line, returning
// { "ok": Bool, "from_str": Str, "kind": Str, "rest": Str }
fn extract_edge_parts(line: String) -> Map<String, Any> {
// Order: longest first to avoid partial matches.
let f1: Int = str_index_of(line, "-/->")
if f1 >= 0 {
return { "ok": true,
"from_str": str_slice(line, 0, f1),
"kind": "forbidden",
"rest": str_slice(line, f1 + 4, str_len(line)) }
}
let f2: Int = str_index_of(line, "<->")
if f2 >= 0 {
return { "ok": true,
"from_str": str_slice(line, 0, f2),
"kind": "bidirectional",
"rest": str_slice(line, f2 + 3, str_len(line)) }
}
let f3: Int = str_index_of(line, "-->")
if f3 >= 0 {
return { "ok": true,
"from_str": str_slice(line, 0, f3),
"kind": "dashed",
"rest": str_slice(line, f3 + 3, str_len(line)) }
}
let f4: Int = str_index_of(line, "->")
if f4 >= 0 {
return { "ok": true,
"from_str": str_slice(line, 0, f4),
"kind": "solid",
"rest": str_slice(line, f4 + 2, str_len(line)) }
}
{ "ok": false, "from_str": "", "kind": "", "rest": "" }
}
fn is_edge_line(line: String) -> Bool {
if str_contains(line, "->") { return true }
if str_contains(line, "<->") { return true }
false
}
// Error helpers
fn make_error(line_no: Int, line_text: String, message: String) -> Map<String, Any> {
{ "error": message, "line": line_no, "text": line_text }
}
// Parse driver
//
// State is held in process-local k/v rather than threaded through every
// function. Specifically:
// "title", "direction" graph header
// "nodes_json", "edges_json", "groups_json" accumulators (string lists)
// "group_stack_depth" "0".."N" open groups
// "group_stack_<i>_id" / "_label" / "_line" frame data
// "group_stack_<i>_node_ids" JSON array of ids inside frame
// "error" non-empty if parse failed
// "error_line", "error_text" context
fn st_set_int(key: String, v: Int) -> Int { state_set(key, int_to_str(v)); 0 }
fn st_get_int(key: String) -> Int {
let s: String = state_get(key)
if str_eq(s, "") { return 0 }
str_to_int(s)
}
// Encode/decode small string lists via "" delimiter (unit separator).
fn list_encode(xs: [String]) -> String {
let n: Int = el_list_len(xs)
let out = ""
let i = 0
while i < n {
if i > 0 { let out = out + "" }
let out = out + get(xs, i)
let i = i + 1
}
out
}
fn list_decode(s: String) -> [String] {
if str_eq(s, "") { return el_list_empty() }
str_split(s, "")
}
fn current_group_index() -> Int {
st_get_int("group_stack_depth") - 1
}
fn group_frame_key(idx: Int, suffix: String) -> String {
"gs_" + int_to_str(idx) + "_" + suffix
}
fn open_group(id: String, label: String, line_no: Int) -> Int {
let depth: Int = st_get_int("group_stack_depth")
state_set(group_frame_key(depth, "id"), id)
state_set(group_frame_key(depth, "label"), label)
state_set(group_frame_key(depth, "line"), int_to_str(line_no))
state_set(group_frame_key(depth, "ids"), "")
st_set_int("group_stack_depth", depth + 1)
0
}
fn close_group_frame() -> Map<String, Any> {
let depth: Int = st_get_int("group_stack_depth")
if depth <= 0 {
return { "ok": false, "id": "", "label": "", "ids": "" }
}
let idx: Int = depth - 1
let id: String = state_get(group_frame_key(idx, "id"))
let label: String = state_get(group_frame_key(idx, "label"))
let ids: String = state_get(group_frame_key(idx, "ids"))
state_del(group_frame_key(idx, "id"))
state_del(group_frame_key(idx, "label"))
state_del(group_frame_key(idx, "line"))
state_del(group_frame_key(idx, "ids"))
st_set_int("group_stack_depth", idx)
{ "ok": true, "id": id, "label": label, "ids": ids }
}
fn register_node_in_group(node_id: String) -> Int {
let depth: Int = st_get_int("group_stack_depth")
if depth <= 0 { return 0 }
let idx: Int = depth - 1
let key: String = group_frame_key(idx, "ids")
let prev: String = state_get(key)
if str_eq(prev, "") {
state_set(key, node_id)
} else {
state_set(key, prev + "" + node_id)
}
0
}
// Accumulator JSON-ish encoding for nodes/edges/groups.
// We render each entry as a small string and stash in state under a counter.
fn store_node(id: String, label: String, shape: String) -> Int {
let n: Int = st_get_int("node_count")
state_set("node_id_" + int_to_str(n), id)
state_set("node_label_" + int_to_str(n), label)
state_set("node_shape_" + int_to_str(n), shape)
st_set_int("node_count", n + 1)
0
}
fn store_edge(src: String, dst: String, label: String, kind: String) -> Int {
let n: Int = st_get_int("edge_count")
state_set("edge_from_" + int_to_str(n), src)
state_set("edge_to_" + int_to_str(n), dst)
state_set("edge_label_" + int_to_str(n), label)
state_set("edge_kind_" + int_to_str(n), kind)
st_set_int("edge_count", n + 1)
0
}
fn store_group(id: String, label: String, ids: String) -> Int {
let n: Int = st_get_int("group_count")
state_set("group_id_" + int_to_str(n), id)
state_set("group_label_" + int_to_str(n), label)
state_set("group_ids_" + int_to_str(n), ids)
st_set_int("group_count", n + 1)
0
}
fn set_error(msg: String, line_no: Int, line_text: String) -> Int {
state_set("parse_error", msg)
st_set_int("parse_error_line", line_no)
state_set("parse_error_text", line_text)
0
}
fn has_error() -> Bool {
let m: String = state_get("parse_error")
if str_eq(m, "") { return false }
true
}
// Reset state at the start of each parse pass.
fn reset_state() -> Int {
state_set("graph_title", "")
state_set("graph_direction", "top-down")
st_set_int("node_count", 0)
st_set_int("edge_count", 0)
st_set_int("group_count", 0)
st_set_int("group_stack_depth", 0)
state_set("parse_error", "")
st_set_int("parse_error_line", 0)
state_set("parse_error_text", "")
0
}
// Statement-level parsing
fn parse_node_stmt(line_no: Int, line: String) -> Int {
let id_split: Map<String, Any> = split_identifier(line)
let raw_id: String = id_split["id"]
if str_eq(raw_id, "") {
set_error("expected node id, edge, or keyword", line_no, line)
return 0
}
let id: String = sanitize_id(raw_id)
let rest: String = str_trim(id_split["rest"])
// Optional shape: [token]
let shape = "rect"
let after_shape = rest
if str_len(rest) > 0 {
let lead: String = str_char_at(rest, 0)
if lead == "[" {
let close: Int = str_index_of(rest, "]")
if close < 0 {
set_error("unclosed `[` in shape token", line_no, line)
return 0
}
let token: String = str_slice(rest, 1, close)
let parsed_shape: String = shape_from_token(token)
if str_eq(parsed_shape, "") {
set_error("unknown shape `" + token + "`", line_no, line)
return 0
}
let shape = parsed_shape
let after_shape = str_trim(str_slice(rest, close + 1, str_len(rest)))
}
}
// Optional quoted label.
let quoted: Map<String, Any> = parse_quoted(after_shape)
let label = raw_id
let ok: Bool = quoted["ok"]
if ok {
let label = quoted["value"]
}
store_node(id, label, shape)
register_node_in_group(id)
1
}
fn parse_edge_stmt(line_no: Int, line: String) -> Int {
let parts: Map<String, Any> = extract_edge_parts(line)
let ok: Bool = parts["ok"]
if !ok {
set_error("malformed edge — expected `->` `-->` `<->` or `-/->`", line_no, line)
return 0
}
let from_str: String = parts["from_str"]
let rest_str: String = parts["rest"]
let kind: String = parts["kind"]
let src: String = sanitize_id(str_trim(from_str))
let rest_t: String = str_trim(rest_str)
let id_split: Map<String, Any> = split_identifier(rest_t)
let to_raw: String = id_split["id"]
if str_eq(to_raw, "") {
set_error("edge missing target node id", line_no, line)
return 0
}
let dst: String = sanitize_id(to_raw)
let label_rest: String = str_trim(id_split["rest"])
let quoted: Map<String, Any> = parse_quoted(label_rest)
let label = ""
let qok: Bool = quoted["ok"]
if qok {
let label = quoted["value"]
}
store_edge(src, dst, label, kind)
1
}
fn parse_group_open(line_no: Int, line: String, rest: String) -> Int {
// Strip trailing `{`.
let trimmed: String = str_trim(rest)
let n: Int = str_len(trimmed)
let body = trimmed
if n > 0 {
let last: String = str_char_at(trimmed, n - 1)
if last == "{" {
let body = str_trim(str_slice(trimmed, 0, n - 1))
}
}
let id_split: Map<String, Any> = split_identifier(body)
let raw_id: String = id_split["id"]
if str_eq(raw_id, "") {
set_error("group declaration missing id", line_no, line)
return 0
}
let label_rest: String = str_trim(id_split["rest"])
let quoted: Map<String, Any> = parse_quoted(label_rest)
let label = raw_id
let qok: Bool = quoted["ok"]
if qok {
let label = quoted["value"]
}
open_group(raw_id, label, line_no)
1
}
fn parse_close_brace(line_no: Int) -> Int {
let frame: Map<String, Any> = close_group_frame()
let frame_ok: Bool = frame["ok"]
if !frame_ok {
set_error("unexpected `}` — no open group", line_no, "}")
return 0
}
store_group(frame["id"], frame["label"], frame["ids"])
1
}
fn parse_line_dispatch(line_no: Int, line: String) -> Int {
if line == "}" { return parse_close_brace(line_no) }
if str_starts_with(line, "title:") {
let after: String = str_trim(str_slice(line, 6, str_len(line)))
let q: Map<String, Any> = parse_quoted(after)
let qok: Bool = q["ok"]
if !qok {
set_error("expected quoted string after `title:`", line_no, line)
return 0
}
state_set("graph_title", q["value"])
return 1
}
if str_starts_with(line, "direction:") {
let after: String = str_trim(str_slice(line, 10, str_len(line)))
let dir: String = parse_direction(after)
if str_eq(dir, "") {
set_error("unknown direction — expected top-down, left-right, right-left, or bottom-up",
line_no, line)
return 0
}
state_set("graph_direction", dir)
return 1
}
if str_starts_with(line, "group ") {
let after: String = str_slice(line, 6, str_len(line))
return parse_group_open(line_no, line, after)
}
if is_edge_line(line) {
return parse_edge_stmt(line_no, line)
}
parse_node_stmt(line_no, line)
}
// Materialise accumulators into the final graph map
fn build_graph_value() -> Map<String, Any> {
let n_nodes: Int = st_get_int("node_count")
let nodes: [Map<String, Any>] = el_list_empty()
let i = 0
while i < n_nodes {
let s: String = int_to_str(i)
let node: Map<String, Any> = {
"id": state_get("node_id_" + s),
"label": state_get("node_label_" + s),
"shape": state_get("node_shape_" + s)
}
let nodes = native_list_append(nodes, node)
let i = i + 1
}
let n_edges: Int = st_get_int("edge_count")
let edges: [Map<String, Any>] = el_list_empty()
let i = 0
while i < n_edges {
let s: String = int_to_str(i)
let edge: Map<String, Any> = {
"from": state_get("edge_from_" + s),
"to": state_get("edge_to_" + s),
"label": state_get("edge_label_" + s),
"kind": state_get("edge_kind_" + s)
}
let edges = native_list_append(edges, edge)
let i = i + 1
}
let n_groups: Int = st_get_int("group_count")
let groups: [Map<String, Any>] = el_list_empty()
let i = 0
while i < n_groups {
let s: String = int_to_str(i)
let raw_ids: String = state_get("group_ids_" + s)
let id_list: [String] = list_decode(raw_ids)
let group: Map<String, Any> = {
"id": state_get("group_id_" + s),
"label": state_get("group_label_" + s),
"node_ids": id_list,
"direction": ""
}
let groups = native_list_append(groups, group)
let i = i + 1
}
{
"title": state_get("graph_title"),
"direction": state_get("graph_direction"),
"nodes": nodes,
"edges": edges,
"groups": groups
}
}
// Public entry point
fn arbor_parse(source: String) -> Map<String, Any> {
reset_state()
let lines: [Map<String, Any>] = preprocess(source)
let n: Int = el_list_len(lines)
let i = 0
let abort = false
while i < n {
if abort {
// skip error already recorded
} else {
let row: Map<String, Any> = get(lines, i)
let line_no: Int = row["no"]
let text: String = row["text"]
parse_line_dispatch(line_no, text)
if has_error() {
let abort = true
}
}
let i = i + 1
}
if !has_error() {
let depth: Int = st_get_int("group_stack_depth")
if depth > 0 {
let idx: Int = depth - 1
let id: String = state_get(group_frame_key(idx, "id"))
let line_no: Int = st_get_int(group_frame_key(idx, "line"))
set_error("unclosed group '" + id + "' — missing closing `}`",
line_no, "group " + id)
}
}
if has_error() {
return {
"error": state_get("parse_error"),
"line": st_get_int("parse_error_line"),
"text": state_get("parse_error_text")
}
}
build_graph_value()
}
// Smoke test
fn fail_msg(label: String, got: String, want: String) -> Int {
println("FAIL " + label + " got=[" + got + "] want=[" + want + "]")
state_set("smoke_failures", "1")
0
}
fn check_eq(label: String, got: String, want: String) -> Int {
if got == want {
println("ok " + label)
return 1
}
fail_msg(label, got, want)
}
// Helper: a graph map is in the error state iff it has a non-empty "error".
fn parse_failed(g: Map<String, Any>) -> Bool {
let m: String = g["error"]
if str_eq(m, "") { return false }
// map_get returns NULL for missing keys; str_eq treats two NULLs as equal
// and NULL vs "" as not equal guard explicitly.
if str_len(m) == 0 { return false }
true
}
let src1 = "title: \"Test\"\ndirection: left-right\n\napi [rounded] \"REST API\"\ndb [cylinder] \"Postgres\"\n\napi -> db \"reads\""
let g1: Map<String, Any> = arbor_parse(src1)
if parse_failed(g1) {
println("FAIL parse 1: " + g1["error"])
state_set("smoke_failures", "1")
}
check_eq("title parsed", g1["title"], "Test")
check_eq("direction parsed", g1["direction"], "left-right")
let nodes1: [Map<String, Any>] = g1["nodes"]
let nn1: Int = el_list_len(nodes1)
check_eq("two nodes", int_to_str(nn1), "2")
let edges1: [Map<String, Any>] = g1["edges"]
let ne1: Int = el_list_len(edges1)
check_eq("one edge", int_to_str(ne1), "1")
let e0: Map<String, Any> = get(edges1, 0)
check_eq("edge from", e0["from"], "api")
check_eq("edge to", e0["to"], "db")
check_eq("edge label", e0["label"], "reads")
check_eq("edge kind", e0["kind"], "solid")
let n0: Map<String, Any> = get(nodes1, 0)
check_eq("node 0 shape", n0["shape"], "rounded")
check_eq("node 0 label", n0["label"], "REST API")
// Test edge varieties
let src2 = "a \"A\"\nb \"B\"\na -> b\na --> b\na -/-> b\na <-> b"
let g2: Map<String, Any> = arbor_parse(src2)
let edges2: [Map<String, Any>] = g2["edges"]
check_eq("4 edges parsed", int_to_str(el_list_len(edges2)), "4")
let kinds = ""
let i = 0
while i < el_list_len(edges2) {
let e: Map<String, Any> = get(edges2, i)
let k: String = e["kind"]
let kinds = kinds + k + ","
let i = i + 1
}
check_eq("edge kinds", kinds, "solid,dashed,forbidden,bidirectional,")
// Groups
let src3 = "group core \"Application Core\" {\n api [rounded] \"REST API\"\n svc \"Business Logic\"\n}\nstandalone \"Out\""
let g3: Map<String, Any> = arbor_parse(src3)
let groups3: [Map<String, Any>] = g3["groups"]
check_eq("one group", int_to_str(el_list_len(groups3)), "1")
let grp0: Map<String, Any> = get(groups3, 0)
check_eq("group label", grp0["label"], "Application Core")
let gnids: [String] = grp0["node_ids"]
check_eq("group has 2 members", int_to_str(el_list_len(gnids)), "2")
let nodes3: [Map<String, Any>] = g3["nodes"]
check_eq("3 total nodes (incl standalone)",
int_to_str(el_list_len(nodes3)), "3")
// Error: unknown shape
let src4 = "node [hexagon] \"X\""
let g4: Map<String, Any> = arbor_parse(src4)
let err4: String = g4["error"]
if str_eq(err4, "") {
println("FAIL expected error for unknown shape")
state_set("smoke_failures", "1")
} else {
if str_contains(err4, "hexagon") {
println("ok error mentions hexagon: " + err4)
} else {
println("FAIL error wording: " + err4)
state_set("smoke_failures", "1")
}
}
// Error: unclosed group
let src5 = "group g \"G\" {\n a \"A\"\n"
let g5: Map<String, Any> = arbor_parse(src5)
let err5: String = g5["error"]
if str_eq(err5, "") {
println("FAIL expected unclosed-group error")
state_set("smoke_failures", "1")
} else {
if str_contains(err5, "unclosed") {
println("ok unclosed group detected")
} else {
println("FAIL unclosed error wording: " + err5)
state_set("smoke_failures", "1")
}
}
// Comments and inline comments
let src6 = "// header\na \"A\" // trailing\nb \"B\""
let g6: Map<String, Any> = arbor_parse(src6)
check_eq("comments stripped", int_to_str(el_list_len(g6["nodes"])), "2")
// Empty input
let g7: Map<String, Any> = arbor_parse("")
check_eq("empty graph nodes", int_to_str(el_list_len(g7["nodes"])), "0")
check_eq("empty graph default direction", g7["direction"], "top-down")
println("")
let f: String = state_get("smoke_failures")
if str_eq(f, "1") {
println("arbor-parse: FAILED")
exit_program(1)
} else {
println("arbor-parse: ok")
}
+21
View File
@@ -0,0 +1,21 @@
// arbor-render SVG renderer. Consumes a diagram graph + layout result and
// emits an SVG document. PNG rasterization is not provided in this vessel
// because the El runtime does not expose a vector-to-raster primitive yet
// (see report).
vessel "arbor-render" {
version "0.1.0"
description "SVG renderer for Arbor diagrams"
authors ["Neuron Technologies"]
edition "2026"
}
dependencies {
arbor-core "0.1"
arbor-layout "0.1"
}
build {
entry "src/main.el"
output "dist/"
}
+575
View File
@@ -0,0 +1,575 @@
// arbor-render SVG emission from a laid-out diagram.
//
// Entry point:
// fn arbor_render_svg(graph: Map, layout: Map, forbidden: [String]) -> String
//
// The graph is the lowered (diagram-form) shape produced by arbor-core /
// arbor-diagram (`title`, `direction`, `nodes`, `edges`, `groups`). The
// layout is whatever arbor-layout returned: `node_pos_<id>`, `node_size_<id>`,
// `group_bounds_<id>`, `node_ids`, `group_ids`, `canvas`.
//
// `forbidden` is a list of "from->to" key strings same format as
// arbor-core's collect_forbidden(). The Rust crate threaded a HashSet
// through; El threads a list and we linear-scan.
//
// SVG is text emission straightforward El. Every float coordinate is
// passed through format_float(_, 1) for stable output.
//
// PNG render is intentionally out of scope
// The Rust crate rasterises via resvg tiny_skia png. The El runtime
// today exposes no equivalent: there is no resvg, no usvg, no font rasterer,
// no PNG encoder, no path-fill code. fs_write writes text only there is
// no binary write primitive. arbor_render_png() returns an error map in El
// until the runtime grows a rasterer (see "runtime gaps" in the report).
// Colour palette (matches the Rust constants exactly)
fn col_node_fill() -> String { "#ffffff" }
fn col_node_stroke() -> String { "#334155" }
fn col_primary_fill() -> String { "#0052A0" }
fn col_primary_text() -> String { "#ffffff" }
fn col_node_text() -> String { "#0D0D14" }
fn col_edge() -> String { "#64748B" }
fn col_edge_forbidden() -> String { "#DC2626" }
fn col_group_fill() -> String { "rgba(0,0,0,0.03)" }
fn col_group_stroke() -> String { "#CBD5E1" }
fn col_group_text() -> String { "#64748B" }
fn col_edge_label() -> String { "#64748B" }
// XML escape
fn esc(s: String) -> String {
let r1: String = str_replace(s, "&", "&amp;")
let r2: String = str_replace(r1, "<", "&lt;")
let r3: String = str_replace(r2, ">", "&gt;")
let r4: String = str_replace(r3, "\"", "&quot;")
r4
}
// Float to "%.1f" the Rust pt() helper.
fn pt(v: el_val_t) -> String {
format_float(v, 1)
}
// Float arithmetic helpers float_to_int / int_to_float trip through Int,
// which is exact for the integer-valued floats used by the layout pass.
fn fadd(a: el_val_t, b: el_val_t) -> el_val_t {
let ai: Int = float_to_int(a)
let bi: Int = float_to_int(b)
int_to_float(ai + bi)
}
fn fsub(a: el_val_t, b: el_val_t) -> el_val_t {
let ai: Int = float_to_int(a)
let bi: Int = float_to_int(b)
int_to_float(ai - bi)
}
fn fdiv2(a: el_val_t) -> el_val_t {
let ai: Int = float_to_int(a)
int_to_float(ai / 2)
}
fn fmid(a: el_val_t, b: el_val_t) -> el_val_t {
fdiv2(fadd(a, b))
}
// forbidden-edge linear lookup
fn forbidden_key(from: String, to: String) -> String {
from + "->" + to
}
fn forbidden_contains(set: [String], src: String, dst: String) -> Bool {
let key: String = forbidden_key(src, dst)
let n: Int = el_list_len(set)
let i = 0
while i < n {
let s: String = get(set, i)
if str_eq(s, key) { return true }
let i = i + 1
}
false
}
// Arrow marker defs
fn arrow_defs() -> String {
let s = "\n <marker id=\"ah\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n"
let s = s + " <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"" + col_edge() + "\"/>\n"
let s = s + " </marker>\n"
let s = s + " <marker id=\"ah-bi\" markerWidth=\"10\" markerHeight=\"7\" refX=\"1\" refY=\"3.5\" orient=\"auto-start-reverse\">\n"
let s = s + " <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"" + col_edge() + "\"/>\n"
let s = s + " </marker>\n"
let s = s + " <marker id=\"ah-red\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n"
let s = s + " <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"" + col_edge_forbidden() + "\"/>\n"
let s = s + " </marker>"
s
}
// Node rendering
fn render_node(buf: String, node: Map<String, Any>, layout: Map<String, Any>) -> String {
let nid: String = node["id"]
let pos: Map<String, Any> = el_map_get(layout, "node_pos_" + nid)
let sz: Map<String, Any> = el_map_get(layout, "node_size_" + nid)
let cx: el_val_t = pos["x"]
let cy: el_val_t = pos["y"]
let w: el_val_t = sz["w"]
let h: el_val_t = sz["h"]
let x: el_val_t = fsub(cx, fdiv2(w))
let y: el_val_t = fsub(cy, fdiv2(h))
let fill_in: String = node["style_fill"]
let stroke_in: String = node["style_stroke"]
let color_in: String = node["style_color"]
let fill = col_node_fill()
if str_len(fill_in) > 0 { let fill = fill_in }
let stroke = col_node_stroke()
if str_len(stroke_in) > 0 { let stroke = stroke_in }
let text_col = col_node_text()
if str_len(color_in) > 0 { let text_col = color_in }
let shape: String = node["shape"]
let buf = buf
if str_eq(shape, "rectangle") {
let buf = buf + " <rect x=\"" + pt(x) + "\" y=\"" + pt(y)
let buf = buf + "\" width=\"" + pt(w) + "\" height=\"" + pt(h)
let buf = buf + "\" rx=\"4\" fill=\"" + fill + "\" stroke=\"" + stroke
let buf = buf + "\" stroke-width=\"1.5\"/>\n"
}
if str_eq(shape, "rounded_rect") {
let buf = buf + " <rect x=\"" + pt(x) + "\" y=\"" + pt(y)
let buf = buf + "\" width=\"" + pt(w) + "\" height=\"" + pt(h)
let buf = buf + "\" rx=\"20\" fill=\"" + fill + "\" stroke=\"" + stroke
let buf = buf + "\" stroke-width=\"1.5\"/>\n"
}
if str_eq(shape, "stadium") {
let buf = buf + " <rect x=\"" + pt(x) + "\" y=\"" + pt(y)
let buf = buf + "\" width=\"" + pt(w) + "\" height=\"" + pt(h)
let buf = buf + "\" rx=\"" + pt(fdiv2(h)) + "\" fill=\"" + fill
let buf = buf + "\" stroke=\"" + stroke + "\" stroke-width=\"1.5\"/>\n"
}
if str_eq(shape, "cylinder") {
// body: rect from y+ry to bottom; ry ≈ h/6 (Rust uses h*0.18, we use h/6
// to stay in integer arithmetic visually indistinguishable on the
// canvas sizes the layout produces).
let hi: Int = float_to_int(h)
let ry: el_val_t = int_to_float(hi / 6)
let body_y: el_val_t = fadd(y, ry)
let body_h: el_val_t = fsub(h, ry)
let buf = buf + " <rect x=\"" + pt(x) + "\" y=\"" + pt(body_y)
let buf = buf + "\" width=\"" + pt(w) + "\" height=\"" + pt(body_h)
let buf = buf + "\" fill=\"" + fill + "\" stroke=\"" + stroke + "\" stroke-width=\"1.5\"/>\n"
// top ellipse
let buf = buf + " <ellipse cx=\"" + pt(cx) + "\" cy=\"" + pt(body_y)
let buf = buf + "\" rx=\"" + pt(fdiv2(w)) + "\" ry=\"" + pt(ry)
let buf = buf + "\" fill=\"" + fill + "\" stroke=\"" + stroke + "\" stroke-width=\"1.5\"/>\n"
// bottom ellipse
let bot_y: el_val_t = fadd(y, h)
let buf = buf + " <ellipse cx=\"" + pt(cx) + "\" cy=\"" + pt(bot_y)
let buf = buf + "\" rx=\"" + pt(fdiv2(w)) + "\" ry=\"" + pt(ry)
let buf = buf + "\" fill=\"" + fill + "\" stroke=\"" + stroke + "\" stroke-width=\"1.5\"/>\n"
}
if str_eq(shape, "diamond") {
let hw: el_val_t = fdiv2(w)
let hh: el_val_t = fdiv2(h)
let buf = buf + " <polygon points=\""
let buf = buf + pt(cx) + "," + pt(fsub(cy, hh)) + " "
let buf = buf + pt(fadd(cx, hw)) + "," + pt(cy) + " "
let buf = buf + pt(cx) + "," + pt(fadd(cy, hh)) + " "
let buf = buf + pt(fsub(cx, hw)) + "," + pt(cy)
let buf = buf + "\" fill=\"" + fill + "\" stroke=\"" + stroke + "\" stroke-width=\"1.5\"/>\n"
}
// Label.
let label: String = node["label"]
let buf = buf + " <text x=\"" + pt(cx) + "\" y=\"" + pt(cy)
let buf = buf + "\" text-anchor=\"middle\" dominant-baseline=\"middle\""
let buf = buf + " class=\"arbor-node-label\" fill=\"" + text_col + "\">"
let buf = buf + esc(label) + "</text>\n"
// Sublabel Rust's DiagramNode stores Option<String>; El uses "" sentinel.
let sub: String = node["sublabel"]
if str_len(sub) > 0 {
let sub_y: el_val_t = fadd(cy, int_to_float(14))
let buf = buf + " <text x=\"" + pt(cx) + "\" y=\"" + pt(sub_y)
let buf = buf + "\" text-anchor=\"middle\" dominant-baseline=\"middle\""
let buf = buf + " class=\"arbor-node-label\" fill=\"" + text_col + "\" font-size=\"10\">"
let buf = buf + esc(sub) + "</text>\n"
}
buf
}
// Edge rendering
//
// We emit a straight line from one node centre to the other and let the
// browser draw it; the Rust crate renders cubic bezier paths but the runtime
// has no robust math layer, and the rectangles are large enough that
// straight edges read clearly. (See "runtime gaps".)
fn render_edge(buf: String, edge: Map<String, Any>, layout: Map<String, Any>, forbidden: [String]) -> String {
let from_id: String = edge["from"]
let to_id: String = edge["to"]
let from_pos: Map<String, Any> = el_map_get(layout, "node_pos_" + from_id)
let to_pos: Map<String, Any> = el_map_get(layout, "node_pos_" + to_id)
let fx: el_val_t = from_pos["x"]
let fy: el_val_t = from_pos["y"]
let tx: el_val_t = to_pos["x"]
let ty: el_val_t = to_pos["y"]
let is_forbidden: Bool = forbidden_contains(forbidden, from_id, to_id)
let stroke = col_edge()
if is_forbidden { let stroke = col_edge_forbidden() }
let line: String = edge["line"]
let arrow: String = edge["arrow"]
let dash_attr = ""
if str_eq(line, "dashed") { let dash_attr = " stroke-dasharray=\"5,3\"" }
if str_eq(line, "dotted") { let dash_attr = " stroke-dasharray=\"2,2\"" }
let marker_start = ""
if str_eq(arrow, "both") { let marker_start = " marker-start=\"url(#ah-bi)\"" }
if str_eq(arrow, "backward") { let marker_start = " marker-start=\"url(#ah-bi)\"" }
let marker_end = " marker-end=\"url(#ah)\""
if is_forbidden { let marker_end = " marker-end=\"url(#ah-red)\"" }
if str_eq(arrow, "none") { let marker_end = "" }
if str_eq(arrow, "backward") { let marker_end = "" }
let buf = buf + " <line x1=\"" + pt(fx) + "\" y1=\"" + pt(fy)
let buf = buf + "\" x2=\"" + pt(tx) + "\" y2=\"" + pt(ty)
let buf = buf + "\" stroke=\"" + stroke + "\" stroke-width=\"1.5\""
let buf = buf + dash_attr + marker_start + marker_end + "/>\n"
// Forbidden marker circle-X at midpoint.
if is_forbidden {
let mx: el_val_t = fmid(fx, tx)
let my: el_val_t = fmid(fy, ty)
let r: el_val_t = int_to_float(7)
let buf = buf + " <circle cx=\"" + pt(mx) + "\" cy=\"" + pt(my)
let buf = buf + "\" r=\"" + pt(r) + "\" fill=\"white\" stroke=\""
let buf = buf + col_edge_forbidden() + "\" stroke-width=\"1.5\"/>\n"
let off: el_val_t = int_to_float(4)
let buf = buf + " <line x1=\"" + pt(fsub(mx, off)) + "\" y1=\"" + pt(fsub(my, off))
let buf = buf + "\" x2=\"" + pt(fadd(mx, off)) + "\" y2=\"" + pt(fadd(my, off))
let buf = buf + "\" stroke=\"" + col_edge_forbidden() + "\" stroke-width=\"1.5\"/>\n"
let buf = buf + " <line x1=\"" + pt(fadd(mx, off)) + "\" y1=\"" + pt(fsub(my, off))
let buf = buf + "\" x2=\"" + pt(fsub(mx, off)) + "\" y2=\"" + pt(fadd(my, off))
let buf = buf + "\" stroke=\"" + col_edge_forbidden() + "\" stroke-width=\"1.5\"/>\n"
}
// Edge label
let label: String = edge["label"]
if str_len(label) > 0 {
let mx: el_val_t = fmid(fx, tx)
let my: el_val_t = fmid(fy, ty)
let lw: el_val_t = int_to_float(str_len(label) * 7 + 8)
let lh: el_val_t = int_to_float(16)
let buf = buf + " <rect x=\"" + pt(fsub(mx, fdiv2(lw))) + "\" y=\"" + pt(fsub(my, fdiv2(lh)))
let buf = buf + "\" width=\"" + pt(lw) + "\" height=\"" + pt(lh)
let buf = buf + "\" rx=\"3\" fill=\"white\" opacity=\"0.85\"/>\n"
let buf = buf + " <text x=\"" + pt(mx) + "\" y=\"" + pt(my)
let buf = buf + "\" text-anchor=\"middle\" dominant-baseline=\"middle\""
let buf = buf + " class=\"arbor-edge-label\">" + esc(label) + "</text>\n"
}
buf
}
// Group rendering
fn render_group(buf: String, group: Map<String, Any>, layout: Map<String, Any>) -> String {
let gid: String = group["id"]
let bounds: Map<String, Any> = el_map_get(layout, "group_bounds_" + gid)
// Layout may not have bounds for empty groups defensive.
let bx_check: el_val_t = bounds["x"]
if float_to_int(bx_check) == 0 {
// Could be a real 0; cheaper to skip via presence check on group_ids.
}
let bx: el_val_t = bounds["x"]
let by: el_val_t = bounds["y"]
let bw: el_val_t = bounds["w"]
let bh: el_val_t = bounds["h"]
let buf = buf + " <rect x=\"" + pt(bx) + "\" y=\"" + pt(by)
let buf = buf + "\" width=\"" + pt(bw) + "\" height=\"" + pt(bh)
let buf = buf + "\" rx=\"8\" fill=\"" + col_group_fill() + "\" stroke=\""
let buf = buf + col_group_stroke() + "\" stroke-width=\"1\" stroke-dasharray=\"4,3\"/>\n"
// Group label in the top-left corner.
let lx: el_val_t = fadd(bx, int_to_float(8))
let ly: el_val_t = fadd(by, int_to_float(14))
let label: String = group["label"]
let buf = buf + " <text x=\"" + pt(lx) + "\" y=\"" + pt(ly)
let buf = buf + "\" class=\"arbor-group-label\">" + esc(label) + "</text>\n"
buf
}
// Public entry point
fn arbor_render_svg(graph: Map<String, Any>, layout: Map<String, Any>, forbidden: [String]) -> String {
let canvas: Map<String, Any> = el_map_get(layout, "canvas")
let cw: el_val_t = canvas["w"]
let ch: el_val_t = canvas["h"]
let buf = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" + pt(cw)
let buf = buf + "\" height=\"" + pt(ch) + "\" viewBox=\"0 0 " + pt(cw) + " " + pt(ch) + "\">\n"
let buf = buf + " <defs>"
let buf = buf + arrow_defs()
let buf = buf + "\n <style>\n"
let buf = buf + " .arbor-node-label { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 13px; }\n"
let buf = buf + " .arbor-group-label { font-family: 'Helvetica Neue', Helvetica, Arial, monospace; font-size: 10px; fill: " + col_group_text() + "; letter-spacing: 0.08em; }\n"
let buf = buf + " .arbor-edge-label { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 11px; fill: " + col_edge_label() + "; }\n"
let buf = buf + " </style>\n"
let buf = buf + " </defs>\n"
// Groups first (behind everything).
let buf = buf + " <!-- Groups -->\n"
let groups: [Map<String, Any>] = graph["groups"]
let gn: Int = el_list_len(groups)
let i = 0
while i < gn {
let g: Map<String, Any> = get(groups, i)
let gid: String = g["id"]
// Only render groups the layout actually placed.
let gids: [String] = el_map_get(layout, "group_ids")
let placed = false
let j = 0
while j < el_list_len(gids) {
if str_eq(get(gids, j), gid) { let placed = true }
let j = j + 1
}
if placed {
let buf = render_group(buf, g, layout)
}
let i = i + 1
}
// Edges
let buf = buf + " <!-- Edges -->\n"
let edges: [Map<String, Any>] = graph["edges"]
let en: Int = el_list_len(edges)
let i = 0
while i < en {
let e: Map<String, Any> = get(edges, i)
let buf = render_edge(buf, e, layout, forbidden)
let i = i + 1
}
// Nodes
let buf = buf + " <!-- Nodes -->\n"
let nodes: [Map<String, Any>] = graph["nodes"]
let nn: Int = el_list_len(nodes)
let i = 0
while i < nn {
let n: Map<String, Any> = get(nodes, i)
let buf = render_node(buf, n, layout)
let i = i + 1
}
// Title
let title: String = graph["title"]
if str_len(title) > 0 {
let title_x: el_val_t = fdiv2(cw)
let buf = buf + " <text x=\"" + pt(title_x) + "\" y=\"22\" text-anchor=\"middle\""
let buf = buf + " font-family=\"'Helvetica Neue', Helvetica, Arial, sans-serif\""
let buf = buf + " font-size=\"15\" font-weight=\"600\" fill=\"" + col_node_text() + "\">"
let buf = buf + esc(title) + "</text>\n"
}
let buf = buf + "</svg>\n"
buf
}
// PNG not implemented; the runtime has no SVG rasterizer or PNG encoder.
// Returns an error map that callers can inspect via map["error"].
fn arbor_render_png(graph: Map<String, Any>, layout: Map<String, Any>, forbidden: [String]) -> Map<String, Any> {
{
"error": "PNG rasterization not available in El runtime — install a runtime image library or use the Rust binary"
}
}
// Smoke test
fn fail(label: String, msg: String) -> Int {
println("FAIL " + label + ": " + msg)
state_set("smoke_failures", "1")
0
}
fn check_contains(label: String, haystack: String, needle: String) -> Int {
if str_contains(haystack, needle) {
println("ok " + label)
return 1
}
fail(label, "missing [" + needle + "]")
}
fn check_not_contains(label: String, haystack: String, needle: String) -> Int {
if str_contains(haystack, needle) {
return fail(label, "should not contain [" + needle + "]")
}
println("ok " + label)
1
}
fn make_test_node(id: String, label: String, shape: String) -> Map<String, Any> {
{
"id": id, "label": label, "sublabel": "",
"shape": shape,
"style_fill": "", "style_stroke": "", "style_color": ""
}
}
fn make_test_edge(src: String, dst: String, line: String, arrow: String, label: String) -> Map<String, Any> {
{
"from": src, "to": dst, "label": label,
"line": line, "arrow": arrow
}
}
fn make_test_pos(x: Int, y: Int) -> Map<String, Any> {
{ "x": int_to_float(x), "y": int_to_float(y) }
}
fn make_test_size(w: Int, h: Int) -> Map<String, Any> {
{ "w": int_to_float(w), "h": int_to_float(h) }
}
// Build a minimal layout map by hand.
fn build_layout(node_ids: [String], group_ids: [String], cw: Int, ch: Int) -> Map<String, Any> {
let r: Map<String, Any> = el_map_new(0)
let r = el_map_set(r, "node_ids", node_ids)
let r = el_map_set(r, "group_ids", group_ids)
let r = el_map_set(r, "canvas", { "w": int_to_float(cw), "h": int_to_float(ch) })
r
}
let n_a: Map<String, Any> = make_test_node("a", "Node A", "rectangle")
let n_b: Map<String, Any> = make_test_node("b", "Node B", "rectangle")
let e_ab: Map<String, Any> = make_test_edge("a", "b", "solid", "forward", "")
let nodes: [Map<String, Any>] = native_list_empty()
let nodes = native_list_append(nodes, n_a)
let nodes = native_list_append(nodes, n_b)
let edges: [Map<String, Any>] = native_list_empty()
let edges = native_list_append(edges, e_ab)
let groups: [Map<String, Any>] = native_list_empty()
let g: Map<String, Any> = {
"title": "Test", "direction": "top-down",
"nodes": nodes, "edges": edges, "groups": groups
}
let nid_list: [String] = native_list_empty()
let nid_list = native_list_append(nid_list, "a")
let nid_list = native_list_append(nid_list, "b")
let gid_list: [String] = native_list_empty()
let layout: Map<String, Any> = build_layout(nid_list, gid_list, 400, 300)
let layout = el_map_set(layout, "node_pos_a", make_test_pos(100, 60))
let layout = el_map_set(layout, "node_pos_b", make_test_pos(100, 200))
let layout = el_map_set(layout, "node_size_a", make_test_size(120, 40))
let layout = el_map_set(layout, "node_size_b", make_test_size(120, 40))
let forbidden: [String] = native_list_empty()
let svg: String = arbor_render_svg(g, layout, forbidden)
check_contains("svg starts with <svg", svg, "<svg xmlns=")
check_contains("svg ends with </svg>", svg, "</svg>")
check_contains("svg contains node label", svg, "Node A")
check_contains("svg contains title", svg, ">Test</text>")
check_contains("svg has rect for rectangle node", svg, "<rect")
check_contains("svg has line for edge", svg, "<line")
check_contains("svg has arrow marker def", svg, "id=\"ah\"")
// Escape test
let n_esc: Map<String, Any> = make_test_node("x", "A & B <C>", "rectangle")
let nodes2: [Map<String, Any>] = native_list_empty()
let nodes2 = native_list_append(nodes2, n_esc)
let g2: Map<String, Any> = {
"title": "Test <Title>", "direction": "top-down",
"nodes": nodes2, "edges": native_list_empty(), "groups": native_list_empty()
}
let nid2: [String] = native_list_empty()
let nid2 = native_list_append(nid2, "x")
let layout2: Map<String, Any> = build_layout(nid2, native_list_empty(), 200, 100)
let layout2 = el_map_set(layout2, "node_pos_x", make_test_pos(80, 40))
let layout2 = el_map_set(layout2, "node_size_x", make_test_size(120, 40))
let svg2: String = arbor_render_svg(g2, layout2, native_list_empty())
check_contains("escapes ampersand", svg2, "&amp;")
check_contains("escapes <", svg2, "&lt;")
check_not_contains("no raw <C>", svg2, "<C>")
// Forbidden edge
let e_fb: Map<String, Any> = make_test_edge("a", "b", "solid", "forward", "")
let edges3: [Map<String, Any>] = native_list_empty()
let edges3 = native_list_append(edges3, e_fb)
let g3: Map<String, Any> = {
"title": "F", "direction": "top-down",
"nodes": nodes, "edges": edges3, "groups": native_list_empty()
}
let fb: [String] = native_list_empty()
let fb = native_list_append(fb, forbidden_key("a", "b"))
let svg3: String = arbor_render_svg(g3, layout, fb)
check_contains("forbidden uses red marker", svg3, "ah-red")
check_contains("forbidden colour present", svg3, col_edge_forbidden())
// Diamond shape polygon
let n_d: Map<String, Any> = make_test_node("d", "Decide", "diamond")
let g4: Map<String, Any> = {
"title": "", "direction": "top-down",
"nodes": native_list_append(native_list_empty(), n_d),
"edges": native_list_empty(), "groups": native_list_empty()
}
let nid4: [String] = native_list_append(native_list_empty(), "d")
let layout4: Map<String, Any> = build_layout(nid4, native_list_empty(), 200, 100)
let layout4 = el_map_set(layout4, "node_pos_d", make_test_pos(80, 50))
let layout4 = el_map_set(layout4, "node_size_d", make_test_size(120, 40))
let svg4: String = arbor_render_svg(g4, layout4, native_list_empty())
check_contains("diamond uses polygon", svg4, "<polygon")
// Cylinder shape ellipses
let n_cy: Map<String, Any> = make_test_node("cy", "DB", "cylinder")
let g5: Map<String, Any> = {
"title": "", "direction": "top-down",
"nodes": native_list_append(native_list_empty(), n_cy),
"edges": native_list_empty(), "groups": native_list_empty()
}
let nid5: [String] = native_list_append(native_list_empty(), "cy")
let layout5: Map<String, Any> = build_layout(nid5, native_list_empty(), 200, 100)
let layout5 = el_map_set(layout5, "node_pos_cy", make_test_pos(80, 50))
let layout5 = el_map_set(layout5, "node_size_cy", make_test_size(120, 40))
let svg5: String = arbor_render_svg(g5, layout5, native_list_empty())
check_contains("cylinder uses ellipse", svg5, "<ellipse")
// Dashed edge
let e_dash: Map<String, Any> = make_test_edge("a", "b", "dashed", "forward", "")
let g6: Map<String, Any> = {
"title": "", "direction": "top-down",
"nodes": nodes, "edges": native_list_append(native_list_empty(), e_dash),
"groups": native_list_empty()
}
let svg6: String = arbor_render_svg(g6, layout, native_list_empty())
check_contains("dashed line dasharray", svg6, "stroke-dasharray=\"5,3\"")
// PNG returns an error map
let png: Map<String, Any> = arbor_render_png(g, layout, native_list_empty())
let err: String = png["error"]
if str_len(err) > 0 {
println("ok PNG returns error map")
} else {
println("FAIL PNG should have returned error")
state_set("smoke_failures", "1")
}
println("")
let f: String = state_get("smoke_failures")
if str_eq(f, "1") {
println("arbor-render: FAILED")
exit_program(1)
} else {
println("arbor-render: ok")
}
+1
View File
@@ -0,0 +1 @@
import "language-profile.el"
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
import "dedup_test_a_nodedup.el"
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
extern fn fn_a(x: String) -> String
+1
View File
@@ -0,0 +1 @@
extern fn fn_a(x: String) -> String
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
extern fn fn_a(x: String) -> String
+6
View File
@@ -0,0 +1,6 @@
import "language-profile.el"
import "dedup_test_a.el"
fn main_fn(x: String) -> String {
return x
}
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
import "dedup_test_a.el"
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
import "dedup_test_a_notail.el"
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
extern fn fn_a(x: String) -> String
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
extern fn fn_b(x: String) -> String
+761
View File
@@ -0,0 +1,761 @@
// big language-profile for testing
fn lang_profile_big0(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big0(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big0("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big1(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big1(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big1("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big2(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big2(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big2("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big3(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big3(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big3("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big4(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big4(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big4("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big5(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big5(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big5("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big6(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big6(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big6("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big7(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big7(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big7("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big8(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big8(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big8("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big9(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big9(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big9("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big10(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big10(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big10("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big11(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big11(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big11("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big12(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big12(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big12("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big13(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big13(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big13("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big14(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big14(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big14("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big15(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big15(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big15("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big16(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big16(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big16("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big17(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big17(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big17("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big18(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big18(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big18("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
fn lang_profile_big19(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "code")
let r = native_list_append(r, code)
let r = native_list_append(r, "word_order")
let r = native_list_append(r, word_order)
let r = native_list_append(r, "morph_type")
let r = native_list_append(r, morph_type)
let r = native_list_append(r, "has_case")
let r = native_list_append(r, has_case)
let r = native_list_append(r, "has_gender")
let r = native_list_append(r, has_gender)
let r = native_list_append(r, "script_dir")
let r = native_list_append(r, script_dir)
let r = native_list_append(r, "agreement")
let r = native_list_append(r, agreement)
let r = native_list_append(r, "null_subject")
let r = native_list_append(r, null_subject)
return r
}
fn lang_get_big19(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn lang_profile_en() -> [String] {
return lang_profile_big19("en", "SVO", "fusional", "false", "false", "ltr", "number;person", "false")
}
+40
View File
@@ -0,0 +1,40 @@
import "language-profile.el"
extern fn es_pluralize(noun: String) -> String
extern fn es_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn fr_pluralize(noun: String) -> String
extern fn fr_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn de_noun_plural(noun: String, gender: String) -> String
extern fn de_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn ru_noun_case(noun: String, gender: String, gram_case: String, number: String) -> String
extern fn ru_conjugate(verb: String, tense: String, person: String, number: String, gender: String) -> String
extern fn ja_conjugate(dict_form: String, form: String) -> String
extern fn fi_apply_case(noun: String, gram_case: String, number: String) -> String
extern fn fi_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn ar_sound_plural(noun: String, gender: String) -> String
extern fn ar_conjugate(verb: String, tense: String, person: String, gender: String, number: String) -> String
extern fn hi_noun_direct(noun: String, gender: String, number: String) -> String
extern fn hi_gender(noun: String) -> String
extern fn hi_conjugate(verb: String, tense: String, person: String, gender: String, number: String) -> String
extern fn sw_noun_plural(noun: String) -> String
extern fn sw_conjugate(verb: String, person: String, number: String, noun_class: String, tense: String) -> String
extern fn la_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn he_conjugate(verb: String, tense: String, person: String, gender: String, number: String) -> String
extern fn grc_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn ang_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn sa_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn got_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn non_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn enm_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn pi_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn fro_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn goh_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn sga_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn txb_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn peo_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn akk_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn uga_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn egy_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn sux_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn gez_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn cop_conjugate(verb: String, tense: String, person: String, number: String) -> String
+3
View File
@@ -0,0 +1,3 @@
fn morph_tiny(x: String) -> String {
return x
}
+2
View File
@@ -0,0 +1,2 @@
import "language-profile.el"
extern fn es_pluralize(noun: String) -> String
+1
View File
@@ -0,0 +1 @@
extern fn es_pluralize(noun: String) -> String
+312
View File
@@ -0,0 +1,312 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "morph_externsonly.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+312
View File
@@ -0,0 +1,312 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "grammar.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+312
View File
@@ -0,0 +1,312 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "morphology.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+312
View File
@@ -0,0 +1,312 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "morph_tiny.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+313
View File
@@ -0,0 +1,313 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "test_empty_gram.el"
import "test_empty_gram.el"
import "test_empty_gram.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+311
View File
@@ -0,0 +1,311 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile-big.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+313
View File
@@ -0,0 +1,313 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "test_empty_gram.el"
import "test_empty_gram.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+313
View File
@@ -0,0 +1,313 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "morphology.el"
import "test_empty_gram.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+312
View File
@@ -0,0 +1,312 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "morphology.el"
import "grammar.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+310
View File
@@ -0,0 +1,310 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+312
View File
@@ -0,0 +1,312 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "one_extern_nolp.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+312
View File
@@ -0,0 +1,312 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "one_extern.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+311
View File
@@ -0,0 +1,311 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+311
View File
@@ -0,0 +1,311 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "grammar.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+311
View File
@@ -0,0 +1,311 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "morphology.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+313
View File
@@ -0,0 +1,313 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "morphology.el"
import "test_tiny_gram.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+313
View File
@@ -0,0 +1,313 @@
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
//
// The realizer is now language-agnostic. It reads the "lang" field from the
// GramSpec to resolve a language profile, then dispatches word order, question
// formation, and morphology through the engine functions in grammar.el and
// morphology.el.
//
// English remains the default (backward compatible) when no "lang" key is set.
//
// Realization pipeline per call:
// 1. Extract lang code -> resolve profile
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
// 3. Compute person/number from agent (English heuristic; other languages TBD)
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
// 5. Choose question strategy from gram_question_strategy(profile)
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
// 7. Capitalize and terminate
//
// Depends on: morphology (morph_conjugate, agree_determiner)
// grammar (gram_order_constituents, gram_question_strategy,
// gram_build_vp, build_np, build_pp, slots_get)
// language-profile (lang_from_code, lang_get, ...)
import "language-profile.el"
import "ext_a.el"
import "ext_b.el"
// Agent agreement analysis
//
// Person and number are inferred from English pronouns. For other languages
// the grammatical person/number should come from the Engram vocabulary node
// for the subject; here we use a heuristic that is correct for English and
// passable for languages where the same pronoun strings are used.
fn agent_person(agent: String) -> String {
if str_eq(agent, "I") { return "first" }
if str_eq(agent, "me") { return "first" }
if str_eq(agent, "we") { return "first" }
if str_eq(agent, "us") { return "first" }
if str_eq(agent, "you") { return "second" }
return "third"
}
fn agent_number(agent: String) -> String {
if str_eq(agent, "I") { return "singular" }
if str_eq(agent, "me") { return "singular" }
if str_eq(agent, "he") { return "singular" }
if str_eq(agent, "him") { return "singular" }
if str_eq(agent, "she") { return "singular" }
if str_eq(agent, "her") { return "singular" }
if str_eq(agent, "it") { return "singular" }
if str_eq(agent, "you") { return "singular" }
if str_eq(agent, "we") { return "plural" }
if str_eq(agent, "us") { return "plural" }
if str_eq(agent, "they") { return "plural" }
if str_eq(agent, "them") { return "plural" }
return "singular"
}
// NP realization
fn realize_np(referent: String, number: String) -> String {
return referent
}
// VP realization
//
// Returns [main_verb_surface, aux_surface_or_empty].
// Delegates conjugation to morph_conjugate with the language profile.
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
let empty_aux: String = ""
if str_eq(tense, "future") {
// Future: modal "will" + base (English) or language-specific future marker.
// For isolating/agglutinative languages the future marker is also the
// base form (morph_conjugate returns base); the surface "will" only appears
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
let code: String = lang_get(profile, "code")
if str_eq(code, "en") {
let result: [String] = native_list_empty()
let result = native_list_append(result, base_verb)
let result = native_list_append(result, "will")
return result
}
// Other languages: conjugate normally (engine returns base form for
// languages without loaded Engram suffix data).
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
if str_eq(aspect, "progressive") {
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, gerund)
let result = native_list_append(result, be_aux)
return result
}
if str_eq(aspect, "perfect") {
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
let have_form: String = morph_conjugate("have", tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, pp)
let result = native_list_append(result, have_form)
return result
}
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
let result: [String] = native_list_empty()
let result = native_list_append(result, surf)
let result = native_list_append(result, empty_aux)
return result
}
// Question formation
//
// Strategy is resolved from gram_question_strategy(profile):
//
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
// "inversion" (fr, de) - subject-verb inversion.
// realize_question_lang: build the question surface string for any language.
// Returns the complete surface string (without final punctuation).
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
let strategy: String = gram_question_strategy(profile)
let code: String = lang_get(profile, "code")
// do-support (English)
if str_eq(strategy, "do-support") {
if str_eq(aspect, "progressive") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
let gerund: String = native_list_get(vp_pair, 0)
let be_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, gerund)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
if str_eq(aspect, "perfect") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
let pp: String = native_list_get(vp_pair, 0)
let have_aux: String = native_list_get(vp_pair, 1)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, have_aux)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, pp)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// Simple: do-support
if str_eq(predicate, "be") {
let be_form: String = morph_conjugate("be", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, be_form)
let parts = native_list_append(parts, agent)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
let do_form: String = morph_conjugate("do", tense, person, number, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, do_form)
let parts = native_list_append(parts, agent)
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// particle (ja, hi, fi)
// Build in statement order, then append the question particle.
if str_eq(strategy, "particle") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let loc_part: String = ""
if !str_eq(location, "") {
let loc_part = core + " " + location
} else {
let loc_part = core
}
// Language-specific question particles
if str_eq(code, "ja") { return loc_part + "" }
if str_eq(code, "hi") { return loc_part + " क्या" }
if str_eq(code, "fi") { return loc_part + "-ko" }
return loc_part + "?"
}
// inversion (fr, de)
if str_eq(strategy, "inversion") {
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
// Inversion: Verb-Subject-Object order
let parts: [String] = native_list_empty()
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, aux_s)
} else {
let parts = native_list_append(parts, verb_s)
}
let parts = native_list_append(parts, agent)
if !str_eq(aux_s, "") {
let parts = native_list_append(parts, verb_s)
}
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
return str_join(parts, " ")
}
// intonation (zh, es, ar, ru, sw) statement order, "?" added by caller
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_s: String = native_list_get(vp_pair, 0)
let aux_s: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
if !str_eq(location, "") {
return core + " " + location
}
return core
}
// Capitalization and punctuation
fn capitalize_first(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return s
}
let first: String = str_slice(s, 0, 1)
let rest: String = str_slice(s, 1, n)
return str_to_upper(first) + rest
}
fn add_punct(s: String, intent: String) -> String {
if str_eq(intent, "question") { return s + "?" }
return s + "."
}
// Main realization entry point
fn realize_lang(form: [String], profile: [String]) -> String {
let intent: String = slots_get(form, "intent")
let agent: String = slots_get(form, "agent")
let predicate: String = slots_get(form, "predicate")
let patient: String = slots_get(form, "patient")
let location: String = slots_get(form, "location")
let tense_raw: String = slots_get(form, "tense")
let aspect_raw: String= slots_get(form, "aspect")
let tense: String = tense_raw
if str_eq(tense, "") { let tense = "present" }
let aspect: String = aspect_raw
if str_eq(aspect, "") { let aspect = "simple" }
let person: String = agent_person(agent)
let number: String = agent_number(agent)
// Command (imperative)
if str_eq(intent, "command") {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, predicate)
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "command")
}
// Question
if str_eq(intent, "question") {
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
return add_punct(capitalize_first(surface), "question")
}
// Assertion (declarative)
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
let verb_surf: String = native_list_get(vp_pair, 0)
let aux_surf: String = native_list_get(vp_pair, 1)
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, core)
if !str_eq(location, "") {
let parts = native_list_append(parts, location)
}
let sentence: String = str_join(parts, " ")
return add_punct(capitalize_first(sentence), "assert")
}
// realize: backward-compatible English entry point (original signature).
fn realize(form: [String]) -> String {
let lang_code: String = slots_get(form, "lang")
if str_eq(lang_code, "") {
return realize_lang(form, lang_default())
}
return realize_lang(form, lang_from_code(lang_code))
}
+311
View File
@@ -0,0 +1,311 @@
// semantics.el - Semantic layer: SemFrame -> GramSpec (slot map for realizer).
//
// Bridges from intent/meaning representation to the grammar layer.
// A SemFrame is a slot map ([String] key-value list) with these keys:
//
// "intent" - "assert" | "query" | "describe" | "greet"
// "subject" - subject referent (pronoun or noun phrase)
// "object" - object referent (optional, "" if absent)
// "modifiers" - semicolon-separated modifier strings (e.g. "in the park;quickly")
// "lang" - ISO 639-1 language code (optional, defaults to "en")
//
// sem_to_spec converts a SemFrame into a realizer slot map ready for realize().
// sem_realize is the end-to-end shortcut: frame -> realized text.
//
// All existing function signatures are preserved; new *_lang variants add an
// explicit lang_code parameter.
//
// Depends on: grammar (slots_*, realize), language-profile (lang_from_code)
import "test_empty_gram.el"
import "test_empty_gram.el"
import "test_empty_gram.el"
// SemFrame constructors
// Build a SemFrame with all four core fields. Language defaults to "en".
fn sem_frame(intent: String, subject: String, obj: String, modifiers: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "intent")
let r = native_list_append(r, intent)
let r = native_list_append(r, "subject")
let r = native_list_append(r, subject)
let r = native_list_append(r, "object")
let r = native_list_append(r, obj)
let r = native_list_append(r, "modifiers")
let r = native_list_append(r, modifiers)
let r = native_list_append(r, "lang")
let r = native_list_append(r, "en")
return r
}
// Build a SemFrame with an explicit language code.
fn sem_frame_lang(intent: String, subject: String, obj: String, modifiers: String, lang_code: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "intent")
let r = native_list_append(r, intent)
let r = native_list_append(r, "subject")
let r = native_list_append(r, subject)
let r = native_list_append(r, "object")
let r = native_list_append(r, obj)
let r = native_list_append(r, "modifiers")
let r = native_list_append(r, modifiers)
let r = native_list_append(r, "lang")
let r = native_list_append(r, lang_code)
return r
}
// Convenience: no object, no modifiers, English.
fn sem_frame_simple(intent: String, subject: String) -> [String] {
return sem_frame(intent, subject, "", "")
}
// Convenience: with object, no modifiers, English.
fn sem_frame_obj(intent: String, subject: String, obj: String) -> [String] {
return sem_frame(intent, subject, obj, "")
}
// SemFrame field accessors
fn sem_intent(frame: [String]) -> String {
return slots_get(frame, "intent")
}
fn sem_subject(frame: [String]) -> String {
return slots_get(frame, "subject")
}
fn sem_object(frame: [String]) -> String {
return slots_get(frame, "object")
}
fn sem_modifiers(frame: [String]) -> String {
return slots_get(frame, "modifiers")
}
fn sem_lang(frame: [String]) -> String {
let code: String = slots_get(frame, "lang")
if str_eq(code, "") {
return "en"
}
return code
}
// Modifier helpers
fn sem_first_modifier(mods: String) -> String {
let n: Int = str_len(mods)
if n == 0 {
return ""
}
let i: Int = 0
let running: Bool = true
while running {
if i >= n {
let running = false
} else {
let c: String = str_slice(mods, i, i + 1)
if str_eq(c, ";") {
let running = false
} else {
let i = i + 1
}
}
}
return str_slice(mods, 0, i)
}
// Intent mapping
fn sem_intent_to_realize(intent: String) -> String {
if str_eq(intent, "assert") { return "assert" }
if str_eq(intent, "query") { return "question" }
if str_eq(intent, "describe") { return "assert" }
if str_eq(intent, "greet") { return "greet" }
return "assert"
}
// sem_to_spec: SemFrame -> realizer slot map
//
// The "lang" key from the SemFrame is forwarded into the GramSpec so that
// realize() can resolve the correct language profile.
fn sem_to_spec(frame: [String]) -> [String] {
let intent: String = sem_intent(frame)
let subject: String = sem_subject(frame)
let obj: String = sem_object(frame)
let mods: String = sem_modifiers(frame)
let lang_code: String = sem_lang(frame)
let location: String = sem_first_modifier(mods)
if str_eq(intent, "greet") {
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, "greet")
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, "present")
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, "simple")
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
if str_eq(intent, "describe") {
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, "assert")
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, "be")
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, "present")
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, "simple")
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
let realize_intent: String = sem_intent_to_realize(intent)
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, realize_intent)
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, "present")
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, "simple")
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
// sem_to_spec_full
fn sem_to_spec_full(frame: [String], verb: String, tense: String, aspect: String) -> [String] {
let intent: String = sem_intent(frame)
let subject: String = sem_subject(frame)
let obj: String = sem_object(frame)
let mods: String = sem_modifiers(frame)
let lang_code: String = sem_lang(frame)
let location: String = sem_first_modifier(mods)
if str_eq(intent, "greet") {
return sem_to_spec(frame)
}
if str_eq(intent, "describe") {
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, "assert")
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, "be")
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, tense)
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, aspect)
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
let realize_intent: String = sem_intent_to_realize(intent)
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, realize_intent)
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, verb)
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, tense)
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, aspect)
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
// Greet realization helper
fn sem_realize_greet(subject: String) -> String {
if str_eq(subject, "") {
return "Hello."
}
return "Hello, " + subject + "."
}
// sem_realize: SemFrame -> text
fn sem_realize(frame: [String]) -> String {
let intent: String = sem_intent(frame)
if str_eq(intent, "greet") {
return sem_realize_greet(sem_subject(frame))
}
let spec: [String] = sem_to_spec(frame)
return realize(spec)
}
// sem_realize_full
fn sem_realize_full(frame: [String], verb: String, tense: String, aspect: String) -> String {
let intent: String = sem_intent(frame)
if str_eq(intent, "greet") {
return sem_realize_greet(sem_subject(frame))
}
let spec: [String] = sem_to_spec_full(frame, verb, tense, aspect)
return realize(spec)
}
// sem_realize_lang: realize in an explicitly specified language
//
// Convenience for callers that want to specify the output language without
// constructing a full SemFrame. The lang_code overrides whatever "lang" is
// set in the frame.
fn sem_realize_lang(frame: [String], lang_code: String) -> String {
let intent: String = sem_intent(frame)
if str_eq(intent, "greet") {
return sem_realize_greet(sem_subject(frame))
}
// Inject the lang_code into the frame before converting to spec.
let patched: [String] = slots_set(frame, "lang", lang_code)
let spec: [String] = sem_to_spec(patched)
return realize(spec)
}
+308
View File
@@ -0,0 +1,308 @@
// semantics.el - Semantic layer: SemFrame -> GramSpec (slot map for realizer).
//
// Bridges from intent/meaning representation to the grammar layer.
// A SemFrame is a slot map ([String] key-value list) with these keys:
//
// "intent" - "assert" | "query" | "describe" | "greet"
// "subject" - subject referent (pronoun or noun phrase)
// "object" - object referent (optional, "" if absent)
// "modifiers" - semicolon-separated modifier strings (e.g. "in the park;quickly")
// "lang" - ISO 639-1 language code (optional, defaults to "en")
//
// sem_to_spec converts a SemFrame into a realizer slot map ready for realize().
// sem_realize is the end-to-end shortcut: frame -> realized text.
//
// All existing function signatures are preserved; new *_lang variants add an
// explicit lang_code parameter.
//
// Depends on: grammar (slots_*, realize), language-profile (lang_from_code)
// SemFrame constructors
// Build a SemFrame with all four core fields. Language defaults to "en".
fn sem_frame(intent: String, subject: String, obj: String, modifiers: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "intent")
let r = native_list_append(r, intent)
let r = native_list_append(r, "subject")
let r = native_list_append(r, subject)
let r = native_list_append(r, "object")
let r = native_list_append(r, obj)
let r = native_list_append(r, "modifiers")
let r = native_list_append(r, modifiers)
let r = native_list_append(r, "lang")
let r = native_list_append(r, "en")
return r
}
// Build a SemFrame with an explicit language code.
fn sem_frame_lang(intent: String, subject: String, obj: String, modifiers: String, lang_code: String) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "intent")
let r = native_list_append(r, intent)
let r = native_list_append(r, "subject")
let r = native_list_append(r, subject)
let r = native_list_append(r, "object")
let r = native_list_append(r, obj)
let r = native_list_append(r, "modifiers")
let r = native_list_append(r, modifiers)
let r = native_list_append(r, "lang")
let r = native_list_append(r, lang_code)
return r
}
// Convenience: no object, no modifiers, English.
fn sem_frame_simple(intent: String, subject: String) -> [String] {
return sem_frame(intent, subject, "", "")
}
// Convenience: with object, no modifiers, English.
fn sem_frame_obj(intent: String, subject: String, obj: String) -> [String] {
return sem_frame(intent, subject, obj, "")
}
// SemFrame field accessors
fn sem_intent(frame: [String]) -> String {
return slots_get(frame, "intent")
}
fn sem_subject(frame: [String]) -> String {
return slots_get(frame, "subject")
}
fn sem_object(frame: [String]) -> String {
return slots_get(frame, "object")
}
fn sem_modifiers(frame: [String]) -> String {
return slots_get(frame, "modifiers")
}
fn sem_lang(frame: [String]) -> String {
let code: String = slots_get(frame, "lang")
if str_eq(code, "") {
return "en"
}
return code
}
// Modifier helpers
fn sem_first_modifier(mods: String) -> String {
let n: Int = str_len(mods)
if n == 0 {
return ""
}
let i: Int = 0
let running: Bool = true
while running {
if i >= n {
let running = false
} else {
let c: String = str_slice(mods, i, i + 1)
if str_eq(c, ";") {
let running = false
} else {
let i = i + 1
}
}
}
return str_slice(mods, 0, i)
}
// Intent mapping
fn sem_intent_to_realize(intent: String) -> String {
if str_eq(intent, "assert") { return "assert" }
if str_eq(intent, "query") { return "question" }
if str_eq(intent, "describe") { return "assert" }
if str_eq(intent, "greet") { return "greet" }
return "assert"
}
// sem_to_spec: SemFrame -> realizer slot map
//
// The "lang" key from the SemFrame is forwarded into the GramSpec so that
// realize() can resolve the correct language profile.
fn sem_to_spec(frame: [String]) -> [String] {
let intent: String = sem_intent(frame)
let subject: String = sem_subject(frame)
let obj: String = sem_object(frame)
let mods: String = sem_modifiers(frame)
let lang_code: String = sem_lang(frame)
let location: String = sem_first_modifier(mods)
if str_eq(intent, "greet") {
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, "greet")
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, "present")
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, "simple")
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
if str_eq(intent, "describe") {
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, "assert")
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, "be")
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, "present")
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, "simple")
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
let realize_intent: String = sem_intent_to_realize(intent)
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, realize_intent)
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, "")
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, "present")
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, "simple")
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
// sem_to_spec_full
fn sem_to_spec_full(frame: [String], verb: String, tense: String, aspect: String) -> [String] {
let intent: String = sem_intent(frame)
let subject: String = sem_subject(frame)
let obj: String = sem_object(frame)
let mods: String = sem_modifiers(frame)
let lang_code: String = sem_lang(frame)
let location: String = sem_first_modifier(mods)
if str_eq(intent, "greet") {
return sem_to_spec(frame)
}
if str_eq(intent, "describe") {
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, "assert")
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, "be")
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, tense)
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, aspect)
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
let realize_intent: String = sem_intent_to_realize(intent)
let spec: [String] = native_list_empty()
let spec = native_list_append(spec, "intent")
let spec = native_list_append(spec, realize_intent)
let spec = native_list_append(spec, "agent")
let spec = native_list_append(spec, subject)
let spec = native_list_append(spec, "predicate")
let spec = native_list_append(spec, verb)
let spec = native_list_append(spec, "patient")
let spec = native_list_append(spec, obj)
let spec = native_list_append(spec, "location")
let spec = native_list_append(spec, location)
let spec = native_list_append(spec, "tense")
let spec = native_list_append(spec, tense)
let spec = native_list_append(spec, "aspect")
let spec = native_list_append(spec, aspect)
let spec = native_list_append(spec, "lang")
let spec = native_list_append(spec, lang_code)
return spec
}
// Greet realization helper
fn sem_realize_greet(subject: String) -> String {
if str_eq(subject, "") {
return "Hello."
}
return "Hello, " + subject + "."
}
// sem_realize: SemFrame -> text
fn sem_realize(frame: [String]) -> String {
let intent: String = sem_intent(frame)
if str_eq(intent, "greet") {
return sem_realize_greet(sem_subject(frame))
}
let spec: [String] = sem_to_spec(frame)
return realize(spec)
}
// sem_realize_full
fn sem_realize_full(frame: [String], verb: String, tense: String, aspect: String) -> String {
let intent: String = sem_intent(frame)
if str_eq(intent, "greet") {
return sem_realize_greet(sem_subject(frame))
}
let spec: [String] = sem_to_spec_full(frame, verb, tense, aspect)
return realize(spec)
}
// sem_realize_lang: realize in an explicitly specified language
//
// Convenience for callers that want to specify the output language without
// constructing a full SemFrame. The lang_code overrides whatever "lang" is
// set in the frame.
fn sem_realize_lang(frame: [String], lang_code: String) -> String {
let intent: String = sem_intent(frame)
if str_eq(intent, "greet") {
return sem_realize_greet(sem_subject(frame))
}
// Inject the lang_code into the frame before converting to spec.
let patched: [String] = slots_set(frame, "lang", lang_code)
let spec: [String] = sem_to_spec(patched)
return realize(spec)
}
View File
+3
View File
@@ -0,0 +1,3 @@
fn gram_fn(x: String) -> String {
return x
}