Archived
Add native El NLG system: morphology, vocabulary, grammar, realizer
Implements a complete natural language generation stack in El: - morphology.el: English pluralization, verb conjugation (40+ irregulars), determiner agreement - vocabulary.el: inline seed lexicon (~100 entries: pronouns, nouns, verbs, adjectives, etc.) - grammar.el: CFG rules (S/NP/VP/PP), slot-map driven tree generator, s-expression renderer - realizer.el: semantic form -> English text with tense/aspect/agreement, do-support for questions - nlg.el: JSON-driven public API tying all modules together - tests/run.sh: acceptance corpus runner (6 tests, all passing)
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
// basic-sentence.el - Realize "I see the cat."
|
||||
//
|
||||
// SemanticForm: assert, agent=I, predicate=see, patient=the cat, present simple.
|
||||
|
||||
fn run_test() -> String {
|
||||
let form: [String] = native_list_empty()
|
||||
let form = native_list_append(form, "intent")
|
||||
let form = native_list_append(form, "assert")
|
||||
let form = native_list_append(form, "agent")
|
||||
let form = native_list_append(form, "I")
|
||||
let form = native_list_append(form, "predicate")
|
||||
let form = native_list_append(form, "see")
|
||||
let form = native_list_append(form, "patient")
|
||||
let form = native_list_append(form, "the cat")
|
||||
let form = native_list_append(form, "location")
|
||||
let form = native_list_append(form, "")
|
||||
let form = native_list_append(form, "tense")
|
||||
let form = native_list_append(form, "present")
|
||||
let form = native_list_append(form, "aspect")
|
||||
let form = native_list_append(form, "simple")
|
||||
return realize(form)
|
||||
}
|
||||
|
||||
println(run_test())
|
||||
@@ -0,0 +1,24 @@
|
||||
// command.el - Realize "Go home."
|
||||
//
|
||||
// SemanticForm: command, predicate=go, location=home.
|
||||
|
||||
fn run_test() -> String {
|
||||
let form: [String] = native_list_empty()
|
||||
let form = native_list_append(form, "intent")
|
||||
let form = native_list_append(form, "command")
|
||||
let form = native_list_append(form, "agent")
|
||||
let form = native_list_append(form, "")
|
||||
let form = native_list_append(form, "predicate")
|
||||
let form = native_list_append(form, "go")
|
||||
let form = native_list_append(form, "patient")
|
||||
let form = native_list_append(form, "")
|
||||
let form = native_list_append(form, "location")
|
||||
let form = native_list_append(form, "home")
|
||||
let form = native_list_append(form, "tense")
|
||||
let form = native_list_append(form, "present")
|
||||
let form = native_list_append(form, "aspect")
|
||||
let form = native_list_append(form, "simple")
|
||||
return realize(form)
|
||||
}
|
||||
|
||||
println(run_test())
|
||||
@@ -0,0 +1,24 @@
|
||||
// past-tense.el - Realize "She ran in the park."
|
||||
//
|
||||
// SemanticForm: assert, agent=she, predicate=run, location=in the park, past simple.
|
||||
|
||||
fn run_test() -> String {
|
||||
let form: [String] = native_list_empty()
|
||||
let form = native_list_append(form, "intent")
|
||||
let form = native_list_append(form, "assert")
|
||||
let form = native_list_append(form, "agent")
|
||||
let form = native_list_append(form, "she")
|
||||
let form = native_list_append(form, "predicate")
|
||||
let form = native_list_append(form, "run")
|
||||
let form = native_list_append(form, "patient")
|
||||
let form = native_list_append(form, "")
|
||||
let form = native_list_append(form, "location")
|
||||
let form = native_list_append(form, "in the park")
|
||||
let form = native_list_append(form, "tense")
|
||||
let form = native_list_append(form, "past")
|
||||
let form = native_list_append(form, "aspect")
|
||||
let form = native_list_append(form, "simple")
|
||||
return realize(form)
|
||||
}
|
||||
|
||||
println(run_test())
|
||||
@@ -0,0 +1,16 @@
|
||||
// plural-noun.el - Test noun pluralization.
|
||||
//
|
||||
// Expected: "cats|children|boxes|cities|fish|leaves"
|
||||
|
||||
fn run_test() -> String {
|
||||
let r: [String] = native_list_empty()
|
||||
let r = native_list_append(r, pluralize("cat"))
|
||||
let r = native_list_append(r, pluralize("child"))
|
||||
let r = native_list_append(r, pluralize("box"))
|
||||
let r = native_list_append(r, pluralize("city"))
|
||||
let r = native_list_append(r, pluralize("fish"))
|
||||
let r = native_list_append(r, pluralize("leaf"))
|
||||
return str_join(r, "|")
|
||||
}
|
||||
|
||||
println(run_test())
|
||||
@@ -0,0 +1,24 @@
|
||||
// question.el - Realize "Do you see the dog?"
|
||||
//
|
||||
// SemanticForm: question, agent=you, predicate=see, patient=the dog, present simple.
|
||||
|
||||
fn run_test() -> String {
|
||||
let form: [String] = native_list_empty()
|
||||
let form = native_list_append(form, "intent")
|
||||
let form = native_list_append(form, "question")
|
||||
let form = native_list_append(form, "agent")
|
||||
let form = native_list_append(form, "you")
|
||||
let form = native_list_append(form, "predicate")
|
||||
let form = native_list_append(form, "see")
|
||||
let form = native_list_append(form, "patient")
|
||||
let form = native_list_append(form, "the dog")
|
||||
let form = native_list_append(form, "location")
|
||||
let form = native_list_append(form, "")
|
||||
let form = native_list_append(form, "tense")
|
||||
let form = native_list_append(form, "present")
|
||||
let form = native_list_append(form, "aspect")
|
||||
let form = native_list_append(form, "simple")
|
||||
return realize(form)
|
||||
}
|
||||
|
||||
println(run_test())
|
||||
@@ -0,0 +1,15 @@
|
||||
// verb-conjugation.el - Test verb conjugation.
|
||||
//
|
||||
// Expected: "ran|walked|is|am|sleeping"
|
||||
|
||||
fn run_test() -> String {
|
||||
let r: [String] = native_list_empty()
|
||||
let r = native_list_append(r, verb_form("run", "past", "third", "singular"))
|
||||
let r = native_list_append(r, verb_form("walk", "past", "third", "singular"))
|
||||
let r = native_list_append(r, verb_form("be", "present", "third", "singular"))
|
||||
let r = native_list_append(r, verb_form("be", "present", "first", "singular"))
|
||||
let r = native_list_append(r, verb_form("sleep","progressive","third","singular"))
|
||||
return str_join(r, "|")
|
||||
}
|
||||
|
||||
println(run_test())
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
# run.sh - build and execute the nlg/ acceptance corpus.
|
||||
#
|
||||
# Each examples/<case>.el is a self-contained El program that calls NLG
|
||||
# functions and prints a deterministic result line. Because El has no
|
||||
# import system, the runner concatenates all NLG source modules above each
|
||||
# test file before handing it to elc.
|
||||
#
|
||||
# Module load order:
|
||||
# morphology.el (standalone)
|
||||
# vocabulary.el (standalone)
|
||||
# grammar.el (uses slot helpers)
|
||||
# realizer.el (uses morphology + grammar)
|
||||
# nlg.el (public API)
|
||||
# <test>.el (calls the above)
|
||||
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
EL_HOME="${EL_HOME:-$(cd ../.. && pwd)/el}"
|
||||
ELC="${ELC:-${EL_HOME}/dist/platform/elc}"
|
||||
RUNTIME_DIR="${EL_HOME}/el-compiler/runtime"
|
||||
SRC_DIR="$(cd .. && pwd)/src"
|
||||
|
||||
if [ ! -x "${ELC}" ]; then
|
||||
echo "elc not found at ${ELC}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# NLG source modules in dependency order
|
||||
NLG_MODULES=(
|
||||
"${SRC_DIR}/morphology.el"
|
||||
"${SRC_DIR}/vocabulary.el"
|
||||
"${SRC_DIR}/grammar.el"
|
||||
"${SRC_DIR}/realizer.el"
|
||||
"${SRC_DIR}/nlg.el"
|
||||
)
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
FAILED_NAMES=()
|
||||
|
||||
run_nlg_case() {
|
||||
local name="$1"
|
||||
local src="$2"
|
||||
local expected="$3"
|
||||
|
||||
local combined_src
|
||||
local out_c
|
||||
local out_bin
|
||||
combined_src="$(mktemp -t nlg_test.XXXXXX).el"
|
||||
out_c="$(mktemp -t nlg_test.XXXXXX).c"
|
||||
out_bin="$(mktemp -t nlg_test.XXXXXX)"
|
||||
|
||||
# Concatenate modules + test file
|
||||
for mod in "${NLG_MODULES[@]}"; do
|
||||
cat "${mod}" >> "${combined_src}"
|
||||
echo "" >> "${combined_src}"
|
||||
done
|
||||
cat "${src}" >> "${combined_src}"
|
||||
|
||||
if ! "${ELC}" "${combined_src}" > "${out_c}" 2>/tmp/nlg_test.elc.err; then
|
||||
echo "FAIL ${name} - elc emit failed:"
|
||||
cat /tmp/nlg_test.elc.err | sed 's/^/ /'
|
||||
FAIL=$((FAIL+1))
|
||||
FAILED_NAMES+=("${name}")
|
||||
rm -f "${combined_src}" "${out_c}" "${out_bin}"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! cc -O2 -I "${RUNTIME_DIR}" "${out_c}" "${RUNTIME_DIR}/el_runtime.c" \
|
||||
-lcurl -lpthread -o "${out_bin}" 2>/tmp/nlg_test.cc.err; then
|
||||
echo "FAIL ${name} - cc failed:"
|
||||
cat /tmp/nlg_test.cc.err | sed 's/^/ /'
|
||||
FAIL=$((FAIL+1))
|
||||
FAILED_NAMES+=("${name}")
|
||||
rm -f "${combined_src}" "${out_c}" "${out_bin}"
|
||||
return
|
||||
fi
|
||||
|
||||
local got
|
||||
got="$("${out_bin}" 2>&1)"
|
||||
|
||||
if [ "${got}" = "${expected}" ]; then
|
||||
echo "PASS ${name}"
|
||||
PASS=$((PASS+1))
|
||||
else
|
||||
echo "FAIL ${name} expected: '${expected}', got: '${got}'"
|
||||
FAIL=$((FAIL+1))
|
||||
FAILED_NAMES+=("${name}")
|
||||
fi
|
||||
|
||||
rm -f "${combined_src}" "${out_c}" "${out_bin}"
|
||||
}
|
||||
|
||||
echo "==> Running nlg acceptance corpus"
|
||||
echo
|
||||
|
||||
run_nlg_case "basic-sentence" examples/basic-sentence.el "I see the cat."
|
||||
run_nlg_case "past-tense" examples/past-tense.el "She ran in the park."
|
||||
run_nlg_case "question" examples/question.el "Do you see the dog?"
|
||||
run_nlg_case "command" examples/command.el "Go home."
|
||||
run_nlg_case "plural-noun" examples/plural-noun.el "cats|children|boxes|cities|fish|leaves"
|
||||
run_nlg_case "verb-conjugation" examples/verb-conjugation.el "ran|walked|is|am|sleeping"
|
||||
|
||||
echo
|
||||
echo "${PASS} passed, ${FAIL} failed"
|
||||
if [ "${FAIL}" -gt 0 ]; then
|
||||
echo "failed: ${FAILED_NAMES[*]}"
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
Reference in New Issue
Block a user