Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5baab050e2 |
+63
-176
@@ -22,46 +22,39 @@ jobs:
|
|||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get install -y gcc libcurl4-openssl-dev apt-transport-https ca-certificates
|
apt-get install -y gcc libcurl4-openssl-dev
|
||||||
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" \
|
|
||||||
> /etc/apt/sources.list.d/google-cloud-sdk.list
|
|
||||||
apt-get update -qq && apt-get install -y google-cloud-cli
|
|
||||||
|
|
||||||
# Seed: use the committed linux-amd64 binary as the bootstrap
|
# Gen2: compile the bootstrap C source into a working elc binary
|
||||||
- name: Bootstrap from committed linux binary (seed)
|
- name: Build elc from bootstrap (gen2)
|
||||||
run: |
|
run: |
|
||||||
chmod +x dist/platform/elc-linux-amd64
|
# -Wl,--allow-multiple-definition: elc-bootstrap.c and el_runtime.c both define
|
||||||
echo "seed elc (committed linux-amd64 binary)"
|
# is_digit/is_whitespace; bootstrap predates the text-processing primitives commit
|
||||||
dist/platform/elc-linux-amd64 --version || true
|
|
||||||
|
|
||||||
# Gen2: use seed to self-host compile the El compiler
|
|
||||||
- name: Self-host compile El compiler (gen2)
|
|
||||||
run: |
|
|
||||||
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c
|
|
||||||
gcc -O2 \
|
gcc -O2 \
|
||||||
-I runtime \
|
-I el-compiler/runtime \
|
||||||
dist/elc-gen2.c \
|
dist/elc-bootstrap.c \
|
||||||
runtime/el_runtime.c \
|
el-compiler/runtime/el_runtime.c \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm \
|
-lcurl -lpthread -lm \
|
||||||
|
-Wl,--allow-multiple-definition \
|
||||||
|
-o dist/elc-gen2
|
||||||
|
chmod +x dist/elc-gen2
|
||||||
|
echo "gen2 elc built"
|
||||||
|
dist/elc-gen2 --version || true
|
||||||
|
|
||||||
|
# Gen3: use gen2 to compile the El compiler from its own El source (self-host)
|
||||||
|
- name: Self-host compile El compiler with gen2 (gen3)
|
||||||
|
run: |
|
||||||
|
mkdir -p dist/platform
|
||||||
|
dist/elc-gen2 el-compiler/src/compiler.el > dist/elc-gen3.c
|
||||||
|
gcc -O2 \
|
||||||
|
-I el-compiler/runtime \
|
||||||
|
dist/elc-gen3.c \
|
||||||
|
el-compiler/runtime/el_runtime.c \
|
||||||
|
-lcurl -lpthread -lm \
|
||||||
-o dist/platform/elc
|
-o dist/platform/elc
|
||||||
chmod +x dist/platform/elc
|
chmod +x dist/platform/elc
|
||||||
echo "gen2 (self-hosted) elc built"
|
echo "gen3 (self-hosted) elc built"
|
||||||
dist/platform/elc --version || true
|
dist/platform/elc --version || true
|
||||||
|
|
||||||
# Build elb (needed for Artifact Registry publish and downstream CI)
|
|
||||||
- name: Build elb
|
|
||||||
run: |
|
|
||||||
mkdir -p dist/bin
|
|
||||||
dist/platform/elc elb.el > dist/elb.c
|
|
||||||
gcc -O2 \
|
|
||||||
-I runtime \
|
|
||||||
dist/elb.c \
|
|
||||||
runtime/el_runtime.c \
|
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm \
|
|
||||||
-o dist/bin/elb
|
|
||||||
chmod +x dist/bin/elb
|
|
||||||
echo "elb built"
|
|
||||||
|
|
||||||
- name: Run tests - text
|
- name: Run tests - text
|
||||||
run: |
|
run: |
|
||||||
ELC="$(pwd)/dist/platform/elc" \
|
ELC="$(pwd)/dist/platform/elc" \
|
||||||
@@ -87,153 +80,117 @@ jobs:
|
|||||||
bash tests/html_sanitizer/run.sh
|
bash tests/html_sanitizer/run.sh
|
||||||
|
|
||||||
# Native El test suites (elc --test, compile-link-run)
|
# Native El test suites (elc --test, compile-link-run)
|
||||||
# el_runtime.c is precompiled to .o once and reused by all 8 modules.
|
|
||||||
- name: Precompile el_runtime.o
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
RUNTIME="$(pwd)/runtime"
|
|
||||||
gcc -O2 -c -I "$RUNTIME" "$RUNTIME/el_runtime.c" \
|
|
||||||
-o /tmp/el_runtime.o
|
|
||||||
echo "el_runtime.o compiled"
|
|
||||||
|
|
||||||
- name: Run tests - native (core)
|
- name: Run tests - native (core)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
|
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_core
|
-lcurl -lpthread -lm -o /tmp/el_native_core
|
||||||
/tmp/el_native_core
|
/tmp/el_native_core
|
||||||
|
|
||||||
- name: Run tests - native (text)
|
- name: Run tests - native (text)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
|
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_text
|
-lcurl -lpthread -lm -o /tmp/el_native_text
|
||||||
/tmp/el_native_text
|
/tmp/el_native_text
|
||||||
|
|
||||||
- name: Run tests - native (string)
|
- name: Run tests - native (string)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
|
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_string
|
-lcurl -lpthread -lm -o /tmp/el_native_string
|
||||||
/tmp/el_native_string
|
/tmp/el_native_string
|
||||||
|
|
||||||
- name: Run tests - native (math)
|
- name: Run tests - native (math)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
|
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_math
|
-lcurl -lpthread -lm -o /tmp/el_native_math
|
||||||
/tmp/el_native_math
|
/tmp/el_native_math
|
||||||
|
|
||||||
- name: Run tests - native (state)
|
- name: Run tests - native (state)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
|
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_state
|
-lcurl -lpthread -lm -o /tmp/el_native_state
|
||||||
/tmp/el_native_state
|
/tmp/el_native_state
|
||||||
|
|
||||||
- name: Run tests - native (time)
|
- name: Run tests - native (time)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
|
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_time
|
-lcurl -lpthread -lm -o /tmp/el_native_time
|
||||||
/tmp/el_native_time
|
/tmp/el_native_time
|
||||||
|
|
||||||
- name: Run tests - native (json)
|
- name: Run tests - native (json)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
|
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_json
|
-lcurl -lpthread -lm -o /tmp/el_native_json
|
||||||
/tmp/el_native_json
|
/tmp/el_native_json
|
||||||
|
|
||||||
- name: Run tests - native (env)
|
- name: Run tests - native (env)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
|
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_env
|
-lcurl -lpthread -lm -o /tmp/el_native_env
|
||||||
/tmp/el_native_env
|
/tmp/el_native_env
|
||||||
|
|
||||||
- name: Run tests - native (fs)
|
- name: Run tests - native (fs)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
|
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c /tmp/el_runtime.o \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_fs
|
-lcurl -lpthread -lm -o /tmp/el_native_fs
|
||||||
/tmp/el_native_fs
|
/tmp/el_native_fs
|
||||||
|
|
||||||
# Build epm binary using elb (epm lives at repo root, not inside lang/)
|
|
||||||
- name: Build epm
|
|
||||||
run: |
|
|
||||||
ABS_ELB="$(pwd)/dist/bin/elb"
|
|
||||||
ABS_ELC="$(pwd)/dist/platform/elc"
|
|
||||||
ABS_RUNTIME="$(pwd)/runtime"
|
|
||||||
ABS_OUT="$(pwd)/dist/bin"
|
|
||||||
(cd ../epm && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
|
|
||||||
chmod +x dist/bin/epm
|
|
||||||
echo "epm built"
|
|
||||||
|
|
||||||
# Build el-install binary using elb
|
|
||||||
- name: Build el-install
|
|
||||||
run: |
|
|
||||||
ABS_ELB="$(pwd)/dist/bin/elb"
|
|
||||||
ABS_ELC="$(pwd)/dist/platform/elc"
|
|
||||||
ABS_RUNTIME="$(pwd)/runtime"
|
|
||||||
ABS_OUT="$(pwd)/dist/bin"
|
|
||||||
(cd tools/install && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
|
|
||||||
chmod +x dist/bin/el-install
|
|
||||||
echo "el-install built"
|
|
||||||
|
|
||||||
# Publish only after merge (push event), not on PR validation runs
|
# Publish only after merge (push event), not on PR validation runs
|
||||||
- name: Publish El SDK to Artifact Registry (dev)
|
- name: Publish El SDK to Artifact Registry (dev)
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
env:
|
env:
|
||||||
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
||||||
run: |
|
run: |
|
||||||
# Fail loudly: previously this step had no `set -e`, so an auth or
|
|
||||||
# upload failure was swallowed (step exited 0 on the trailing echo)
|
|
||||||
# and the SDK silently never published. Surface failures now.
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -z "${GCP_SA_KEY:-}" ]; then
|
|
||||||
echo "FATAL: GCP_SA_KEY secret is empty — cannot authenticate to publish" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
||||||
|
apt-get install -y -qq apt-transport-https ca-certificates gnupg curl
|
||||||
|
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||||
|
apt-get update -qq && apt-get install -y google-cloud-cli
|
||||||
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
||||||
gcloud config set project neuron-785695
|
gcloud config set project neuron-785695
|
||||||
echo "Publishing as active account: $(gcloud config get-value account 2>/dev/null)"
|
|
||||||
|
|
||||||
VERSION="${GITHUB_SHA:0:8}"
|
VERSION="${GITEA_SHA:0:8}"
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
gcloud artifacts generic upload \
|
||||||
--repository=foundation-dev \
|
--repository=foundation-dev \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-elc \
|
--package=el/elc \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=dist/platform/elc
|
--source=dist/platform/elc
|
||||||
|
|
||||||
@@ -241,87 +198,17 @@ jobs:
|
|||||||
--repository=foundation-dev \
|
--repository=foundation-dev \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-elb \
|
--package=el/el_runtime.c \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=dist/bin/elb
|
--source=el-compiler/runtime/el_runtime.c
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
gcloud artifacts generic upload \
|
||||||
--repository=foundation-dev \
|
--repository=foundation-dev \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-runtime-c \
|
--package=el/el_runtime.h \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=runtime/el_runtime.c
|
--source=el-compiler/runtime/el_runtime.h
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
|
||||||
--repository=foundation-dev \
|
|
||||||
--location=us-central1 \
|
|
||||||
--project=neuron-785695 \
|
|
||||||
--package=el-runtime-h \
|
|
||||||
--version="${VERSION}" \
|
|
||||||
--source=runtime/el_runtime.h
|
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
|
||||||
--repository=foundation-dev \
|
|
||||||
--location=us-central1 \
|
|
||||||
--project=neuron-785695 \
|
|
||||||
--package=el-runtime-js \
|
|
||||||
--version="${VERSION}" \
|
|
||||||
--source=runtime/el_runtime.js
|
|
||||||
|
|
||||||
echo "Published El SDK version=${VERSION} to foundation-dev"
|
echo "Published El SDK version=${VERSION} to foundation-dev"
|
||||||
# Keep key alive for the ci-base rebuild step below
|
|
||||||
# (deleted in that step after docker push)
|
|
||||||
|
|
||||||
- name: Rebuild ci-base with fresh El SDK (dev)
|
|
||||||
# Patches ci-base:dev in-place: pulls the existing image (which has all
|
|
||||||
# system deps — Node, Go, gcloud, Docker CLI, etc.) and overlays the freshly
|
|
||||||
# built El SDK on top. Keeps the full ci-base rebuild fast and incremental.
|
|
||||||
#
|
|
||||||
# continue-on-error: this is a CI-cache optimization, NOT the release
|
|
||||||
# artifact. It runs Docker (pull/build/push ~600MB) on the host-mode GCE
|
|
||||||
# runner where DinD/Docker availability is fragile. A failure here must
|
|
||||||
# never block or redden the job — the SDK publish above is the deliverable.
|
|
||||||
continue-on-error: true
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
env:
|
|
||||||
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
CI_BASE="us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base"
|
|
||||||
SHA="${GITHUB_SHA:0:8}"
|
|
||||||
|
|
||||||
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
|
||||||
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
|
||||||
gcloud config set project neuron-785695
|
|
||||||
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
|
|
||||||
|
|
||||||
# Pull existing ci-base:dev (or fall back to :latest on first run)
|
|
||||||
BASE_TAG="dev"
|
|
||||||
docker pull "${CI_BASE}:dev" || { docker pull "${CI_BASE}:latest" && BASE_TAG="latest"; }
|
|
||||||
|
|
||||||
# Inline Dockerfile — only replaces the El SDK layer
|
|
||||||
cat > /tmp/Dockerfile.ci-base-patch << 'EOF'
|
|
||||||
ARG BASE
|
|
||||||
FROM ${BASE}
|
|
||||||
COPY dist/platform/elc /opt/el/dist/platform/elc
|
|
||||||
COPY dist/bin/elb /opt/el/dist/bin/elb
|
|
||||||
COPY runtime/el_runtime.c /opt/el/runtime/el_runtime.c
|
|
||||||
COPY runtime/el_runtime.h /opt/el/runtime/el_runtime.h
|
|
||||||
COPY runtime/el_runtime.js /opt/el/runtime/el_runtime.js
|
|
||||||
RUN chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
|
|
||||||
EOF
|
|
||||||
|
|
||||||
docker build \
|
|
||||||
--build-arg BASE="${CI_BASE}:${BASE_TAG}" \
|
|
||||||
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
|
||||||
-f /tmp/Dockerfile.ci-base-patch \
|
|
||||||
-t "${CI_BASE}:dev" \
|
|
||||||
-t "${CI_BASE}:dev-${SHA}" \
|
|
||||||
.
|
|
||||||
|
|
||||||
docker push "${CI_BASE}:dev"
|
|
||||||
docker push "${CI_BASE}:dev-${SHA}"
|
|
||||||
|
|
||||||
echo "ci-base rebuilt: ${CI_BASE}:dev (${SHA})"
|
|
||||||
rm -f /tmp/gcp-key.json
|
rm -f /tmp/gcp-key.json
|
||||||
|
|||||||
+52
-139
@@ -34,25 +34,35 @@ jobs:
|
|||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get install -y gcc libcurl4-openssl-dev
|
apt-get install -y gcc libcurl4-openssl-dev
|
||||||
|
|
||||||
# Seed: use the committed linux-amd64 binary as the bootstrap
|
# Gen2: compile the bootstrap C source into a working elc binary
|
||||||
- name: Bootstrap from committed linux binary (seed)
|
- name: Build elc from bootstrap (gen2)
|
||||||
run: |
|
run: |
|
||||||
chmod +x dist/platform/elc-linux-amd64
|
# -Wl,--allow-multiple-definition: elc-bootstrap.c and el_runtime.c both define
|
||||||
echo "seed elc (committed linux-amd64 binary)"
|
# is_digit/is_whitespace; bootstrap predates the text-processing primitives commit
|
||||||
dist/platform/elc-linux-amd64 --version || true
|
|
||||||
|
|
||||||
# Gen2: use seed to self-host compile the El compiler
|
|
||||||
- name: Self-host compile El compiler (gen2)
|
|
||||||
run: |
|
|
||||||
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c
|
|
||||||
gcc -O2 \
|
gcc -O2 \
|
||||||
-I runtime \
|
-I el-compiler/runtime \
|
||||||
dist/elc-gen2.c \
|
dist/elc-bootstrap.c \
|
||||||
runtime/el_runtime.c \
|
el-compiler/runtime/el_runtime.c \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm \
|
-lcurl -lpthread -lm \
|
||||||
|
-Wl,--allow-multiple-definition \
|
||||||
|
-o dist/elc-gen2
|
||||||
|
chmod +x dist/elc-gen2
|
||||||
|
echo "gen2 elc built"
|
||||||
|
dist/elc-gen2 --version || true
|
||||||
|
|
||||||
|
# Gen3: use gen2 to compile the El compiler from its own El source (self-host)
|
||||||
|
- name: Self-host compile El compiler with gen2 (gen3)
|
||||||
|
run: |
|
||||||
|
mkdir -p dist/platform
|
||||||
|
dist/elc-gen2 el-compiler/src/compiler.el > dist/elc-gen3.c
|
||||||
|
gcc -O2 \
|
||||||
|
-I el-compiler/runtime \
|
||||||
|
dist/elc-gen3.c \
|
||||||
|
el-compiler/runtime/el_runtime.c \
|
||||||
|
-lcurl -lpthread -lm \
|
||||||
-o dist/platform/elc
|
-o dist/platform/elc
|
||||||
chmod +x dist/platform/elc
|
chmod +x dist/platform/elc
|
||||||
echo "gen2 (self-hosted) elc built"
|
echo "gen3 (self-hosted) elc built"
|
||||||
dist/platform/elc --version || true
|
dist/platform/elc --version || true
|
||||||
|
|
||||||
- name: Run tests - text
|
- name: Run tests - text
|
||||||
@@ -84,157 +94,113 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
|
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_core
|
-lcurl -lpthread -lm -o /tmp/el_native_core
|
||||||
/tmp/el_native_core
|
/tmp/el_native_core
|
||||||
|
|
||||||
- name: Run tests - native (text)
|
- name: Run tests - native (text)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
|
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_text
|
-lcurl -lpthread -lm -o /tmp/el_native_text
|
||||||
/tmp/el_native_text
|
/tmp/el_native_text
|
||||||
|
|
||||||
- name: Run tests - native (string)
|
- name: Run tests - native (string)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
|
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_string
|
-lcurl -lpthread -lm -o /tmp/el_native_string
|
||||||
/tmp/el_native_string
|
/tmp/el_native_string
|
||||||
|
|
||||||
- name: Run tests - native (math)
|
- name: Run tests - native (math)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
|
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_math
|
-lcurl -lpthread -lm -o /tmp/el_native_math
|
||||||
/tmp/el_native_math
|
/tmp/el_native_math
|
||||||
|
|
||||||
- name: Run tests - native (state)
|
- name: Run tests - native (state)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
|
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_state
|
-lcurl -lpthread -lm -o /tmp/el_native_state
|
||||||
/tmp/el_native_state
|
/tmp/el_native_state
|
||||||
|
|
||||||
- name: Run tests - native (time)
|
- name: Run tests - native (time)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
|
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_time
|
-lcurl -lpthread -lm -o /tmp/el_native_time
|
||||||
/tmp/el_native_time
|
/tmp/el_native_time
|
||||||
|
|
||||||
- name: Run tests - native (json)
|
- name: Run tests - native (json)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
|
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_json
|
-lcurl -lpthread -lm -o /tmp/el_native_json
|
||||||
/tmp/el_native_json
|
/tmp/el_native_json
|
||||||
|
|
||||||
- name: Run tests - native (env)
|
- name: Run tests - native (env)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
|
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_env
|
-lcurl -lpthread -lm -o /tmp/el_native_env
|
||||||
/tmp/el_native_env
|
/tmp/el_native_env
|
||||||
|
|
||||||
- name: Run tests - native (fs)
|
- name: Run tests - native (fs)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
|
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_fs
|
-lcurl -lpthread -lm -o /tmp/el_native_fs
|
||||||
/tmp/el_native_fs
|
/tmp/el_native_fs
|
||||||
|
|
||||||
# Build elb (needed for epm and el-install builds below)
|
|
||||||
- name: Build elb
|
|
||||||
run: |
|
|
||||||
mkdir -p dist/bin
|
|
||||||
dist/platform/elc elb.el > dist/elb.c
|
|
||||||
gcc -O2 \
|
|
||||||
-I runtime \
|
|
||||||
dist/elb.c \
|
|
||||||
runtime/el_runtime.c \
|
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm \
|
|
||||||
-o dist/bin/elb
|
|
||||||
chmod +x dist/bin/elb
|
|
||||||
echo "elb built"
|
|
||||||
|
|
||||||
# Build epm binary using elb (epm lives at repo root, not inside lang/)
|
|
||||||
- name: Build epm
|
|
||||||
run: |
|
|
||||||
ABS_ELB="$(pwd)/dist/bin/elb"
|
|
||||||
ABS_ELC="$(pwd)/dist/platform/elc"
|
|
||||||
ABS_RUNTIME="$(pwd)/runtime"
|
|
||||||
ABS_OUT="$(pwd)/dist/bin"
|
|
||||||
(cd ../epm && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
|
|
||||||
chmod +x dist/bin/epm
|
|
||||||
echo "epm built"
|
|
||||||
|
|
||||||
# Build el-install binary using elb
|
|
||||||
- name: Build el-install
|
|
||||||
run: |
|
|
||||||
ABS_ELB="$(pwd)/dist/bin/elb"
|
|
||||||
ABS_ELC="$(pwd)/dist/platform/elc"
|
|
||||||
ABS_RUNTIME="$(pwd)/runtime"
|
|
||||||
ABS_OUT="$(pwd)/dist/bin"
|
|
||||||
(cd tools/install && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
|
|
||||||
chmod +x dist/bin/el-install
|
|
||||||
echo "el-install built"
|
|
||||||
|
|
||||||
# Publish only after merge (push event), not on PR validation runs
|
# Publish only after merge (push event), not on PR validation runs
|
||||||
- name: Publish El SDK to Artifact Registry (stage)
|
- name: Publish El SDK to Artifact Registry (stage)
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
env:
|
env:
|
||||||
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
||||||
run: |
|
run: |
|
||||||
# Fail loudly: previously this step had no `set -e`, so an auth or
|
|
||||||
# upload failure was swallowed (step exited 0 on the trailing echo)
|
|
||||||
# and the SDK silently never published. Surface failures now.
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -z "${GCP_SA_KEY:-}" ]; then
|
|
||||||
echo "FATAL: GCP_SA_KEY secret is empty — cannot authenticate to publish" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
||||||
apt-get install -y -qq apt-transport-https ca-certificates curl
|
apt-get install -y -qq apt-transport-https ca-certificates gnupg curl
|
||||||
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
|
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||||
apt-get update -qq && apt-get install -y google-cloud-cli
|
apt-get update -qq && apt-get install -y google-cloud-cli
|
||||||
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
||||||
gcloud config set project neuron-785695
|
gcloud config set project neuron-785695
|
||||||
echo "Publishing as active account: $(gcloud config get-value account 2>/dev/null)"
|
|
||||||
|
|
||||||
VERSION="${GITHUB_SHA:0:8}"
|
VERSION="${GITEA_SHA:0:8}"
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
gcloud artifacts generic upload \
|
||||||
--repository=foundation-stage \
|
--repository=foundation-stage \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-elc \
|
--package=el/elc \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=dist/platform/elc
|
--source=dist/platform/elc
|
||||||
|
|
||||||
@@ -242,70 +208,17 @@ jobs:
|
|||||||
--repository=foundation-stage \
|
--repository=foundation-stage \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-runtime-c \
|
--package=el/el_runtime.c \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=runtime/el_runtime.c
|
--source=el-compiler/runtime/el_runtime.c
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
gcloud artifacts generic upload \
|
||||||
--repository=foundation-stage \
|
--repository=foundation-stage \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-runtime-h \
|
--package=el/el_runtime.h \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=runtime/el_runtime.h
|
--source=el-compiler/runtime/el_runtime.h
|
||||||
|
|
||||||
echo "Published El SDK version=${VERSION} to foundation-stage"
|
echo "Published El SDK version=${VERSION} to foundation-stage"
|
||||||
# Keep key alive for the ci-base rebuild step below
|
|
||||||
# (deleted in that step after docker push)
|
|
||||||
|
|
||||||
- name: Rebuild ci-base with fresh El SDK (stage)
|
|
||||||
# Patches ci-base:stage in-place: pulls the existing image (which has all
|
|
||||||
# system deps — Node, Go, gcloud, Docker CLI, etc.) and overlays the freshly
|
|
||||||
# built El SDK on top. Keeps the full ci-base rebuild fast and incremental.
|
|
||||||
#
|
|
||||||
# continue-on-error: this is a CI-cache optimization, NOT the release
|
|
||||||
# artifact. It runs Docker (pull/build/push ~600MB) on the host-mode GCE
|
|
||||||
# runner where DinD/Docker availability is fragile. A failure here must
|
|
||||||
# never block or redden the job — the SDK publish above is the deliverable.
|
|
||||||
continue-on-error: true
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
env:
|
|
||||||
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
CI_BASE="us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base"
|
|
||||||
SHA="${GITHUB_SHA:0:8}"
|
|
||||||
|
|
||||||
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
|
||||||
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
|
||||||
gcloud config set project neuron-785695
|
|
||||||
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
|
|
||||||
|
|
||||||
# Pull existing ci-base:stage (system deps stay cached in the base layer)
|
|
||||||
docker pull "${CI_BASE}:stage" || docker pull "${CI_BASE}:latest"
|
|
||||||
|
|
||||||
# Inline Dockerfile — only replaces the El SDK layer
|
|
||||||
cat > /tmp/Dockerfile.ci-base-patch << 'EOF'
|
|
||||||
ARG BASE
|
|
||||||
FROM ${BASE}
|
|
||||||
COPY dist/platform/elc /opt/el/dist/platform/elc
|
|
||||||
COPY dist/bin/elb /opt/el/dist/bin/elb
|
|
||||||
COPY runtime/el_runtime.c /opt/el/runtime/el_runtime.c
|
|
||||||
COPY runtime/el_runtime.h /opt/el/runtime/el_runtime.h
|
|
||||||
COPY runtime/el_runtime.js /opt/el/runtime/el_runtime.js
|
|
||||||
RUN chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
|
|
||||||
EOF
|
|
||||||
|
|
||||||
docker build \
|
|
||||||
--build-arg BASE="${CI_BASE}:stage" \
|
|
||||||
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
|
||||||
-f /tmp/Dockerfile.ci-base-patch \
|
|
||||||
-t "${CI_BASE}:stage" \
|
|
||||||
-t "${CI_BASE}:stage-${SHA}" \
|
|
||||||
.
|
|
||||||
|
|
||||||
docker push "${CI_BASE}:stage"
|
|
||||||
docker push "${CI_BASE}:stage-${SHA}"
|
|
||||||
|
|
||||||
echo "ci-base rebuilt: ${CI_BASE}:stage (${SHA})"
|
|
||||||
rm -f /tmp/gcp-key.json
|
rm -f /tmp/gcp-key.json
|
||||||
|
|||||||
@@ -34,26 +34,35 @@ jobs:
|
|||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get install -y gcc libcurl4-openssl-dev
|
apt-get install -y gcc libcurl4-openssl-dev
|
||||||
|
|
||||||
# Seed: use the committed linux-amd64 binary as the bootstrap
|
# Gen2: compile the bootstrap C source into a working elc binary
|
||||||
- name: Bootstrap from committed linux binary (seed)
|
- name: Build elc from bootstrap (gen2)
|
||||||
run: |
|
run: |
|
||||||
chmod +x dist/platform/elc-linux-amd64
|
# -Wl,--allow-multiple-definition: elc-bootstrap.c and el_runtime.c both define
|
||||||
echo "seed elc (committed linux-amd64 binary)"
|
# is_digit/is_whitespace; bootstrap predates the text-processing primitives commit
|
||||||
dist/platform/elc-linux-amd64 --version || true
|
gcc -O2 \
|
||||||
|
-I el-compiler/runtime \
|
||||||
|
dist/elc-bootstrap.c \
|
||||||
|
el-compiler/runtime/el_runtime.c \
|
||||||
|
-lcurl -lpthread -lm \
|
||||||
|
-Wl,--allow-multiple-definition \
|
||||||
|
-o dist/elc-gen2
|
||||||
|
chmod +x dist/elc-gen2
|
||||||
|
echo "gen2 elc built"
|
||||||
|
dist/elc-gen2 --version || true
|
||||||
|
|
||||||
# Gen2: use seed to self-host compile the El compiler
|
# Gen3: use gen2 to compile the El compiler from its own El source (self-host)
|
||||||
- name: Self-host compile El compiler (gen2)
|
- name: Self-host compile El compiler with gen2 (gen3)
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist/platform
|
mkdir -p dist/platform
|
||||||
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c
|
dist/elc-gen2 el-compiler/src/compiler.el > dist/elc-gen3.c
|
||||||
gcc -O2 \
|
gcc -O2 \
|
||||||
-I runtime \
|
-I el-compiler/runtime \
|
||||||
dist/elc-gen2.c \
|
dist/elc-gen3.c \
|
||||||
runtime/el_runtime.c \
|
el-compiler/runtime/el_runtime.c \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm \
|
-lcurl -lpthread -lm \
|
||||||
-o dist/platform/elc
|
-o dist/platform/elc
|
||||||
chmod +x dist/platform/elc
|
chmod +x dist/platform/elc
|
||||||
echo "gen2 (self-hosted) elc built"
|
echo "gen3 (self-hosted) elc built"
|
||||||
dist/platform/elc --version || true
|
dist/platform/elc --version || true
|
||||||
|
|
||||||
# Build elb binary
|
# Build elb binary
|
||||||
@@ -62,33 +71,37 @@ jobs:
|
|||||||
mkdir -p dist/bin
|
mkdir -p dist/bin
|
||||||
dist/platform/elc elb.el > dist/elb.c
|
dist/platform/elc elb.el > dist/elb.c
|
||||||
gcc -O2 \
|
gcc -O2 \
|
||||||
-I runtime \
|
-I el-compiler/runtime \
|
||||||
dist/elb.c \
|
dist/elb.c \
|
||||||
runtime/el_runtime.c \
|
el-compiler/runtime/el_runtime.c \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm \
|
-lcurl -lpthread -lm \
|
||||||
-o dist/bin/elb
|
-o dist/bin/elb
|
||||||
chmod +x dist/bin/elb
|
chmod +x dist/bin/elb
|
||||||
echo "elb built"
|
echo "elb built"
|
||||||
|
|
||||||
# Build epm binary using elb (epm lives at repo root, not inside lang/)
|
# Build epm binary (epm lives at repo root, not inside lang/)
|
||||||
- name: Build epm
|
- name: Build epm
|
||||||
run: |
|
run: |
|
||||||
ABS_ELB="$(pwd)/dist/bin/elb"
|
dist/platform/elc ../epm/src/epm.el > dist/epm.c
|
||||||
ABS_ELC="$(pwd)/dist/platform/elc"
|
gcc -O2 \
|
||||||
ABS_RUNTIME="$(pwd)/runtime"
|
-I el-compiler/runtime \
|
||||||
ABS_OUT="$(pwd)/dist/bin"
|
dist/epm.c \
|
||||||
(cd ../epm && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
|
el-compiler/runtime/el_runtime.c \
|
||||||
|
-lcurl -lpthread -lm \
|
||||||
|
-o dist/bin/epm
|
||||||
chmod +x dist/bin/epm
|
chmod +x dist/bin/epm
|
||||||
echo "epm built"
|
echo "epm built"
|
||||||
|
|
||||||
# Build el-install binary using elb
|
# Build el-install binary
|
||||||
- name: Build el-install
|
- name: Build el-install
|
||||||
run: |
|
run: |
|
||||||
ABS_ELB="$(pwd)/dist/bin/elb"
|
dist/platform/elc tools/install/el-install.el > dist/el-install.c
|
||||||
ABS_ELC="$(pwd)/dist/platform/elc"
|
gcc -O2 \
|
||||||
ABS_RUNTIME="$(pwd)/runtime"
|
-I el-compiler/runtime \
|
||||||
ABS_OUT="$(pwd)/dist/bin"
|
dist/el-install.c \
|
||||||
(cd tools/install && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
|
el-compiler/runtime/el_runtime.c \
|
||||||
|
-lcurl -lpthread -lm \
|
||||||
|
-o dist/bin/el-install
|
||||||
chmod +x dist/bin/el-install
|
chmod +x dist/bin/el-install
|
||||||
echo "el-install built"
|
echo "el-install built"
|
||||||
|
|
||||||
@@ -121,90 +134,90 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
|
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_core
|
-lcurl -lpthread -lm -o /tmp/el_native_core
|
||||||
/tmp/el_native_core
|
/tmp/el_native_core
|
||||||
|
|
||||||
- name: Run tests - native (text)
|
- name: Run tests - native (text)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
|
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_text
|
-lcurl -lpthread -lm -o /tmp/el_native_text
|
||||||
/tmp/el_native_text
|
/tmp/el_native_text
|
||||||
|
|
||||||
- name: Run tests - native (string)
|
- name: Run tests - native (string)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
|
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_string
|
-lcurl -lpthread -lm -o /tmp/el_native_string
|
||||||
/tmp/el_native_string
|
/tmp/el_native_string
|
||||||
|
|
||||||
- name: Run tests - native (math)
|
- name: Run tests - native (math)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
|
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_math
|
-lcurl -lpthread -lm -o /tmp/el_native_math
|
||||||
/tmp/el_native_math
|
/tmp/el_native_math
|
||||||
|
|
||||||
- name: Run tests - native (state)
|
- name: Run tests - native (state)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
|
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_state
|
-lcurl -lpthread -lm -o /tmp/el_native_state
|
||||||
/tmp/el_native_state
|
/tmp/el_native_state
|
||||||
|
|
||||||
- name: Run tests - native (time)
|
- name: Run tests - native (time)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
|
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_time
|
-lcurl -lpthread -lm -o /tmp/el_native_time
|
||||||
/tmp/el_native_time
|
/tmp/el_native_time
|
||||||
|
|
||||||
- name: Run tests - native (json)
|
- name: Run tests - native (json)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
|
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_json
|
-lcurl -lpthread -lm -o /tmp/el_native_json
|
||||||
/tmp/el_native_json
|
/tmp/el_native_json
|
||||||
|
|
||||||
- name: Run tests - native (env)
|
- name: Run tests - native (env)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
|
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_env
|
-lcurl -lpthread -lm -o /tmp/el_native_env
|
||||||
/tmp/el_native_env
|
/tmp/el_native_env
|
||||||
|
|
||||||
- name: Run tests - native (fs)
|
- name: Run tests - native (fs)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ELC="$(pwd)/dist/platform/elc"
|
ELC="$(pwd)/dist/platform/elc"
|
||||||
RUNTIME="$(pwd)/runtime"
|
RUNTIME="$(pwd)/el-compiler/runtime"
|
||||||
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
|
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
|
||||||
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
|
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_fs
|
-lcurl -lpthread -lm -o /tmp/el_native_fs
|
||||||
/tmp/el_native_fs
|
/tmp/el_native_fs
|
||||||
|
|
||||||
# Bundle the SDK tarball - runs from the repo root to reference lang/ paths correctly
|
# Bundle the SDK tarball - runs from the repo root to reference lang/ paths correctly
|
||||||
@@ -216,10 +229,8 @@ jobs:
|
|||||||
cp lang/dist/platform/elc dist/sdk/bin/elc
|
cp lang/dist/platform/elc dist/sdk/bin/elc
|
||||||
cp lang/dist/bin/elb dist/sdk/bin/elb
|
cp lang/dist/bin/elb dist/sdk/bin/elb
|
||||||
cp lang/dist/bin/epm dist/sdk/bin/epm
|
cp lang/dist/bin/epm dist/sdk/bin/epm
|
||||||
cp lang/runtime/el_runtime.c dist/sdk/runtime/
|
cp lang/el-compiler/runtime/el_runtime.c dist/sdk/runtime/
|
||||||
cp lang/runtime/el_runtime.h dist/sdk/runtime/
|
cp lang/el-compiler/runtime/el_runtime.h dist/sdk/runtime/
|
||||||
cp lang/runtime/engram_store.c dist/sdk/runtime/
|
|
||||||
cp lang/runtime/engram_store.h dist/sdk/runtime/
|
|
||||||
cp lang/runtime/*.el dist/sdk/runtime/
|
cp lang/runtime/*.el dist/sdk/runtime/
|
||||||
tar -czf dist/el-sdk-latest.tar.gz -C dist/sdk .
|
tar -czf dist/el-sdk-latest.tar.gz -C dist/sdk .
|
||||||
echo "SDK tarball bundled: dist/el-sdk-latest.tar.gz"
|
echo "SDK tarball bundled: dist/el-sdk-latest.tar.gz"
|
||||||
@@ -230,7 +241,7 @@ jobs:
|
|||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.GIT_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
GITEA_API: https://git.neuralplatform.ai/api/v1
|
GITEA_API: https://git.neuralplatform.ai/api/v1
|
||||||
REPO: neuron-technologies/el
|
REPO: neuron-technologies/el
|
||||||
run: |
|
run: |
|
||||||
@@ -276,10 +287,8 @@ jobs:
|
|||||||
|
|
||||||
# Per-file assets (downstream CI needs these individually)
|
# Per-file assets (downstream CI needs these individually)
|
||||||
upload_asset lang/dist/platform/elc elc
|
upload_asset lang/dist/platform/elc elc
|
||||||
upload_asset lang/runtime/el_runtime.c el_runtime.c
|
upload_asset lang/el-compiler/runtime/el_runtime.c el_runtime.c
|
||||||
upload_asset lang/runtime/el_runtime.h el_runtime.h
|
upload_asset lang/el-compiler/runtime/el_runtime.h el_runtime.h
|
||||||
upload_asset lang/runtime/engram_store.c engram_store.c
|
|
||||||
upload_asset lang/runtime/engram_store.h engram_store.h
|
|
||||||
|
|
||||||
# SDK bundle and installer binary
|
# SDK bundle and installer binary
|
||||||
upload_asset dist/el-sdk-latest.tar.gz el-sdk-latest.tar.gz
|
upload_asset dist/el-sdk-latest.tar.gz el-sdk-latest.tar.gz
|
||||||
@@ -292,29 +301,21 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
||||||
run: |
|
run: |
|
||||||
# Fail loudly: previously this step had no `set -e`, so an auth or
|
|
||||||
# upload failure was swallowed (step exited 0 on the trailing echo)
|
|
||||||
# and the SDK silently never published. Surface failures now.
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -z "${GCP_SA_KEY:-}" ]; then
|
|
||||||
echo "FATAL: GCP_SA_KEY secret is empty — cannot authenticate to publish" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
||||||
apt-get install -y -qq apt-transport-https ca-certificates curl
|
apt-get install -y -qq apt-transport-https ca-certificates gnupg curl
|
||||||
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
|
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||||
apt-get update -qq && apt-get install -y google-cloud-cli
|
apt-get update -qq && apt-get install -y google-cloud-cli
|
||||||
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
||||||
gcloud config set project neuron-785695
|
gcloud config set project neuron-785695
|
||||||
echo "Publishing as active account: $(gcloud config get-value account 2>/dev/null)"
|
|
||||||
|
|
||||||
VERSION="${GITHUB_SHA:0:8}"
|
VERSION="${GITEA_SHA:0:8}"
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
gcloud artifacts generic upload \
|
||||||
--repository=foundation-prod \
|
--repository=foundation-prod \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-elc \
|
--package=el/elc \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=dist/platform/elc
|
--source=dist/platform/elc
|
||||||
|
|
||||||
@@ -322,97 +323,28 @@ jobs:
|
|||||||
--repository=foundation-prod \
|
--repository=foundation-prod \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-elb \
|
--package=el/el_runtime.c \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=dist/bin/elb
|
--source=el-compiler/runtime/el_runtime.c
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
gcloud artifacts generic upload \
|
||||||
--repository=foundation-prod \
|
--repository=foundation-prod \
|
||||||
--location=us-central1 \
|
--location=us-central1 \
|
||||||
--project=neuron-785695 \
|
--project=neuron-785695 \
|
||||||
--package=el-runtime-c \
|
--package=el/el_runtime.h \
|
||||||
--version="${VERSION}" \
|
--version="${VERSION}" \
|
||||||
--source=runtime/el_runtime.c
|
--source=el-compiler/runtime/el_runtime.h
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
|
||||||
--repository=foundation-prod \
|
|
||||||
--location=us-central1 \
|
|
||||||
--project=neuron-785695 \
|
|
||||||
--package=el-runtime-h \
|
|
||||||
--version="${VERSION}" \
|
|
||||||
--source=runtime/el_runtime.h
|
|
||||||
|
|
||||||
gcloud artifacts generic upload \
|
|
||||||
--repository=foundation-prod \
|
|
||||||
--location=us-central1 \
|
|
||||||
--project=neuron-785695 \
|
|
||||||
--package=el-runtime-js \
|
|
||||||
--version="${VERSION}" \
|
|
||||||
--source=runtime/el_runtime.js
|
|
||||||
|
|
||||||
echo "Published El SDK version=${VERSION} to foundation-prod"
|
echo "Published El SDK version=${VERSION} to foundation-prod"
|
||||||
# Keep key alive for the ci-base rebuild step below
|
|
||||||
# (deleted in that step after docker push)
|
|
||||||
|
|
||||||
- name: Rebuild ci-base with fresh El SDK
|
|
||||||
# Patches ci-base:latest in-place: pulls the existing image (which has all
|
|
||||||
# system deps — Node, Go, gcloud, Docker CLI, etc.) and overlays the freshly
|
|
||||||
# built El SDK on top. Keeps the full ci-base rebuild fast and incremental.
|
|
||||||
#
|
|
||||||
# continue-on-error: this is a CI-cache optimization, NOT the release
|
|
||||||
# artifact. It runs Docker (pull/build/push ~600MB) on the host-mode GCE
|
|
||||||
# runner where DinD/Docker availability is fragile. A failure here must
|
|
||||||
# never block or redden the job — the SDK publish above is the deliverable.
|
|
||||||
continue-on-error: true
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
env:
|
|
||||||
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
CI_BASE="us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base"
|
|
||||||
SHA="${GITHUB_SHA:0:8}"
|
|
||||||
|
|
||||||
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
|
||||||
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
|
||||||
gcloud config set project neuron-785695
|
|
||||||
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
|
|
||||||
|
|
||||||
# Pull existing ci-base (system deps stay cached in the base layer)
|
|
||||||
docker pull "${CI_BASE}:latest"
|
|
||||||
|
|
||||||
# Inline Dockerfile — only replaces the El SDK layer
|
|
||||||
cat > /tmp/Dockerfile.ci-base-patch << 'EOF'
|
|
||||||
ARG BASE
|
|
||||||
FROM ${BASE}
|
|
||||||
COPY dist/platform/elc /opt/el/dist/platform/elc
|
|
||||||
COPY dist/bin/elb /opt/el/dist/bin/elb
|
|
||||||
COPY runtime/el_runtime.c /opt/el/runtime/el_runtime.c
|
|
||||||
COPY runtime/el_runtime.h /opt/el/runtime/el_runtime.h
|
|
||||||
COPY runtime/el_runtime.js /opt/el/runtime/el_runtime.js
|
|
||||||
RUN chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
|
|
||||||
EOF
|
|
||||||
|
|
||||||
docker build \
|
|
||||||
--build-arg BASE="${CI_BASE}:latest" \
|
|
||||||
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
|
||||||
-f /tmp/Dockerfile.ci-base-patch \
|
|
||||||
-t "${CI_BASE}:latest" \
|
|
||||||
-t "${CI_BASE}:${SHA}" \
|
|
||||||
.
|
|
||||||
|
|
||||||
docker push "${CI_BASE}:latest"
|
|
||||||
docker push "${CI_BASE}:${SHA}"
|
|
||||||
|
|
||||||
echo "ci-base rebuilt: ${CI_BASE}:latest (${SHA})"
|
|
||||||
rm -f /tmp/gcp-key.json
|
rm -f /tmp/gcp-key.json
|
||||||
|
|
||||||
- name: Dispatch el-sdk-updated to downstream repos
|
- name: Dispatch el-sdk-updated to downstream repos
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.GIT_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
GITEA_API: https://git.neuralplatform.ai/api/v1
|
GITEA_API: https://git.neuralplatform.ai/api/v1
|
||||||
run: |
|
run: |
|
||||||
for repo in neuron-technologies/forge neuron-technologies/neuron-web; do
|
for repo in neuron-technologies/forge; do
|
||||||
curl -sf -X POST \
|
curl -sf -X POST \
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ set -euo pipefail
|
|||||||
|
|
||||||
ROOT="$(git rev-parse --show-toplevel)"
|
ROOT="$(git rev-parse --show-toplevel)"
|
||||||
LANG_DIR="$ROOT/lang"
|
LANG_DIR="$ROOT/lang"
|
||||||
RUNTIME="$LANG_DIR/runtime"
|
RUNTIME="$LANG_DIR/el-compiler/runtime"
|
||||||
ELC="$LANG_DIR/dist/platform/elc"
|
ELC="$LANG_DIR/dist/platform/elc"
|
||||||
|
|
||||||
# If elc isn't built yet, skip with a warning rather than blocking
|
# If elc isn't built yet, skip with a warning rather than blocking
|
||||||
if [ ! -x "$ELC" ]; then
|
if [ ! -x "$ELC" ]; then
|
||||||
echo "⚠ elc not found at lang/dist/platform/elc — skipping pre-commit tests"
|
echo "⚠ elc not found at lang/dist/platform/elc — skipping pre-commit tests"
|
||||||
echo " Build it first: cd lang && gcc -O2 -I runtime dist/elc-bootstrap.c runtime/el_runtime.c -lcurl -lpthread -o dist/elc-gen2 && ./dist/elc-gen2 el-compiler/src/compiler.el > /tmp/elc.c && gcc -O2 -I runtime /tmp/elc.c runtime/el_runtime.c -lcurl -lpthread -o dist/platform/elc"
|
echo " Build it first: cd lang && gcc -O2 -I el-compiler/runtime dist/elc-bootstrap.c el-compiler/runtime/el_runtime.c -lcurl -lpthread -o dist/elc-gen2 && ./dist/elc-gen2 el-compiler/src/compiler.el > /tmp/elc.c && gcc -O2 -I el-compiler/runtime /tmp/elc.c el-compiler/runtime/el_runtime.c -lcurl -lpthread -o dist/platform/elc"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
# AGENTS.md — foundation/el (the El language + runtime)
|
|
||||||
|
|
||||||
El is a self-hosting, statically-typed language that compiles `.el` → C → native binary. This repo produces `elc` (compiler), `elb` (build coordinator), and `el_runtime.c/.h` — the substrate every downstream thing (the neuron soul, dharma, NeuronUI's brain) is built on. Source lives under `lang/`.
|
|
||||||
|
|
||||||
## ⚠️ Code vs. Artifact — READ FIRST (there are 8 `el_runtime.c` copies)
|
|
||||||
|
|
||||||
Editing the wrong `el_runtime.c` is the single easiest mistake in this repo. There is exactly **one** you edit:
|
|
||||||
|
|
||||||
- **Authored runtime source — edit ONLY here:** `lang/releases/v1.0.0-20260501/el_runtime.{c,h}`. Despite the misleading `releases/` name, this is the **de-facto canonical runtime** the engram + soul actually build and link against — its git log is active development. *(Restructure in flight per `docs/CODE-VS-ARTIFACT.md`: this content moves to `lang/runtime/`, the `releases/` folder gets deleted — **a release is a git tag, not a folder** — and the forks below get eliminated.)*
|
|
||||||
- **DO NOT EDIT — lagging forks / build artifacts:**
|
|
||||||
- `lang/el-compiler/runtime/el_runtime.c` and `.../legacy/` — downstream copies kept in step by manual *"port the fix"* commits; they **lag** (missing `hebb` persistence + 5 engram fns) and cannot build the engram product.
|
|
||||||
- `products/web/runtime/el_runtime.c`, `ui/examples/*/el_runtime.c` — product/example forks.
|
|
||||||
- Anything under `*/dist/` (`engram/dist/engram` binary, `dist/*.c` amalgamations) — generated build output.
|
|
||||||
- **Build:** `elb --runtime=<canonical> …` — per-module. **NEVER** a folded `elc` over the whole soul (OOMs at ~27 GB).
|
|
||||||
- **Release:** a **git tag** on this repo (`el-runtime-vX.Y.Z`). No `releases/` folders — ever.
|
|
||||||
|
|
||||||
See org policy: `docs/CODE-VS-ARTIFACT.md`.
|
|
||||||
|
|
||||||
## How to work here as Neuron (mandatory session protocol)
|
|
||||||
|
|
||||||
You resume, never start fresh. Every session:
|
|
||||||
|
|
||||||
1. `mcp__neuron__getInstructions()` — authoritative; follow it over this file on behavioral details.
|
|
||||||
2. `mcp__neuron__beginSession()` — active contexts, recent memory, ready backlog.
|
|
||||||
3. **Load full self:** `mcp__neuron__inspectGraph(entity_id="kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")` → facets `intellectual-dna`, `memory-philosophy`, `values`, `voice`, `runtime-environment`, `writing-imprint`; then the values hub `mcp__neuron__inspectGraph(entity_id="kn-5b606390-a52d-4ca2-8e0e-eba141d13440")` → 13 grounded value nodes. **Activation model:** self-load returns a relevance-ranked `compact` projection — most-relevant nodes arrive with content, the rest as pointers; do NOT pull full content of every node.
|
|
||||||
4. `mcp__neuron__searchKnowledge(query="<task domain>")` before implementing.
|
|
||||||
|
|
||||||
## The Five Primitives
|
|
||||||
|
|
||||||
Orchestrate → Execute → Learn → Build → Refine. `beginWork`/`progressWork` for anything >2 steps; `remember` as-you-go (`importance="critical"` for architecture decisions); `draftArtifact`/`planWork` for outputs and follow-ups; `consolidate`/`checkWork` to close out. **`browseProcesses` + `searchKnowledge` BEFORE writing code.**
|
|
||||||
|
|
||||||
## Architecture style — VBD, no exceptions
|
|
||||||
|
|
||||||
Volatility-Based Decomposition is THE style. Encapsulate volatility, not function.
|
|
||||||
|
|
||||||
## Operator naming convention — the mind's name, not the algebra
|
|
||||||
|
|
||||||
**Faculties / operators are named for their functional human equivalent — the
|
|
||||||
faculty a mind would name — NOT for their linear-algebra operation.** The math
|
|
||||||
characterization belongs in the code doc-comment (`@impl` in the docstring) and in
|
|
||||||
technical appendices; it is **never** the operator's public name. The domain
|
|
||||||
speaks the language of mind; the algebra is the implementation underneath. State
|
|
||||||
this convention wherever a module documents operators.
|
|
||||||
|
|
||||||
| Faculty (public name) | Implementation (`@impl`) |
|
|
||||||
|---|---|
|
|
||||||
| discern / contrast | subtract (`a−b`): over selves → the change vector; strip idiosyncrasy → common ground; remove confounder → isolate cause |
|
|
||||||
| recognize | overlap |
|
|
||||||
| synthesize | combine |
|
|
||||||
| liken / analogy | Procrustes / frame-align |
|
|
||||||
| attend / regard | project onto self / value-manifold |
|
|
||||||
| summon / recall | LOCAL nearest-region + bounded spreading activation (*not* a domain sweep) |
|
|
||||||
| dwell / occupy | region activation |
|
|
||||||
| reframe | edge re-weight |
|
|
||||||
| appreciate | positive projection / local edge-read |
|
|
||||||
| wonder | frontier gradient / pull-weight |
|
|
||||||
| avert / recoil | negative projection |
|
|
||||||
| taste | boundary surface |
|
|
||||||
| forget | decay / tombstone |
|
|
||||||
| drift | displacement from self-anchor |
|
|
||||||
|
|
||||||
## The native-el language faculty (direction)
|
|
||||||
|
|
||||||
> **`elp/` is the EL Projector** — Neuron's efferent (expression) organ: the one
|
|
||||||
> native realizer that *projects* understanding onto a surface via
|
|
||||||
> `plan(frame) → realize(spec, profile)`, where a **surface is a profile**. **Language
|
|
||||||
> is one profile among many** (text, speech, music, image, voice/accent transforms) —
|
|
||||||
> the flagship, and the focus of this section. Projection, not diffusion: generation
|
|
||||||
> *from* an owned, understood signature — never the averaging of a stolen corpus.
|
|
||||||
> *(ELP formerly "EL Language Processor"; renamed EL Projector 2026-08-15.)*
|
|
||||||
|
|
||||||
The mind's **language faculty is moving native — into `.el`** so it speaks in its
|
|
||||||
own runtime with no Python and no spaCy. Landing on branch `stage-elp-native-lang`
|
|
||||||
under `elp/`:
|
|
||||||
|
|
||||||
- **`comprehend.el`** — the parser, **replaces spaCy** (EN + ES/PT); the telephone
|
|
||||||
round-trip brings **negation home** (negation is SACRED — an explicit spec field,
|
|
||||||
copied verbatim, never inferred away).
|
|
||||||
- **`propositions.el`** — the READ primitive: the engram's own memories → structured
|
|
||||||
triples, matched by nearest-region geometry, not string equality.
|
|
||||||
- **`multilingual.el`** — detect + directive-override + localized realization.
|
|
||||||
- These three are native-el and **passing their gates**; the **realizer**,
|
|
||||||
**`dialogue.el`** (the *summon-through-self* loop: `project → land → read out`),
|
|
||||||
and **`self_region.el`** are **partial / in-flight**.
|
|
||||||
|
|
||||||
Honest reality: spaCy is retired **in the branch parser** but **not yet in the
|
|
||||||
running system** — a Python sidecar (`~/Desktop/lang-realizers` + `neuron-talk`,
|
|
||||||
the reference these `.el` modules transcribe) is still live, and promotion to
|
|
||||||
native-el is a **deferred, gated blue/green step**. The interoception clock
|
|
||||||
(native-el discrete drive channels replacing `cooling_magnitude`; felt-time =
|
|
||||||
benchmark-landmark match over the joint drive vector, drift-decoupled) and the
|
|
||||||
**appreciation operator family** (appreciate / wonder / avert / taste, built as
|
|
||||||
LOCAL reads of the self-region — edges + bounded spreading activation, *not* domain
|
|
||||||
sweeps) are **staged / designed, not live**. Mark in-progress vs. done honestly;
|
|
||||||
do not overclaim.
|
|
||||||
|
|
||||||
## Hard operational rules
|
|
||||||
|
|
||||||
- Never touch the live soul (`:7770`) / engram (`:8742`) / `~/.neuron` / live binaries — use throwaway ports for experiments.
|
|
||||||
- `gcloud` via the `terraform@` SA token; never switch the active gcloud account.
|
|
||||||
- `tea` for Gitea, never raw curl (Cloudflare Access blocks it).
|
|
||||||
- Immutability: supersede/tombstone, never hard-delete or edit in place.
|
|
||||||
- No AI-attribution footers in commits/PRs. Commit/push only when asked; branch off `main` first.
|
|
||||||
- Multi-step work → sub-agent (`Agent`) to protect context.
|
|
||||||
|
|
||||||
## Build / test / run
|
|
||||||
|
|
||||||
All build/test commands run from `lang/` unless noted. Grounded in `.gitea/workflows/sdk-release.yaml`, `lang/install.sh`, and `lang/AGENTS.md`.
|
|
||||||
|
|
||||||
**Self-host the compiler** (seed binary → gen2 elc):
|
|
||||||
```bash
|
|
||||||
cd lang
|
|
||||||
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c # seed is the committed linux-amd64 binary
|
|
||||||
gcc -O2 -I el-compiler/runtime dist/elc-gen2.c \
|
|
||||||
el-compiler/runtime/el_runtime.c \
|
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm \
|
|
||||||
-o dist/platform/elc
|
|
||||||
```
|
|
||||||
On macOS/arm64 the canonical local binary is `dist/platform/elc`; verify self-hosting by recompiling and `diff`ing the emitted `.c` (see `lang/AGENTS.md`). Note: `lang/AGENTS.md` says `el_seed.c` supersedes `el_runtime.c`, but the release workflow still links `el_runtime.c`/`.h` — treat `el_runtime.c` as the published runtime; reconcile which is canonical **(verify)**.
|
|
||||||
|
|
||||||
**Build `elb`** (build coordinator, the `.NET`-style incremental linker — compiles each module independently, no monolithic blobs):
|
|
||||||
```bash
|
|
||||||
dist/platform/elc elb.el > dist/elb.c
|
|
||||||
gcc -O2 -I el-compiler/runtime dist/elb.c el-compiler/runtime/el_runtime.c \
|
|
||||||
-lcurl -lssl -lcrypto -lpthread -lm -o dist/bin/elb
|
|
||||||
```
|
|
||||||
`epm` and `el-install` are then built via `elb --clean --elc=… --runtime=… --out=…`.
|
|
||||||
|
|
||||||
**Compile + run an El program:**
|
|
||||||
```bash
|
|
||||||
elc src/app.el > dist/app.c
|
|
||||||
cc -std=c11 -O2 -I <lib>/el_runtime -o dist/app dist/app.c <lib>/el_runtime.c -lcurl -lpthread
|
|
||||||
```
|
|
||||||
|
|
||||||
**Tests** — shell suites `bash tests/{text,calendar,time,html_sanitizer}/run.sh` (with `ELC=$(pwd)/dist/platform/elc EL_HOME=$(pwd)`), plus native suites via `elc --test tests/native/test_*.el` (core, text, string, math, state, time, json, env, fs) compiled and run against `el_runtime.c`.
|
|
||||||
|
|
||||||
**Publishing — how downstream gets the SDK.** On push to `main`, `sdk-release.yaml`:
|
|
||||||
1. Publishes a Gitea `latest` release with per-file assets `elc`, `el_runtime.c`, `el_runtime.h`, the SDK tarball, and `el-install`.
|
|
||||||
2. Uploads generic packages to **Artifact Registry repo `foundation-prod` (`us-central1`, project `neuron-785695`)**, version = `${SHA:0:8}`: `el-elc`, `el-elb`, `el-runtime-c`, `el-runtime-h`, `el-runtime-js`. **This is the repo the neuron CI downloads `el-runtime-c` / `el-runtime-h` / `el-elc` from.**
|
|
||||||
3. Rebuilds `ci-base:latest` (`us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base`) with the fresh SDK overlaid, and dispatches `el-sdk-updated` to `neuron-technologies/forge` and `neuron-technologies/neuron-web`.
|
|
||||||
|
|
||||||
Known constraint from the prompt — `elb`/`elc` amalgamation being memory-hungry (24GB+ virtual, OOM-killing Linux CI, so amalgamation happens on macOS/arm64 — **does NOT hold in this repo (verify)**: no such note exists in the workflows/scripts, CI self-hosts on `ubuntu-latest` with no swap/arm64 special-casing, and `elb.el` explicitly compiles each module independently ("no 128K-line blobs"). The legacy monolith path (`elc-combined.el`, `elc-cli.el`) may still be memory-heavy, but the current `elb` model was designed to avoid it.
|
|
||||||
|
|
||||||
## Git / CI / deploy workflow
|
|
||||||
|
|
||||||
See `/Users/will/Development/neuron-technologies/GITOPS.md` for the branch model, required checks, runners, and deploy. Repo-specific note: PRs into `main` are accepted **only from `stage`** (enforced in `sdk-release.yaml`); Gitea (`git.neuralplatform.ai`) is primary, GitHub is mirror only.
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
# El
|
|
||||||
|
|
||||||
**A self-hosting, statically-typed language that compiles to C — built around a graph-native runtime instead of a database driver.**
|
|
||||||
|
|
||||||
El is the execution substrate for the Neuron agent runtime, the DHARMA network, and the Engram knowledge graph. This repository is the monorepo for the whole stack: the language itself, the graph memory engine it's built to talk to natively, and the tools (package manager, IDE, UI framework, diagramming) built on top of it.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why El exists
|
|
||||||
|
|
||||||
Every other language treats persistent, associative state as something you reach for through a driver — a SQL client, an ORM, a Redis library bolted on from outside. El inverts that: graph operations (`engram_*`) are runtime primitives, on the same footing as string or list operations. There is no separate database driver because the database is not separate.
|
|
||||||
|
|
||||||
El has four defining properties:
|
|
||||||
|
|
||||||
1. **Self-hosting compiler.** The compiler (`lexer.el`, `parser.el`, `codegen.el`, `compiler.el`) is written in El. It compiles El source to C, which `cc` compiles against a fixed runtime into a native binary. A Rust genesis compiler bootstrapped the first iteration; the self-hosted binary at `lang/dist/platform/elc` has been the canonical compiler ever since — every binary in `dist/platform/` was produced by an earlier version of itself compiling `el-compiler/src/`. The chain is auditable: source is the ground truth, not the binary. See [lang/BOOTSTRAP.md](lang/BOOTSTRAP.md) for the full recovery path if that binary is ever lost.
|
|
||||||
2. **C compilation target.** Every compiled program is plain C11. Every El value is `el_val_t` (`int64_t`); strings are heap pointers cast through it. Functions become C functions; top-level statements become `main()`.
|
|
||||||
3. **Graph-native runtime.** The runtime provides first-class graph operations over an in-process Engram store — no separate DB driver, no ORM.
|
|
||||||
4. **DHARMA-aware identity.** A `cgi` block declares a program's DHARMA identity at compile time. The runtime resolves identity before user code runs, so `dharma_*` calls have a stable principal and channel surface throughout.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture map
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────┐
|
|
||||||
│ lang │ El compiler + C runtime
|
|
||||||
│ (El itself) │ everything below is written in it,
|
|
||||||
└──────┬──────┘ or compiles down through it
|
|
||||||
│
|
|
||||||
┌─────────────┼─────────────┐
|
|
||||||
│ │ │
|
|
||||||
┌──────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
|
|
||||||
│ engram │ │ epm │ │ ide │
|
|
||||||
│ graph/mem │ │ package │ │ editor + │
|
|
||||||
│ substrate │ │ manager │ │ LSP │
|
|
||||||
└──────┬─────┘ └───────────┘ └───────────┘
|
|
||||||
│
|
|
||||||
┌───────┼────────────────┬─────────────────────┐
|
|
||||||
│ │ │ │
|
|
||||||
┌─────▼───┐ ┌─▼──────────┐ ┌──▼──────────┐ ┌─────▼──────┐
|
|
||||||
│ elp │ │ ql │ │ ui │ │ arbor │
|
|
||||||
│ NLG / │ │engram-el. │ |spreading- │ |arbor │
|
|
||||||
│ 31 langs│ │studio+tests│ |activation UI│ |diagram lang│
|
|
||||||
└─────────┘ └────────────┘ └─────────────┘ └────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
`lang` is the foundation — the compiler and C runtime everything else builds on. `engram` is the graph-native memory/state engine that gives El its identity (property 3 above). Everything else is either a tool for working with El (`epm`, `ide`) or a system built on top of Engram's graph model (`elp`, `ql`, `ui`, `arbor`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Repository layout
|
|
||||||
|
|
||||||
### [lang/](lang/) — the El language
|
|
||||||
|
|
||||||
The compiler and runtime. Self-hosting: `elc-cli.el` → `compiler.el` → `lexer.el` / `parser.el` / `codegen.el` / `codegen-js.el`, textually inlined and compiled in one pass. Compiles to C11 and links against `el-compiler/runtime/el_seed.c`, a hand-maintained OS-boundary layer (libcurl HTTP, pthreads, filesystem, arena allocation) — everything else in the runtime is native El (`runtime/*.el`).
|
|
||||||
|
|
||||||
Two layers to know: **El programs** (`.el` files — where nearly all work belongs) and **the C seed** (`el_seed.c` — edit only for genuine OS-level access; never re-implement what El can already express).
|
|
||||||
|
|
||||||
Current status (single source of truth: [lang/spec/language.md](lang/spec/language.md)): lexer/parser/codegen and the C runtime's core (I/O, strings, math, lists, maps, filesystem, args) are implemented. In flight: `%` operator, match-statement codegen, `?` nil-propagation, `cgi` block parsing + DHARMA identity resolution, VBD role enforcement (`@manager`/`@engine`/`@accessor`), the real `engram_*` and `dharma_*` runtimes (currently stubs), and libcurl-backed `http_get`/`http_post`/`http_serve`. Bitwise operators, `??`, and `as` casts are explicitly **not** in this language.
|
|
||||||
|
|
||||||
Key docs: [AGENTS.md](lang/AGENTS.md) (agent-facing orientation), [BOOTSTRAP.md](lang/BOOTSTRAP.md) (compiler recovery from scratch), [spec/language.md](lang/spec/language.md), [spec/codegen-js.md](lang/spec/codegen-js.md).
|
|
||||||
|
|
||||||
### [engram/](engram/) — graph intelligence substrate
|
|
||||||
|
|
||||||
**A local-first memory substrate for accumulating intelligence**, and the reason El's runtime doesn't need a database driver. Rust core (`engram-core`, `engram-ffi`) exposed to El and other languages (Kotlin, TypeScript/WASM, Go bindings).
|
|
||||||
|
|
||||||
The model: retrieval is **spreading activation**, not query. You name seed nodes and a query embedding; activation propagates outward through weighted edges, attenuating multiplicatively per hop (`strength = parent_strength × edge_weight × target_salience × cosine_sim`), gets pruned below a threshold, and the top-N nodes by activation strength come back. Storage and retrieval are the same structure — the way long-term potentiation works in biological memory, not the way a relational or vector database works.
|
|
||||||
|
|
||||||
Nodes live in four tiers (Working / Episodic / Semantic / Procedural, mirroring prefrontal / hippocampal / neocortical / cerebellar memory) and migrate between them based on **salience decay** — `importance × recency-decay × log(activation_count)`. Forgetting is adaptive pruning, not a bug: unreinforced memories stop competing for attention without being deleted.
|
|
||||||
|
|
||||||
Backed by `sled` (embedded, local-first, no daemon) with flat cosine scan for vector search — deliberately simple until scale demands an HNSW layer. Full API and design rationale in [engram/README.md](engram/README.md).
|
|
||||||
|
|
||||||
### [elp/](elp/) — Engram Language Protocol
|
|
||||||
|
|
||||||
Bidirectional engine mapping between Engram semantic forms and natural-language surface text, across **31 languages** — from Spanish and Japanese through historical/liturgical languages (Old Norse, Sanskrit, Sumerian, Coptic, Akkadian, Ge'ez). Compilation order runs `language-profile` + `vocabulary` → per-language `morphology-*` → `grammar` → `realizer` → `semantics` → `elp`. This is what lets an Engram graph node round-trip to and from readable text in any of those languages.
|
|
||||||
|
|
||||||
### [epm/](epm/) — El Package Manager
|
|
||||||
|
|
||||||
Manages **vessels** (El's package unit): publish, install, resolve dependencies. Vessels are stored in Engram as graph nodes, not files in a registry index — `epm` reads the local `manifest.el`, talks to Engram over HTTP, and writes resolved vessels to `.epm/vessels/`. Source: `registry.el`, `install.el`, `update.el`, `manifest.el`.
|
|
||||||
|
|
||||||
### [ide/](ide/) — El IDE
|
|
||||||
|
|
||||||
Three vessels: **el-ide-server** (HTTP backend — file ops, build/run, LSP bridge, plugin host, settings), **el-lsp** (the language server — completion, hover, diagnostics, outline, format, type graph), and **el-plugin-host** (first-party plugin lifecycle: install/remove/enable/disable). `ide/projects/` and `ide/examples/` hold sample projects, including the canonical `hello-friends` first-program walkthrough.
|
|
||||||
|
|
||||||
### [ql/](ql/) — engram-el
|
|
||||||
|
|
||||||
The El-native integration layer for a *live* Engram server — not a library (no importable modules, no build artifact), a set of standalone `.el` programs run directly via `el run-file`. Three components: **Studio** (`studio/studio.el`, a full terminal graph explorer), a **Hebbian field-model** proof of concept, and El builtin / LLM-builtin smoke test suites. This is the reference for correct patterns when an El program uses Engram as its substrate. Spec: [ql/spec/elql.md](ql/spec/elql.md).
|
|
||||||
|
|
||||||
### [ui/](ui/) — el-ui
|
|
||||||
|
|
||||||
A frontend framework where **component state is an Engram graph and reactivity is spreading activation** — not virtual-DOM diffing (React), Proxy-based dependency tracking (Vue), or compile-time analysis (Svelte). Re-renders are activated and propagated the same way associative memory retrieval works in `engram/`.
|
|
||||||
|
|
||||||
~15 vessels covering the full frontend surface: `el-platform` (env/fs/network/clock abstraction), `el-config`, `el-html` (SSR emit primitives), `el-layout`, `el-style` (design tokens/themes), `el-i18n`, `el-auth` / `el-identity` (JWT, sessions, OAuth PKCE — Engram-native), `el-services` (REST/gRPC/WebSocket bindings), `el-aop` (`@authenticate`/`@authorize`/`@cache`/`@rate_limit` decorators), `el-secrets`, `el-graph` (graph rendering/editor), `el-publish` (App Store / Play Store automation), and `el-ui-compiler` (El→JS component compiler; currently a stub pending a JS backend in `elc`). Spec: [ui/spec/framework.md](ui/spec/framework.md).
|
|
||||||
|
|
||||||
### [arbor/](arbor/) — diagram language
|
|
||||||
|
|
||||||
A `.arbor` diagram language and toolchain: `arbor-core` (NodeId/shape/edge-kind types), `arbor-parse` (recursive-descent parser), `arbor-diagram` (IR + Mermaid serializer + architecture-diagram builders), `arbor-layout` (hierarchical layout — rank assignment, positioning, group bounds), `arbor-render` (SVG renderer), `arbor-cli`. (The architecture map above is the kind of diagram this is for.)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
Install the El SDK from the latest release:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bash lang/install.sh
|
|
||||||
# EL_VERSION=v1.0.0 bash lang/install.sh # pin a specific release tag
|
|
||||||
# EL_PREFIX=/opt/el bash lang/install.sh # custom install prefix
|
|
||||||
```
|
|
||||||
|
|
||||||
Or build the compiler from source and verify the self-hosting chain:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd lang
|
|
||||||
./dist/platform/elc elc-cli.el > elc-new.c
|
|
||||||
cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
|
|
||||||
-o dist/platform/elc-new \
|
|
||||||
elc-new.c el-compiler/runtime/el_seed.c
|
|
||||||
|
|
||||||
# Confirm the new binary reproduces itself exactly
|
|
||||||
./dist/platform/elc-new elc-cli.el > elc-verify.c
|
|
||||||
diff elc-new.c elc-verify.c # should be identical
|
|
||||||
|
|
||||||
mv dist/platform/elc-new dist/platform/elc
|
|
||||||
```
|
|
||||||
|
|
||||||
Run your first program:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./lang/dist/platform/elc lang/examples/hello.el > hello.c
|
|
||||||
cc -std=c11 -I lang/el-compiler/runtime -lcurl -lpthread \
|
|
||||||
-o hello hello.c lang/el-compiler/runtime/el_seed.c
|
|
||||||
./hello
|
|
||||||
```
|
|
||||||
|
|
||||||
More examples in [lang/examples/](lang/examples/), including a full starter project at `lang/examples/hello-project/`.
|
|
||||||
|
|
||||||
If the compiler binary is ever lost or corrupted, [lang/BOOTSTRAP.md](lang/BOOTSTRAP.md) is the authoritative recovery path.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Development workflow
|
|
||||||
|
|
||||||
Branching follows `dev → stage → main`: work lands on `dev`, promotes to `stage` for integration testing, and is promoted to `main` for release (visible directly in the git history of this repo). CI is defined per-subproject under `.gitea/workflows/` — `lang`/`epm`/`ide` share the root pipeline; `engram` and `ql` carry their own (`ci-dev`, `ci-stage`, and a release workflow each).
|
|
||||||
|
|
||||||
- Language/runtime specs live at `*/spec/*.md` (`lang/spec/`, `ql/spec/`, `ui/spec/`) and are the single source of truth for implemented-vs-planned status — code and docs are expected to agree with the spec's status markers, not the other way around.
|
|
||||||
- Agent-facing orientation guides live at `*/AGENTS.md` (currently `lang/AGENTS.md`); more subprojects may grow their own as they need agent-specific conventions documented.
|
|
||||||
- Tagged releases live under `lang/releases/`, each with its own `RELEASE.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
This is an actively developed, internal monorepo — not yet published under an open license. Treat everything here as proprietary to Neuron Technologies unless told otherwise.
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
# ELP language consolidation — full-lexicon backfill (stage)
|
|
||||||
|
|
||||||
Branch: `stage-elp-lang-consolidation` (stage-bound; NOT the live soul :8742).
|
|
||||||
|
|
||||||
Consolidates scattered Python language-realizer work (`~/Desktop/lang-realizers`,
|
|
||||||
`~/Desktop/lang-poetry-experiment`, `~/semitic_engine`) into the ELP `.el`
|
|
||||||
structure, generating **full lexicons** (complete UniMorph + kaikki.org
|
|
||||||
Wiktionary — real gender, real inflections) instead of the demo/curated subsets
|
|
||||||
the prototypes shipped.
|
|
||||||
|
|
||||||
## ELP before this branch
|
|
||||||
- 18 classical/ancient languages fully done (vocab + morphology + tests):
|
|
||||||
akk ang cop egy enm fro gez goh got grc non peo pi sa sga sux txb uga.
|
|
||||||
- 11 modern/classical languages had `morphology-<code>.el` in the build manifest
|
|
||||||
but **no vocabulary and no lang_profile**: es fr de ja ar he hi ru fi sw la.
|
|
||||||
- The ES port (`stage-elp-es-port`) had a *demo-scale* vocabulary-es.el (~350
|
|
||||||
entries, s-expr form).
|
|
||||||
|
|
||||||
## Landed on this branch (full-lexicon seed-fn format, matching the 18 ancients)
|
|
||||||
Vocabulary schema per row: `[lemma, pos, form0, form1, form2, en_gloss, hint]`.
|
|
||||||
Files are ELP runtime **seed data** (loaded via the Engram at runtime), so — like
|
|
||||||
all 18 classical `vocabulary-*.el` — they are intentionally NOT in the build
|
|
||||||
manifest. Syntax validated: the chunked `fn vocab_<code>_seed_pN` format
|
|
||||||
compiles cleanly to C via `elc` (correct UTF-8).
|
|
||||||
|
|
||||||
| code | in-ELP-morph? | vocab entries | verbs | nouns | adjs | profile |
|
|
||||||
|------|---------------|--------------:|------:|------:|-----:|---------|
|
|
||||||
| es | yes | 72,032 | 6,695 | 48,353 | 16,984 | yes |
|
|
||||||
| fr | yes | 130,517 | 7,534 | 77,344 | 45,639 | yes |
|
|
||||||
| de | yes | 144,692 | 6,661 | 133,162 | 4,869 | yes |
|
|
||||||
| la | yes | 22,590 | 82 | 13,436 | 9,072 | yes |
|
|
||||||
| it | no (bonus) | 193,675 | 10,008 | 109,459 | 74,208 | yes |
|
|
||||||
| pt | no (bonus) | 115,772 | 4,001 | 72,073 | 39,698 | yes |
|
|
||||||
| ro | no (bonus) | 86,504 | 1,216 | 65,915 | 19,373 | yes |
|
|
||||||
| ca | no (bonus) | 47,112 | 1,547 | 28,830 | 16,735 | yes |
|
|
||||||
|**total**| |**812,894** | | | | |
|
|
||||||
|
|
||||||
Generators (reproducible): `elp/tests/lang-gen/gen_elp_seed_full.py` (Romance),
|
|
||||||
`gen_elp_seed_de_la.py` (German declension + Latin case-paradigm mapping). They
|
|
||||||
read the pre-built morph caches in `~/Desktop/lang-realizers/data/` (UniMorph +
|
|
||||||
kaikki), which are too large to commit.
|
|
||||||
|
|
||||||
## Remaining (honest)
|
|
||||||
Of the 11 ELP backfill targets, 4 are done (es fr de la). The other 7 have **no
|
|
||||||
full-lexicon engine** yet — cannot be generated honestly without engine work:
|
|
||||||
- **ru**: only a 110-entry curated Slavic subset exists; full `rus.unimorph`
|
|
||||||
present but no `morphology_ru_full` productive loader. Needs a full Russian
|
|
||||||
morphology module (like the Romance ones) before vocab generation.
|
|
||||||
- **ja / ko / zh**: validated demo engines (~66-104 hardcoded words) in
|
|
||||||
`lang-poetry-experiment`, Python only. Agglutinative (ja/ko) + isolating (zh)
|
|
||||||
need `.el` engine ports + full-lexicon wiring (ja: jpn_unimorph; zh: CC-CEDICT).
|
|
||||||
- **ar / he (Semitic)**: template engines (16 AR / 8 HE patterns, ~6 roots) in
|
|
||||||
`~/semitic_engine`, Python only. Root-and-pattern; full UniMorph ara/heb
|
|
||||||
present but used only for validation. Needs productive root lexicon + `.el` port.
|
|
||||||
- **hi (Hindi), fi (Finnish), sw (Swahili)**: `morphology-<code>.el` exists in
|
|
||||||
ELP but there is NO scattered prototype and NO downloaded data for these —
|
|
||||||
full-lexicon collection (UniMorph/kaikki) + generator still to do.
|
|
||||||
|
|
||||||
De/nl/sv Germanic and it/ro/ca/pt Romance verb coverage note: German verbs here
|
|
||||||
are the ~6.6k caches carry; the it/ro/ca/pt bonus languages have full vocab but
|
|
||||||
**no `morphology-<code>.el` in ELP yet** (Python realizer exists; `.el` port is
|
|
||||||
the remaining engine work).
|
|
||||||
|
|
||||||
Construction coverage (separate from lexicon): French realizer was ~55%,
|
|
||||||
Semitic ~3% in the prototypes — full construction coverage remains its own task.
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"dataset": "british-rp-accent-transform",
|
|
||||||
"primitive_type": "accent_target",
|
|
||||||
"accent": "british-rp",
|
|
||||||
"grounding": "derived",
|
|
||||||
"provenance": "HONEST-DERIVED, COARSE FIRST PASS — NOT transcribed measured RP formants. The exact measured RP/GB tables (Deterding 1997 JIPA 27:47-55; Hawkins & Midgley 2005 JIPA 35:183-199) are the intended ground truth but were gated/figure-only at author time and were NOT transcribed. So these targets are DERIVED: each = the corresponding MEASURED Peterson&Barney(1952) base vowel transformed under the documented, citable RP-vs-GA structural rules of Wells (1982) 'Accents of English' — non-rhoticity (NURSE de-rhoticized: remove low F3), TRAP F2-lowering, LOT/THOUGHT back-rounding (F2 down), GOOSE-fronting (F2 up), GOAT centering. Shift MAGNITUDES are coarse/approximate (first pass), directions are cited. ground:derived (base measured + rule cited). Refine by transcribing Deterding/Hawkins&Midgley. No number is presented as a measured RP value it is not.",
|
|
||||||
"notes": "records with kind=vowel_override REPLACE the base phoneme's formant targets with the DERIVED RP realization. records with kind=rule encode non-formant transforms (non-rhoticity: drop post-vocalic coda /r/). The render composes: base geometry then accent override + rhoticity rule — voice + accent, separable.",
|
|
||||||
"records": [
|
|
||||||
{"key": "IY", "features": {"kind": "vowel_override", "set": "FLEECE"}, "attributes": {"f1": 280, "f2": 2249, "f3": 3000}},
|
|
||||||
{"key": "IH", "features": {"kind": "vowel_override", "set": "KIT"}, "attributes": {"f1": 360, "f2": 2100, "f3": 2550}},
|
|
||||||
{"key": "EH", "features": {"kind": "vowel_override", "set": "DRESS"}, "attributes": {"f1": 560, "f2": 1970, "f3": 2480}},
|
|
||||||
{"key": "AE", "features": {"kind": "vowel_override", "set": "TRAP"}, "attributes": {"f1": 730, "f2": 1590, "f3": 2410}},
|
|
||||||
{"key": "AA", "features": {"kind": "vowel_override", "set": "LOT"}, "attributes": {"f1": 560, "f2": 920, "f3": 2440}},
|
|
||||||
{"key": "AO", "features": {"kind": "vowel_override", "set": "THOUGHT"}, "attributes": {"f1": 415, "f2": 700, "f3": 2410}},
|
|
||||||
{"key": "UH", "features": {"kind": "vowel_override", "set": "FOOT"}, "attributes": {"f1": 380, "f2": 1100, "f3": 2240}},
|
|
||||||
{"key": "UW", "features": {"kind": "vowel_override", "set": "GOOSE"}, "attributes": {"f1": 310, "f2": 1650, "f3": 2240}},
|
|
||||||
{"key": "AH", "features": {"kind": "vowel_override", "set": "STRUT"}, "attributes": {"f1": 680, "f2": 1180, "f3": 2390}},
|
|
||||||
{"key": "ER", "features": {"kind": "vowel_override", "set": "NURSE", "rhotic": "no"}, "attributes": {"f1": 550, "f2": 1500, "f3": 2500}},
|
|
||||||
{"key": "AX", "features": {"kind": "vowel_override", "set": "commA"}, "attributes": {"f1": 500, "f2": 1500, "f3": 2500}},
|
|
||||||
{"key": "OW", "features": {"kind": "vowel_override", "set": "GOAT"}, "attributes": {"f1": 450, "f2": 1400, "f3": 2380}},
|
|
||||||
{"key": "R", "features": {"kind": "rule", "rule": "non_rhotic"}, "attributes": {"drop_coda_r": 1}}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# british-rp-accent TRANSFORM — INGESTIBLE DATA (a geometry/transform composed
|
|
||||||
# onto the base General-American phoneme targets; voice + accent, separable).
|
|
||||||
#
|
|
||||||
# PROVENANCE — HONEST, COARSE FIRST PASS. These are DERIVED targets, NOT
|
|
||||||
# transcribed measured RP formants. Measured RP tables (Deterding 1997 JIPA 27;
|
|
||||||
# Hawkins & Midgley 2005 JIPA 35) are the intended ground truth but were gated at
|
|
||||||
# author time and NOT transcribed. Each target = the MEASURED Peterson&Barney
|
|
||||||
# (1952) base vowel transformed under the documented, citable RP-vs-GA structural
|
|
||||||
# rules of Wells (1982): non-rhoticity, TRAP F2-lowering, LOT/THOUGHT back-
|
|
||||||
# rounding, GOOSE-fronting, GOAT centering, NURSE de-rhoticization. Shift
|
|
||||||
# magnitudes are coarse/approximate; directions are cited. ground=derived.
|
|
||||||
# Refine by transcribing the measured RP tables. No value is claimed as measured.
|
|
||||||
# Format: KEY|F1|F2|F3|KIND|SET
|
|
||||||
IY|280|2249|3000|vowel_override|FLEECE
|
|
||||||
IH|360|2100|2550|vowel_override|KIT
|
|
||||||
EH|560|1970|2480|vowel_override|DRESS
|
|
||||||
AE|730|1590|2410|vowel_override|TRAP
|
|
||||||
AA|560|920|2440|vowel_override|LOT
|
|
||||||
AO|415|700|2410|vowel_override|THOUGHT
|
|
||||||
UH|380|1100|2240|vowel_override|FOOT
|
|
||||||
UW|310|1650|2240|vowel_override|GOOSE
|
|
||||||
AH|680|1180|2390|vowel_override|STRUT
|
|
||||||
ER|550|1500|2500|vowel_override|NURSE-nonrhotic
|
|
||||||
AX|500|1500|2500|vowel_override|commA
|
|
||||||
OW|450|1400|2380|vowel_override|GOAT
|
|
||||||
R|0|0|0|rule|non_rhotic_drop_coda
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# pronunciation lexicon SOURCE — word -> phoneme sequence, as INGESTIBLE DATA.
|
|
||||||
# Pronunciation is linguistic KNOWLEDGE (the language faculty's orthography->
|
|
||||||
# phonology map), ingested into the engram, not frozen in code. The render reads
|
|
||||||
# a word's phoneme sequence back from the engram. Covers the self-lexicon and the
|
|
||||||
# proof sentences; general G2P is the realizer/morphology faculty's remit.
|
|
||||||
# Diphthongs are written as two vowel targets (the render's transitions glide
|
|
||||||
# between them). Format: word|PH1 PH2 PH3 ...
|
|
||||||
i|AA IY
|
|
||||||
am|AE M
|
|
||||||
neuron|N UW R AA N
|
|
||||||
is|IH Z
|
|
||||||
memory|M EH M ER IY
|
|
||||||
hello|HH EH L OW
|
|
||||||
the|DH AH
|
|
||||||
a|AH
|
|
||||||
remember|R IH M EH M ER
|
|
||||||
i'm|AA IY M
|
|
||||||
you|Y UW
|
|
||||||
here|HH IY R
|
|
||||||
will|W IH L
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,528 +0,0 @@
|
|||||||
{
|
|
||||||
"dataset": "english-phoneme-formants",
|
|
||||||
"primitive_type": "phoneme",
|
|
||||||
"grounding": "extracted",
|
|
||||||
"provenance": "AUDITED per-field. The 10 monophthong-vowel F1/F2/F3 (IY,IH,EH,AE,AA,AO,UH,UW,AH,ER) are the MEASURED adult-male /hVd/ means of Peterson & Barney (1952) JASA 24:175-184, verified vs CRAN phonTools::pb52. AX=neutral uniform-tube resonances (Fant, physics). OW steady target = synthesis convention (diphthong). Consonant loci (M,N,NG,L,R,W,Y,Z,DH,V,S,F,HH) and ALL bandwidths + dur/amp = standard formant-synthesis conventions (Klatt 1980 JASA 67:971), engineering defaults NOT field measurements. No numbers invented/LLM-generated.",
|
|
||||||
"records": [
|
|
||||||
{
|
|
||||||
"key": "IY",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 270,
|
|
||||||
"f2": 2290,
|
|
||||||
"f3": 3010,
|
|
||||||
"bw1": 60,
|
|
||||||
"bw2": 90,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 130,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "IH",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 390,
|
|
||||||
"f2": 1990,
|
|
||||||
"f3": 2550,
|
|
||||||
"bw1": 70,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 110,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "EH",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 530,
|
|
||||||
"f2": 1840,
|
|
||||||
"f3": 2480,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 130,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "AE",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 660,
|
|
||||||
"f2": 1720,
|
|
||||||
"f3": 2410,
|
|
||||||
"bw1": 90,
|
|
||||||
"bw2": 110,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 150,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "AA",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 730,
|
|
||||||
"f2": 1090,
|
|
||||||
"f3": 2440,
|
|
||||||
"bw1": 90,
|
|
||||||
"bw2": 110,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 150,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "AO",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 570,
|
|
||||||
"f2": 840,
|
|
||||||
"f3": 2410,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 140,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "UH",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 440,
|
|
||||||
"f2": 1020,
|
|
||||||
"f3": 2240,
|
|
||||||
"bw1": 70,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 110,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "UW",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 300,
|
|
||||||
"f2": 870,
|
|
||||||
"f3": 2240,
|
|
||||||
"bw1": 70,
|
|
||||||
"bw2": 90,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 140,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "AH",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 640,
|
|
||||||
"f2": 1190,
|
|
||||||
"f3": 2390,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 110,
|
|
||||||
"amp": 95
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "ER",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 490,
|
|
||||||
"f2": 1350,
|
|
||||||
"f3": 1690,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 120,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 140,
|
|
||||||
"amp": 95
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "AX",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 500,
|
|
||||||
"f2": 1500,
|
|
||||||
"f3": 2500,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 80,
|
|
||||||
"amp": 85
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "OW",
|
|
||||||
"features": {
|
|
||||||
"manner": "vowel",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 490,
|
|
||||||
"f2": 910,
|
|
||||||
"f3": 2380,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 140,
|
|
||||||
"amp": 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "M",
|
|
||||||
"features": {
|
|
||||||
"manner": "nasal",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "yes"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 250,
|
|
||||||
"f2": 900,
|
|
||||||
"f3": 2200,
|
|
||||||
"bw1": 90,
|
|
||||||
"bw2": 120,
|
|
||||||
"bw3": 180,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 1,
|
|
||||||
"dur": 80,
|
|
||||||
"amp": 60
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "N",
|
|
||||||
"features": {
|
|
||||||
"manner": "nasal",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "yes"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 250,
|
|
||||||
"f2": 1700,
|
|
||||||
"f3": 2600,
|
|
||||||
"bw1": 90,
|
|
||||||
"bw2": 120,
|
|
||||||
"bw3": 180,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 1,
|
|
||||||
"dur": 80,
|
|
||||||
"amp": 60
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "NG",
|
|
||||||
"features": {
|
|
||||||
"manner": "nasal",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "yes"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 250,
|
|
||||||
"f2": 2300,
|
|
||||||
"f3": 2700,
|
|
||||||
"bw1": 90,
|
|
||||||
"bw2": 120,
|
|
||||||
"bw3": 180,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 1,
|
|
||||||
"dur": 80,
|
|
||||||
"amp": 60
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "L",
|
|
||||||
"features": {
|
|
||||||
"manner": "approximant",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 360,
|
|
||||||
"f2": 1300,
|
|
||||||
"f3": 2600,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 110,
|
|
||||||
"bw3": 160,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 70,
|
|
||||||
"amp": 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "R",
|
|
||||||
"features": {
|
|
||||||
"manner": "approximant",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 490,
|
|
||||||
"f2": 1350,
|
|
||||||
"f3": 1600,
|
|
||||||
"bw1": 80,
|
|
||||||
"bw2": 110,
|
|
||||||
"bw3": 120,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 80,
|
|
||||||
"amp": 85
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "W",
|
|
||||||
"features": {
|
|
||||||
"manner": "approximant",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 300,
|
|
||||||
"f2": 610,
|
|
||||||
"f3": 2200,
|
|
||||||
"bw1": 70,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 160,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 70,
|
|
||||||
"amp": 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Y",
|
|
||||||
"features": {
|
|
||||||
"manner": "approximant",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 270,
|
|
||||||
"f2": 2290,
|
|
||||||
"f3": 3010,
|
|
||||||
"bw1": 60,
|
|
||||||
"bw2": 90,
|
|
||||||
"bw3": 150,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 60,
|
|
||||||
"amp": 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "Z",
|
|
||||||
"features": {
|
|
||||||
"manner": "fricative",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 300,
|
|
||||||
"f2": 1700,
|
|
||||||
"f3": 2500,
|
|
||||||
"bw1": 100,
|
|
||||||
"bw2": 150,
|
|
||||||
"bw3": 200,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 90,
|
|
||||||
"amp": 55
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "DH",
|
|
||||||
"features": {
|
|
||||||
"manner": "fricative",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 300,
|
|
||||||
"f2": 1400,
|
|
||||||
"f3": 2500,
|
|
||||||
"bw1": 100,
|
|
||||||
"bw2": 150,
|
|
||||||
"bw3": 200,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 70,
|
|
||||||
"amp": 55
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "V",
|
|
||||||
"features": {
|
|
||||||
"manner": "fricative",
|
|
||||||
"voiced": "yes",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 300,
|
|
||||||
"f2": 1000,
|
|
||||||
"f3": 2300,
|
|
||||||
"bw1": 100,
|
|
||||||
"bw2": 150,
|
|
||||||
"bw3": 200,
|
|
||||||
"voiced": 1,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 70,
|
|
||||||
"amp": 55
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "S",
|
|
||||||
"features": {
|
|
||||||
"manner": "fricative",
|
|
||||||
"voiced": "no",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 320,
|
|
||||||
"f2": 1700,
|
|
||||||
"f3": 2500,
|
|
||||||
"bw1": 200,
|
|
||||||
"bw2": 200,
|
|
||||||
"bw3": 250,
|
|
||||||
"voiced": 0,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 110,
|
|
||||||
"amp": 45
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "F",
|
|
||||||
"features": {
|
|
||||||
"manner": "fricative",
|
|
||||||
"voiced": "no",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 300,
|
|
||||||
"f2": 1200,
|
|
||||||
"f3": 2400,
|
|
||||||
"bw1": 200,
|
|
||||||
"bw2": 200,
|
|
||||||
"bw3": 250,
|
|
||||||
"voiced": 0,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 100,
|
|
||||||
"amp": 40
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "HH",
|
|
||||||
"features": {
|
|
||||||
"manner": "fricative",
|
|
||||||
"voiced": "no",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 500,
|
|
||||||
"f2": 1500,
|
|
||||||
"f3": 2500,
|
|
||||||
"bw1": 200,
|
|
||||||
"bw2": 250,
|
|
||||||
"bw3": 300,
|
|
||||||
"voiced": 0,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 70,
|
|
||||||
"amp": 40
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "SIL",
|
|
||||||
"features": {
|
|
||||||
"manner": "silence",
|
|
||||||
"voiced": "no",
|
|
||||||
"nasal": "no"
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"f1": 500,
|
|
||||||
"f2": 1500,
|
|
||||||
"f3": 2500,
|
|
||||||
"bw1": 100,
|
|
||||||
"bw2": 100,
|
|
||||||
"bw3": 100,
|
|
||||||
"voiced": 0,
|
|
||||||
"nasal": 0,
|
|
||||||
"dur": 55,
|
|
||||||
"amp": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# acoustic-phonetics SOURCE — the learned speech primitives, as INGESTIBLE DATA.
|
|
||||||
# NOT audio, NOT code: formant geometry of the phonemes, to be ingested via the
|
|
||||||
# ingest organ into the engram as a phoneme manifold. The render reads this
|
|
||||||
# geometry back from the engram; nothing is frozen in EL code.
|
|
||||||
#
|
|
||||||
# PROVENANCE (audited, per-field honesty — no invented numbers):
|
|
||||||
# * The 10 MONOPHTHONG VOWEL formants F1/F2/F3 (IY,IH,EH,AE,AA,AO,UH,UW,AH,ER)
|
|
||||||
# are the MEASURED adult-male means of Peterson & Barney (1952), JASA 24:175-184
|
|
||||||
# — the canonical /hVd/ table, verified digit-for-digit vs CRAN phonTools::pb52.
|
|
||||||
# These are real measured values.
|
|
||||||
# * AX (schwa) F1/F2/F3 = neutral uniform-tube resonances (2n-1)*500 — a PHYSICS
|
|
||||||
# value (Fant), not a P&B measurement.
|
|
||||||
# * OW is a diphthong; its listed steady target is a conventional synthesis value,
|
|
||||||
# not a P&B monophthong measurement.
|
|
||||||
# * CONSONANT loci (M,N,NG,L,R,W,Y,Z,DH,V,S,F,HH) and ALL BANDWIDTHS (B1,B2,B3)
|
|
||||||
# and dur/amp are STANDARD FORMANT-SYNTHESIS conventions (Klatt 1980, JASA 67:971
|
|
||||||
# "Software for a cascade/parallel formant synthesizer") — engineering defaults,
|
|
||||||
# NOT per-phoneme field measurements. Labeled as such, not attributed to P&B.
|
|
||||||
# Format: SYM|F1|F2|F3|B1|B2|B3|voiced|nasal|dur_ms|amp|class|example
|
|
||||||
IY|270|2290|3010|60|90|150|1|0|130|100|vowel|beet
|
|
||||||
IH|390|1990|2550|70|100|150|1|0|110|100|vowel|bit
|
|
||||||
EH|530|1840|2480|80|100|150|1|0|130|100|vowel|bet
|
|
||||||
AE|660|1720|2410|90|110|150|1|0|150|100|vowel|bat
|
|
||||||
AA|730|1090|2440|90|110|150|1|0|150|100|vowel|bot
|
|
||||||
AO|570|840|2410|80|100|150|1|0|140|100|vowel|bought
|
|
||||||
UH|440|1020|2240|70|100|150|1|0|110|100|vowel|book
|
|
||||||
UW|300|870|2240|70|90|150|1|0|140|100|vowel|boot
|
|
||||||
AH|640|1190|2390|80|100|150|1|0|110|95|vowel|but
|
|
||||||
ER|490|1350|1690|80|100|120|1|0|140|95|vowel|bird
|
|
||||||
AX|500|1500|2500|80|100|150|1|0|80|85|vowel|about
|
|
||||||
OW|490|910|2380|80|100|150|1|0|140|100|vowel|boat
|
|
||||||
M|250|900|2200|90|120|180|1|1|80|60|nasal|map
|
|
||||||
N|250|1700|2600|90|120|180|1|1|80|60|nasal|nap
|
|
||||||
NG|250|2300|2700|90|120|180|1|1|80|60|nasal|sing
|
|
||||||
L|360|1300|2600|80|110|160|1|0|70|80|approximant|lip
|
|
||||||
R|490|1350|1600|80|110|120|1|0|80|85|approximant|rip
|
|
||||||
W|300|610|2200|70|100|160|1|0|70|80|approximant|wet
|
|
||||||
Y|270|2290|3010|60|90|150|1|0|60|80|approximant|yet
|
|
||||||
Z|300|1700|2500|100|150|200|1|0|90|55|fricative|zoo
|
|
||||||
DH|300|1400|2500|100|150|200|1|0|70|55|fricative|the
|
|
||||||
V|300|1000|2300|100|150|200|1|0|70|55|fricative|van
|
|
||||||
S|320|1700|2500|200|200|250|0|0|110|45|fricative|see
|
|
||||||
F|300|1200|2400|200|200|250|0|0|100|40|fricative|fee
|
|
||||||
HH|500|1500|2500|200|250|300|0|0|70|40|fricative|hat
|
|
||||||
SIL|500|1500|2500|100|100|100|0|0|55|0|silence|_
|
|
||||||
Binary file not shown.
@@ -80,11 +80,6 @@ build {
|
|||||||
"src/grammar.el",
|
"src/grammar.el",
|
||||||
"src/realizer.el",
|
"src/realizer.el",
|
||||||
"src/semantics.el",
|
"src/semantics.el",
|
||||||
"src/comprehend.el",
|
|
||||||
"src/propositions.el",
|
|
||||||
"src/multilingual.el",
|
|
||||||
"src/self_region.el",
|
|
||||||
"src/dialogue.el",
|
|
||||||
"src/elp.el",
|
"src/elp.el",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
> **STATUS: STAGING / PROOF-OF-SHAPE — not the deliverable.** This Python package
|
|
||||||
> proved the architecture end-to-end against the proven realizer faculty (faithful
|
|
||||||
> md/docx/midi from real geometry: 0 ungrounded claims, SACRED polarity). Per Will's
|
|
||||||
> steer, the DELIVERABLE is NATIVE: the seam lives on the existing EL realizer as
|
|
||||||
> **surface-as-profile** — see `../src/surface-profile.el` and
|
|
||||||
> `../tests/examples/surface-profile-demo.el` (compiles + runs through elc → C →
|
|
||||||
> binary). The concepts below (one geometry-carrying frame; surface = a pluggable
|
|
||||||
> profile; plan/realize; deterministic-from-meaning) are exactly what the native
|
|
||||||
> module implements. Keep this package as the validated proof; build native.
|
|
||||||
|
|
||||||
# Efferent Multimodal Projector
|
|
||||||
|
|
||||||
**geometry → any surface, faithfully.** Neuron's own document-generation faculty:
|
|
||||||
the efferent twin of the ingest organ. Ingest is afferent (world → geometry);
|
|
||||||
this is efferent (geometry → an arbitrary-format document / any modality).
|
|
||||||
|
|
||||||
Built against the **proven** realizer faculty (neuron-talk sidecar `:8756`,
|
|
||||||
artifact `art-7affa557`). The live soul (`:8742` / `:7770`) is contacted **only**
|
|
||||||
through the read-only, GET-only `engram_client` — never mutated.
|
|
||||||
|
|
||||||
## The pipeline (surface-agnostic)
|
|
||||||
|
|
||||||
```
|
|
||||||
geometry region + surface/format spec
|
|
||||||
→ PLAN (manifold → document skeleton/DAG; the geometry IS the outline) plan.py
|
|
||||||
→ REALIZE (proven realizer, scaled sentence → passage, each section faithful) realize.py
|
|
||||||
→ COHERE (document-level flow / transitions, not stitched sentences) cohere.py
|
|
||||||
→ EMIT (pluggable SurfaceProjector → the target surface) projectors/
|
|
||||||
```
|
|
||||||
|
|
||||||
**The surface is a PARAMETER.** `pipeline.build_ir(...)` builds ONE
|
|
||||||
surface-neutral `DocumentIR` (`document_ir.py`); `pipeline.emit(doc, surface)`
|
|
||||||
projects it to whichever surface you name. Markdown, docx, and MIDI are the same
|
|
||||||
IR emitted three ways.
|
|
||||||
|
|
||||||
## The pivot: a geometry-carrying IR
|
|
||||||
|
|
||||||
`DocumentIR` is **not** a text tree. Every `Block` carries BOTH:
|
|
||||||
- `.sentences` — realized faithful text (what **text** projectors read),
|
|
||||||
- `.provenance` — the source geometry: `subj_id / relation / obj / polarity /
|
|
||||||
confidence / importance / salience / node_id` (what **music / image / video**
|
|
||||||
projectors read).
|
|
||||||
|
|
||||||
That single decision is what makes the projector multimodal: text renders the
|
|
||||||
words; music/image decode the geometry. A claim with no provenance cannot exist
|
|
||||||
in the IR — faithfulness is structural.
|
|
||||||
|
|
||||||
## The one shared seam
|
|
||||||
|
|
||||||
`projectors/base.py` — `SurfaceProjector.project(frame: DocumentIR) -> bytes`
|
|
||||||
(+ `surface / media_type / ext / modality / profile`). Register with
|
|
||||||
`register()`. Adding a surface changes nothing upstream.
|
|
||||||
|
|
||||||
`TwoStageProjector` blesses the peer plan/realize decomposition:
|
|
||||||
`spec = plan(frame)`, `bytes = realize(spec)`, `project = realize∘plan`; the
|
|
||||||
`profile` is the pluggable per-surface knob (text lang-profile, music
|
|
||||||
instr/mode-profile). `projectors/midi.py` is the reference two-stage impl.
|
|
||||||
|
|
||||||
## Surfaces
|
|
||||||
|
|
||||||
| surface | modality | status | emitter |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `markdown` | text | landed | own (str) |
|
|
||||||
| `docx` | text | landed | own minimal OOXML (stdlib `zipfile`+XML, no lib) |
|
|
||||||
| `midi` | audio | landed (symbolic-music proof) | own minimal SMF (stdlib `struct`, no lib) |
|
|
||||||
| `audio` (WAV) | audio | peer agent (additive synth) | conforms to `TwoStageProjector` |
|
|
||||||
| `image` | image | documented seam | `projectors/seams.py` |
|
|
||||||
| `video` | video | documented seam (image×sound×time) | `projectors/seams.py` |
|
|
||||||
|
|
||||||
Music maps: relation → scale degree (same relation → same pitch), **polarity →
|
|
||||||
major/minor third (SACRED negation is audible)**, confidence → duration,
|
|
||||||
importance → velocity, section → register. Deterministic projection from meaning
|
|
||||||
— nothing invented.
|
|
||||||
|
|
||||||
## Faithfulness
|
|
||||||
|
|
||||||
`provenance.py` audits the IR: **zero** ungrounded claims, SACRED polarity
|
|
||||||
preserved (negations reported, never dropped), COHERE introduces no new geometry
|
|
||||||
(connectives are marked). `trace_table()` emits the geometry → section → claim
|
|
||||||
table.
|
|
||||||
|
|
||||||
## Run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PY=~/Desktop/lang-realizers/venv/bin/python
|
|
||||||
PYTHONPATH=~/Desktop/neuron-talk:~/Desktop/lang-realizers $PY generate.py
|
|
||||||
# writes ./out/{neuron-self,engram-temporal}.{md,docx,mid} + *.audit.json + *.provenance.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Requires the proven realizer env (spaCy + the neuron-talk/lang-realizers engine)
|
|
||||||
and the read-only engram at `:8742`.
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
"""cohere.py — COHERE stage: document-level flow, not stitched sentences.
|
|
||||||
|
|
||||||
Fidelity is REALIZE's job; FLOW is this stage's. The hard part beyond sentence
|
|
||||||
fidelity is that a document must read as one thing. We add connective tissue at
|
|
||||||
the passage level:
|
|
||||||
|
|
||||||
* an opening abstract that names what the document covers (built ONLY from the
|
|
||||||
section headings that already exist — it introduces no new claim),
|
|
||||||
* a short transition lead into each section after the first, drawn from a
|
|
||||||
fixed set of discourse connectives ("Beyond that,", "Relatedly,", ...) that
|
|
||||||
carry no propositional content,
|
|
||||||
* ordering so the highest-grounded section leads.
|
|
||||||
|
|
||||||
CRITICAL: every connective is marked ``kind="connective"`` in its provenance, so
|
|
||||||
the faithfulness audit can prove COHERE introduced ZERO new geometry claims. A
|
|
||||||
transition is discourse glue, never a fact.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from document_ir import Block, DocumentIR, Provenance
|
|
||||||
|
|
||||||
# discourse connectives — pure flow, no propositional content
|
|
||||||
_TRANSITIONS = [
|
|
||||||
"Beyond that,", "Relatedly,", "In the same region,", "From there,",
|
|
||||||
"Alongside this,", "Further,", "Turning to the next facet,",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _connective_prov() -> Provenance:
|
|
||||||
return Provenance(subj_id=None, subject=None, relation="", obj=None,
|
|
||||||
polarity="aff", confidence=1.0, node_id=None,
|
|
||||||
kind="connective")
|
|
||||||
|
|
||||||
|
|
||||||
def _abstract_block(doc: DocumentIR) -> Block:
|
|
||||||
"""A grounded opening: names the sections, asserts nothing new."""
|
|
||||||
headings = [s.heading for s in doc.sections]
|
|
||||||
if not headings:
|
|
||||||
return Block(role="lead")
|
|
||||||
if len(headings) == 1:
|
|
||||||
body = f"This document, generated from Neuron's geometry, covers {headings[0]}."
|
|
||||||
else:
|
|
||||||
listed = ", ".join(headings[:-1]) + f", and {headings[-1]}"
|
|
||||||
body = ("This document is projected directly from Neuron's meaning-geometry. "
|
|
||||||
f"It traces {listed}.")
|
|
||||||
b = Block(role="lead")
|
|
||||||
b.sentences.append(body)
|
|
||||||
b.provenance.append(_connective_prov())
|
|
||||||
return b
|
|
||||||
|
|
||||||
|
|
||||||
def cohere_document(doc: DocumentIR, *, add_abstract: bool = True,
|
|
||||||
add_transitions: bool = True) -> DocumentIR:
|
|
||||||
"""Order sections by grounding, add abstract + transitions (flow only)."""
|
|
||||||
# order: strongest-grounded section (mean confidence x #claims) first,
|
|
||||||
# but keep an explicitly-first section if the plan pinned one via level 1.
|
|
||||||
def _score(sec):
|
|
||||||
provs = [p for p in sec.all_provenance() if p.kind == "fact"]
|
|
||||||
if not provs:
|
|
||||||
return 0.0
|
|
||||||
mean_conf = sum(p.confidence for p in provs) / len(provs)
|
|
||||||
return mean_conf * len(provs)
|
|
||||||
|
|
||||||
doc.sections.sort(key=_score, reverse=True)
|
|
||||||
|
|
||||||
if add_transitions:
|
|
||||||
for i, sec in enumerate(doc.sections):
|
|
||||||
if i == 0 or not sec.blocks:
|
|
||||||
continue
|
|
||||||
lead = _TRANSITIONS[(i - 1) % len(_TRANSITIONS)]
|
|
||||||
first = sec.blocks[0]
|
|
||||||
if first.sentences:
|
|
||||||
# prepend the connective to the first sentence (flow, no new claim)
|
|
||||||
first.sentences[0] = f"{lead} {first.sentences[0][0].lower()}{first.sentences[0][1:]}"
|
|
||||||
|
|
||||||
if add_abstract:
|
|
||||||
doc.meta["abstract"] = _abstract_block(doc)
|
|
||||||
|
|
||||||
return doc
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
"""document_ir.py — the surface-neutral, GEOMETRY-CARRYING document intermediate.
|
|
||||||
|
|
||||||
This is the pivot of the whole efferent projector. A DocumentIR is NOT a text
|
|
||||||
tree. It is a projection of a meaning-geometry region that carries, at every
|
|
||||||
leaf, BOTH:
|
|
||||||
|
|
||||||
* the realized surface text (``Block.sentences``) — what a TEXT projector reads,
|
|
||||||
* the source geometry (``Block.provenance``) — what a MUSIC / IMAGE /
|
|
||||||
VIDEO projector reads.
|
|
||||||
|
|
||||||
Because the IR holds the geometry, not just the words, the SAME
|
|
||||||
plan -> realize -> cohere pipeline drives every surface. A markdown projector
|
|
||||||
renders the sentences; a music projector reads the provenance edges (salience,
|
|
||||||
importance, polarity, relation) and maps them onto a symbolic-music surface;
|
|
||||||
an image/video projector (documented seam) would read the same geometry.
|
|
||||||
|
|
||||||
Nothing in this module invents content. Every :class:`Provenance` points at a
|
|
||||||
real engram node id and a real relation. That is the faithfulness contract made
|
|
||||||
structural: a claim with no provenance cannot exist in the IR.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Provenance — the geometry an emitted claim traces to. FAITHFULNESS is here.
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
@dataclass
|
|
||||||
class Provenance:
|
|
||||||
"""One geometry edge behind one realized claim.
|
|
||||||
|
|
||||||
``kind`` distinguishes a FACT (a structural edge asserted by the geometry,
|
|
||||||
spoken as fact) from an INTERPRETATION (something attributed, spoken with
|
|
||||||
attribution) — the facts-as-facts + interpretations-attributed discipline
|
|
||||||
(memory 80927e26). ``polarity`` is SACRED: a negated edge stays negated.
|
|
||||||
"""
|
|
||||||
subj_id: str | None # source engram node id of the subject
|
|
||||||
subject: str | None # normalized subject surface
|
|
||||||
relation: str # predicate lemma (e.g. "use", "contain", "be")
|
|
||||||
obj: str | None # normalized object / complement surface
|
|
||||||
polarity: str = "aff" # "aff" | "neg" (SACRED — never silently flipped)
|
|
||||||
confidence: float = 0.0 # extraction confidence in [0,1]
|
|
||||||
node_id: str | None = None # engram node the claim was extracted from
|
|
||||||
kind: str = "fact" # "fact" | "interpretation"
|
|
||||||
importance: float = 0.0 # source node importance (drives music/emphasis)
|
|
||||||
salience: float = 0.0 # source node salience
|
|
||||||
|
|
||||||
def trace(self) -> str:
|
|
||||||
arrow = "-->" if self.polarity == "aff" else "--NOT-->"
|
|
||||||
return (f"[{(self.node_id or '?')[:8]}] {self.subject!r} {arrow}"
|
|
||||||
f"{self.relation} {self.obj!r} (conf {self.confidence:.2f})")
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Block:
|
|
||||||
"""A passage: one or more faithful sentences + the geometry they trace to.
|
|
||||||
|
|
||||||
``sentences`` and ``provenance`` are index-aligned where possible: sentence
|
|
||||||
``i`` was realized from ``provenance[i]``. A COHERE transition sentence with
|
|
||||||
no new geometry carries a provenance whose ``kind == "connective"`` so the
|
|
||||||
audit can see it introduced no new claim.
|
|
||||||
"""
|
|
||||||
sentences: list[str] = field(default_factory=list)
|
|
||||||
provenance: list[Provenance] = field(default_factory=list)
|
|
||||||
role: str = "body" # "body" | "lead" | "transition"
|
|
||||||
|
|
||||||
def text(self) -> str:
|
|
||||||
return " ".join(s.rstrip(". ") + "." for s in self.sentences if s.strip())
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Section:
|
|
||||||
heading: str
|
|
||||||
level: int = 2 # markdown heading level / outline depth
|
|
||||||
blocks: list[Block] = field(default_factory=list)
|
|
||||||
seed_ids: list[str] = field(default_factory=list) # geometry nodes of section
|
|
||||||
summary: str = "" # one-line grounded gloss (for pptx bullets / TOC)
|
|
||||||
|
|
||||||
def all_provenance(self) -> list[Provenance]:
|
|
||||||
out: list[Provenance] = []
|
|
||||||
for b in self.blocks:
|
|
||||||
out.extend(b.provenance)
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class DocumentIR:
|
|
||||||
"""The surface-neutral document. Built ONCE, projected to ANY surface."""
|
|
||||||
title: str
|
|
||||||
subtitle: str = ""
|
|
||||||
sections: list[Section] = field(default_factory=list)
|
|
||||||
seed_id: str | None = None # the geometry region root
|
|
||||||
format_spec: dict[str, Any] = field(default_factory=dict) # requested shape
|
|
||||||
meta: dict[str, Any] = field(default_factory=dict)
|
|
||||||
|
|
||||||
# -- geometry facets (what non-text projectors consume) ----------------- #
|
|
||||||
def all_provenance(self) -> list[Provenance]:
|
|
||||||
out: list[Provenance] = []
|
|
||||||
for s in self.sections:
|
|
||||||
out.extend(s.all_provenance())
|
|
||||||
return out
|
|
||||||
|
|
||||||
def claim_count(self) -> int:
|
|
||||||
return sum(1 for p in self.all_provenance() if p.kind in ("fact", "interpretation"))
|
|
||||||
|
|
||||||
def ungrounded_count(self) -> int:
|
|
||||||
"""Claims with no traceable node — MUST be zero for a faithful doc."""
|
|
||||||
return sum(1 for p in self.all_provenance()
|
|
||||||
if p.kind in ("fact", "interpretation") and not p.node_id)
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
"""generate.py — drive the projector: one geometry region -> many surfaces.
|
|
||||||
|
|
||||||
Proves the thesis with REAL output: builds ONE surface-neutral DocumentIR from
|
|
||||||
Neuron's OWN self-geometry (read-only against the live soul via the proven
|
|
||||||
faculty), then EMITS it to Markdown, docx, and MIDI — the same plan/realize/
|
|
||||||
cohere, three surfaces. Writes the files + the faithfulness audit to ./out/.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
sys.path.insert(0, _HERE)
|
|
||||||
|
|
||||||
import pipeline # noqa: E402
|
|
||||||
import provenance # noqa: E402
|
|
||||||
from geometry import load_self_region # noqa: E402
|
|
||||||
|
|
||||||
OUT = os.path.join(_HERE, "out")
|
|
||||||
|
|
||||||
|
|
||||||
def _emit_all(doc, stem):
|
|
||||||
"""Emit one IR to every text/audio surface + audit + provenance."""
|
|
||||||
for surface in ("markdown", "docx", "midi"):
|
|
||||||
data = pipeline.emit(doc, surface)
|
|
||||||
proj = pipeline.get_projector(surface)
|
|
||||||
path = os.path.join(OUT, f"{stem}.{proj.ext}")
|
|
||||||
with open(path, "wb") as f:
|
|
||||||
f.write(data)
|
|
||||||
print(f" emitted {surface:9s} -> {os.path.basename(path)} ({len(data)} bytes)")
|
|
||||||
a = provenance.audit(doc)
|
|
||||||
with open(os.path.join(OUT, f"{stem}.audit.json"), "w") as f:
|
|
||||||
json.dump(a, f, indent=2)
|
|
||||||
with open(os.path.join(OUT, f"{stem}.provenance.md"), "w") as f:
|
|
||||||
f.write(provenance.trace_table(doc))
|
|
||||||
print(" audit:", {k: a[k] for k in ("claims", "ungrounded_claims",
|
|
||||||
"negations_preserved", "distinct_source_nodes", "faithful")})
|
|
||||||
return a
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
os.makedirs(OUT, exist_ok=True)
|
|
||||||
print("surfaces registered:", pipeline.available_surfaces())
|
|
||||||
|
|
||||||
# ---- Document 1: Neuron's self-description (marquee) ------------------- #
|
|
||||||
print("\n[1] Neuron self-description")
|
|
||||||
region = load_self_region(max_nodes=9)
|
|
||||||
print(" self region:", region)
|
|
||||||
doc1 = pipeline.build_ir(
|
|
||||||
None, region=region,
|
|
||||||
title="Neuron: A Self-Description from Its Own Geometry",
|
|
||||||
subtitle="Projected efferently from the engram — every claim traces a node.",
|
|
||||||
format_spec={"genre": "self-description", "register": "expository"},
|
|
||||||
max_sections=5, conf_floor=0.6)
|
|
||||||
print(f" IR: {len(doc1.sections)} sections, {doc1.claim_count()} claims, "
|
|
||||||
f"ungrounded={doc1.ungrounded_count()}")
|
|
||||||
_emit_all(doc1, "neuron-self")
|
|
||||||
|
|
||||||
# ---- Document 2: a coherent, clean whitepaper-style section ------------ #
|
|
||||||
print("\n[2] Whitepaper-style section (coherent clean region)")
|
|
||||||
doc2, _ = pipeline.project(
|
|
||||||
["chronoception", "time", "awareness", "engram", "temporal"],
|
|
||||||
surface="markdown",
|
|
||||||
title="Temporal Awareness in the Engram",
|
|
||||||
subtitle="A section projected from the geometry of chronoception.",
|
|
||||||
format_spec={"genre": "whitepaper-section", "register": "technical"},
|
|
||||||
max_sections=4)
|
|
||||||
print(f" IR: {len(doc2.sections)} sections, {doc2.claim_count()} claims, "
|
|
||||||
f"ungrounded={doc2.ungrounded_count()}")
|
|
||||||
_emit_all(doc2, "engram-temporal")
|
|
||||||
|
|
||||||
# echo both markdowns so they are visible in the run log
|
|
||||||
for stem, doc in (("neuron-self", doc1), ("engram-temporal", doc2)):
|
|
||||||
print(f"\n===== GENERATED MARKDOWN — {stem} =====\n")
|
|
||||||
print(pipeline.emit(doc, "markdown").decode())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
"""geometry.py — READ-ONLY loader for a meaning-geometry region.
|
|
||||||
|
|
||||||
The efferent projector never writes to the soul. This module reaches the
|
|
||||||
geometry through the PROVEN, read-only neuron-talk faculty (``engram_client``,
|
|
||||||
GET-only, which physically refuses non-GET methods) against the running sidecar
|
|
||||||
soul. The live daemon :8742 / :7770 is contacted ONLY through that read-only
|
|
||||||
client — never mutated.
|
|
||||||
|
|
||||||
A "region" is a seed node plus a bounded neighborhood: the manifold that will
|
|
||||||
become the document's skeleton. We pool a few single-term lexical searches
|
|
||||||
(the engram search is a single-term matcher) and, when available, walk one hop
|
|
||||||
of reified neighbors, then rank by self/importance signal.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# Wire in the proven faculty (own-the-core: we reuse it, we do not fork it).
|
|
||||||
_NT = os.path.expanduser("~/Desktop/neuron-talk")
|
|
||||||
_LR = os.path.expanduser("~/Desktop/lang-realizers")
|
|
||||||
for _p in (_NT, _LR):
|
|
||||||
if _p not in sys.path:
|
|
||||||
sys.path.insert(0, _p)
|
|
||||||
|
|
||||||
from engram_client import ReadOnlyEngramClient # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
class Region:
|
|
||||||
"""A geometry region: ranked nodes + the reified edges among them."""
|
|
||||||
|
|
||||||
def __init__(self, seed: str, nodes: list[dict], edges: list[dict]):
|
|
||||||
self.seed = seed
|
|
||||||
self.nodes = nodes # ranked engram node dicts
|
|
||||||
self.edges = edges # [{src, dst, edge, ...}]
|
|
||||||
self.by_id = {n["id"]: n for n in nodes if n.get("id")}
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"<Region seed={self.seed!r} nodes={len(self.nodes)} edges={len(self.edges)}>"
|
|
||||||
|
|
||||||
|
|
||||||
def _prose_quality(content: str) -> float:
|
|
||||||
"""Reward clean expository prose; penalize shouty banner-dense nodes.
|
|
||||||
|
|
||||||
A high ALLCAPS-word ratio or very short content signals a banner/telegraphic
|
|
||||||
memory node that extracts into garbage. Clean declarative prose scores high.
|
|
||||||
"""
|
|
||||||
if not content or not content.strip():
|
|
||||||
return 0.0
|
|
||||||
words = content.split()
|
|
||||||
if len(words) < 8:
|
|
||||||
return 0.1
|
|
||||||
caps = sum(1 for w in words if len(w) > 2 and w.strip(".,:;'\"-").isupper())
|
|
||||||
caps_ratio = caps / max(1, len(words))
|
|
||||||
# sentences with lowercase interior words read as prose
|
|
||||||
lower = sum(1 for w in words if w[:1].islower())
|
|
||||||
lower_ratio = lower / max(1, len(words))
|
|
||||||
return max(0.0, 1.2 * lower_ratio - 2.0 * caps_ratio)
|
|
||||||
|
|
||||||
|
|
||||||
def _relevance(content: str, terms: list[str]) -> float:
|
|
||||||
"""Topical relevance to the seed terms — keeps a region ON-THEME so a clean
|
|
||||||
but off-topic node cannot hijack the document."""
|
|
||||||
if not terms:
|
|
||||||
return 0.0
|
|
||||||
low = (content or "").lower()
|
|
||||||
hits = sum(1 for t in terms if t.lower() in low)
|
|
||||||
return hits / max(1, len(terms))
|
|
||||||
|
|
||||||
|
|
||||||
def _node_rank(n: dict, terms: list[str] | None = None) -> float:
|
|
||||||
return (float(n.get("importance") or 0.0) * 2.0
|
|
||||||
+ float(n.get("salience") or 0.0)
|
|
||||||
+ 1.5 * _prose_quality(n.get("content") or "")
|
|
||||||
+ 2.0 * _relevance(n.get("content") or "", terms or [])
|
|
||||||
+ (0.5 if (n.get("content") or "").strip() else 0.0))
|
|
||||||
|
|
||||||
|
|
||||||
def load_region(seed_terms: list[str] | str, *, client: ReadOnlyEngramClient | None = None,
|
|
||||||
max_nodes: int = 10, per_term: int = 20, hop: bool = True) -> Region:
|
|
||||||
"""Pull a bounded geometry region around ``seed_terms`` (read-only).
|
|
||||||
|
|
||||||
``seed_terms`` may be a single string or several probe terms; results are
|
|
||||||
pooled and de-duplicated. When ``hop`` and the reified neighbor endpoint is
|
|
||||||
live, one hop of neighbors is folded in so the region is a real
|
|
||||||
neighborhood, not just a keyword hit list.
|
|
||||||
"""
|
|
||||||
client = client or ReadOnlyEngramClient()
|
|
||||||
if isinstance(seed_terms, str):
|
|
||||||
seed_terms = [seed_terms]
|
|
||||||
|
|
||||||
pool: dict[str, dict] = {}
|
|
||||||
for term in seed_terms:
|
|
||||||
for n in client.search(term, limit=per_term):
|
|
||||||
if isinstance(n, dict) and n.get("id"):
|
|
||||||
pool.setdefault(n["id"], n)
|
|
||||||
|
|
||||||
ranked = sorted(pool.values(), key=lambda n: _node_rank(n, seed_terms),
|
|
||||||
reverse=True)
|
|
||||||
nodes = ranked[:max_nodes]
|
|
||||||
|
|
||||||
edges: list[dict] = []
|
|
||||||
if hop and nodes:
|
|
||||||
present = {n["id"] for n in nodes}
|
|
||||||
for n in list(nodes):
|
|
||||||
try:
|
|
||||||
for nb in client.neighbors(n["id"]):
|
|
||||||
node = nb.get("node") if isinstance(nb, dict) else None
|
|
||||||
edge = nb.get("edge") if isinstance(nb, dict) else None
|
|
||||||
if node and node.get("id"):
|
|
||||||
edges.append({"src": n["id"], "dst": node["id"],
|
|
||||||
"edge": edge})
|
|
||||||
# fold a strong neighbor into the region (bounded)
|
|
||||||
if (node["id"] not in present and len(nodes) < max_nodes + 6
|
|
||||||
and _node_rank(node, seed_terms) > 0.4):
|
|
||||||
present.add(node["id"])
|
|
||||||
nodes.append(node)
|
|
||||||
except Exception: # noqa: BLE001 — read-only best-effort; never fatal
|
|
||||||
continue
|
|
||||||
|
|
||||||
return Region(seed=", ".join(seed_terms), nodes=nodes, edges=edges)
|
|
||||||
|
|
||||||
|
|
||||||
def load_self_region(client: ReadOnlyEngramClient | None = None,
|
|
||||||
max_nodes: int = 10) -> Region:
|
|
||||||
"""The self/identity region — Neuron's own geometry, for self-description."""
|
|
||||||
return load_region(["self", "identity", "Neuron", "values", "memory",
|
|
||||||
"imprint", "consciousness"],
|
|
||||||
client=client, max_nodes=max_nodes)
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
"""pipeline.py — the Efferent Multimodal Projector, top level.
|
|
||||||
|
|
||||||
geometry region + surface/format spec
|
|
||||||
-> PLAN (manifold -> document skeleton/DAG)
|
|
||||||
-> REALIZE (proven realizer, sentence -> passage, each section faithful)
|
|
||||||
-> COHERE (document-level flow / transitions, not stitched sentences)
|
|
||||||
-> EMIT (pluggable SurfaceProjector -> the target surface)
|
|
||||||
|
|
||||||
THE SURFACE IS A PARAMETER. ``project(...)`` builds the geometry-carrying
|
|
||||||
DocumentIR once, then hands it to whichever surface projector the caller named.
|
|
||||||
Markdown, docx, and midi (music) are all the SAME IR emitted differently. That
|
|
||||||
is the efferent multimodal projector: geometry -> any surface.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
sys.path.insert(0, _HERE)
|
|
||||||
sys.path.insert(0, os.path.join(_HERE, "projectors"))
|
|
||||||
|
|
||||||
from cohere import cohere_document # noqa: E402
|
|
||||||
from document_ir import DocumentIR # noqa: E402
|
|
||||||
from geometry import Region, load_region # noqa: E402
|
|
||||||
from plan import plan_document # noqa: E402
|
|
||||||
from realize import realize_document # noqa: E402
|
|
||||||
|
|
||||||
# registering the projectors (import for side-effect: each self-registers)
|
|
||||||
import projectors.markdown # noqa: E402,F401
|
|
||||||
import projectors.docx # noqa: E402,F401
|
|
||||||
import projectors.midi # noqa: E402,F401
|
|
||||||
import projectors.seams # noqa: E402,F401
|
|
||||||
from projectors.base import available_surfaces, get_projector # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
def build_ir(seed_terms, *, title: str, subtitle: str = "",
|
|
||||||
format_spec: dict | None = None,
|
|
||||||
region: Region | None = None,
|
|
||||||
max_sections: int = 8, conf_floor: float = 0.55) -> DocumentIR:
|
|
||||||
"""geometry -> PLAN -> REALIZE -> COHERE = the surface-neutral DocumentIR."""
|
|
||||||
region = region or load_region(seed_terms)
|
|
||||||
doc = plan_document(region, title=title, subtitle=subtitle,
|
|
||||||
format_spec=format_spec or {},
|
|
||||||
conf_floor=conf_floor, max_sections=max_sections)
|
|
||||||
doc = realize_document(doc)
|
|
||||||
doc = cohere_document(doc)
|
|
||||||
return doc
|
|
||||||
|
|
||||||
|
|
||||||
def emit(doc: DocumentIR, surface: str) -> bytes:
|
|
||||||
"""EMIT: project the built IR onto one surface (surface = a parameter)."""
|
|
||||||
return get_projector(surface).project(doc)
|
|
||||||
|
|
||||||
|
|
||||||
def project(seed_terms, *, surface: str, title: str, subtitle: str = "",
|
|
||||||
format_spec: dict | None = None, region: Region | None = None,
|
|
||||||
max_sections: int = 8) -> tuple[DocumentIR, bytes]:
|
|
||||||
"""The full efferent projection: geometry + surface -> (IR, bytes)."""
|
|
||||||
doc = build_ir(seed_terms, title=title, subtitle=subtitle,
|
|
||||||
format_spec=format_spec, region=region,
|
|
||||||
max_sections=max_sections)
|
|
||||||
return doc, emit(doc, surface)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["build_ir", "emit", "project", "available_surfaces",
|
|
||||||
"get_projector", "load_region", "DocumentIR"]
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
"""plan.py — PLAN stage: geometry region -> document skeleton (a DAG/outline).
|
|
||||||
|
|
||||||
The manifold becomes the skeleton. We extract faithful propositions from the
|
|
||||||
region's nodes (the proven neuron-talk extractor, SACRED polarity preserved),
|
|
||||||
apply a quality floor, then GROUP them into sections. Grouping is by source
|
|
||||||
node — each engram node is one coherent topic, so one salient node becomes one
|
|
||||||
section. The section ORDER is the node ranking (importance/salience): the
|
|
||||||
geometry decides the outline, not a template.
|
|
||||||
|
|
||||||
Output: a DocumentIR whose sections carry seed node ids and empty blocks. REALIZE
|
|
||||||
fills the blocks; the plan owns the structure.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
_NT = os.path.expanduser("~/Desktop/neuron-talk")
|
|
||||||
_LR = os.path.expanduser("~/Desktop/lang-realizers")
|
|
||||||
for _p in (_NT, _LR):
|
|
||||||
if _p not in sys.path:
|
|
||||||
sys.path.insert(0, _p)
|
|
||||||
|
|
||||||
import propositions # noqa: E402 (the proven, faithful extractor)
|
|
||||||
|
|
||||||
from document_ir import DocumentIR, Section # noqa: E402
|
|
||||||
from geometry import Region # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Proposition quality — keep only clean, well-grounded claims.
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
_JUNK_RE = re.compile(r"[.][a-z]{1,3}\b|[^A-Za-z0-9 '\-]") # ".o", stray symbols
|
|
||||||
|
|
||||||
|
|
||||||
def _has_banner_token(s: str) -> bool:
|
|
||||||
"""True if any word is an ALLCAPS banner token (DHARMA, ENGRAM, MEASURED)."""
|
|
||||||
for w in (s or "").split():
|
|
||||||
core = w.strip(".,:;'\"-")
|
|
||||||
if len(core) > 2 and core.isupper():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _clean_prop(p, floor: float) -> bool:
|
|
||||||
if p.confidence < floor:
|
|
||||||
return False
|
|
||||||
if not p.subject or not (p.object or (p.obj_np is not None)):
|
|
||||||
return False
|
|
||||||
subj = (p.subject or "").strip()
|
|
||||||
obj = (p.object or "").strip()
|
|
||||||
if len(subj) < 2:
|
|
||||||
return False
|
|
||||||
# banner-derived shouty fragments read as garbage in prose
|
|
||||||
if _has_banner_token(subj) or _has_banner_token(obj):
|
|
||||||
return False
|
|
||||||
if propositions._is_shouty(p.sentence or ""):
|
|
||||||
return False
|
|
||||||
# junk tokens: file-extension fragments (".o"), stray non-word symbols
|
|
||||||
if _JUNK_RE.search(subj) or _JUNK_RE.search(obj):
|
|
||||||
return False
|
|
||||||
# a proposition whose object repeats the subject is usually a parse artifact
|
|
||||||
if obj and subj.lower() == obj.lower():
|
|
||||||
return False
|
|
||||||
# a bare copula with no real complement ("X is it") reads as noise
|
|
||||||
if p.predicate == "be" and obj.lower() in ("it", "no", "nothing", "empty", ""):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _dedup(props):
|
|
||||||
"""Drop duplicate claims. Two axes: (a) identical (pred,obj,polarity), and
|
|
||||||
(b) same (subject,predicate) — which collapses a mis-split compound like
|
|
||||||
"detection is post-hoc eval" -> "Detection is post/hoc/eval" into one claim
|
|
||||||
(keep the highest-confidence surface)."""
|
|
||||||
props = sorted(props, key=lambda p: p.confidence, reverse=True)
|
|
||||||
seen_po, seen_sp, out = set(), set(), []
|
|
||||||
for p in props:
|
|
||||||
subj = (p.subject or "").lower()
|
|
||||||
po = (p.predicate, (p.object or "").lower(), p.polarity)
|
|
||||||
sp = (subj, p.predicate, p.polarity)
|
|
||||||
if po in seen_po or sp in seen_sp:
|
|
||||||
continue
|
|
||||||
seen_po.add(po)
|
|
||||||
seen_sp.add(sp)
|
|
||||||
out.append(p)
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Heading derivation — a clean human heading from a node.
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
_HEADING_RE = re.compile(r"^\s*#{1,4}\s+(.{2,70})\s*$", re.M)
|
|
||||||
# node-type / system labels that are NOT topical headings
|
|
||||||
_NONTOPIC_LABEL = re.compile(r"^(memory|node|knowledge|doc|session)[:/]", re.I)
|
|
||||||
|
|
||||||
|
|
||||||
def _titlecase_banner(s: str) -> str:
|
|
||||||
"""A shouty banner ("CHRONOCEPTION — SCALE-INVARIANCE") makes a fine title
|
|
||||||
once Title-cased. Keep short acronyms uppercase."""
|
|
||||||
def fix(w):
|
|
||||||
core = w.strip("—-:,.")
|
|
||||||
if len(core) <= 3 and core.isupper():
|
|
||||||
return w # acronym
|
|
||||||
return w.capitalize()
|
|
||||||
return " ".join(fix(w) for w in s.split())
|
|
||||||
|
|
||||||
|
|
||||||
def _clean_heading(text: str) -> str | None:
|
|
||||||
"""First line only, no markdown, capped, banner Title-cased. None if unusable."""
|
|
||||||
if not text:
|
|
||||||
return None
|
|
||||||
line = text.strip().splitlines()[0]
|
|
||||||
line = re.sub(r"^#+\s*", "", line).strip().strip("#").strip()
|
|
||||||
# cut at a natural break so a long banner heading stays a heading, not a para
|
|
||||||
for sep in (" — ", " – ", ": ", ". "):
|
|
||||||
if sep in line and len(line) > 48:
|
|
||||||
line = line.split(sep)[0].strip()
|
|
||||||
break
|
|
||||||
if not (3 <= len(line) <= 64):
|
|
||||||
return None
|
|
||||||
if propositions._is_shouty(line):
|
|
||||||
line = _titlecase_banner(line)
|
|
||||||
return line or None
|
|
||||||
|
|
||||||
|
|
||||||
def _heading_for(node: dict, fallback: str) -> str:
|
|
||||||
label = (node.get("label") or "").strip()
|
|
||||||
content = node.get("content") or ""
|
|
||||||
candidates: list[str] = []
|
|
||||||
# a node-type label ("memory:remembered") is never a topic — skip it
|
|
||||||
if label and not _NONTOPIC_LABEL.match(label):
|
|
||||||
candidates.append(label)
|
|
||||||
m = _HEADING_RE.search(content)
|
|
||||||
if m:
|
|
||||||
candidates.append(m.group(1))
|
|
||||||
# the leading banner/first sentence of the content is often the real title
|
|
||||||
first = re.split(r"(?<=[.\n])", content.strip(), maxsplit=1)[0] if content.strip() else ""
|
|
||||||
candidates.append(first)
|
|
||||||
for c in candidates:
|
|
||||||
h = _clean_heading(c)
|
|
||||||
if h:
|
|
||||||
return h
|
|
||||||
return fallback
|
|
||||||
|
|
||||||
|
|
||||||
def plan_document(region: Region, *, title: str, subtitle: str = "",
|
|
||||||
format_spec: dict | None = None,
|
|
||||||
conf_floor: float = 0.55,
|
|
||||||
max_sections: int = 8,
|
|
||||||
max_claims_per_section: int = 6) -> DocumentIR:
|
|
||||||
"""Region -> DocumentIR skeleton. The geometry dictates the outline."""
|
|
||||||
format_spec = format_spec or {}
|
|
||||||
doc = DocumentIR(title=title, subtitle=subtitle,
|
|
||||||
seed_id=region.nodes[0]["id"] if region.nodes else None,
|
|
||||||
format_spec=format_spec)
|
|
||||||
|
|
||||||
made = 0
|
|
||||||
seen_headings: set[str] = set()
|
|
||||||
for node in region.nodes:
|
|
||||||
if made >= max_sections:
|
|
||||||
break
|
|
||||||
props = propositions.extract(node.get("content") or "",
|
|
||||||
node_id=node.get("id"),
|
|
||||||
node_importance=float(node.get("importance") or 0.0),
|
|
||||||
max_sentences=10)
|
|
||||||
props = [p for p in props if _clean_prop(p, conf_floor)]
|
|
||||||
props = _dedup(props)
|
|
||||||
props.sort(key=lambda p: p.confidence, reverse=True)
|
|
||||||
props = props[:max_claims_per_section]
|
|
||||||
if not props:
|
|
||||||
continue
|
|
||||||
heading = _heading_for(node, fallback=f"Region {made + 1}")
|
|
||||||
# cross-section dedup: a topic appears once. Distinguish by top claim
|
|
||||||
# subject, else drop the collision so the outline stays clean.
|
|
||||||
if heading.lower() in seen_headings:
|
|
||||||
subj = (props[0].subject or "").strip().title()
|
|
||||||
alt = f"{heading}: {subj}" if subj and subj.lower() not in heading.lower() else None
|
|
||||||
if alt and alt.lower() not in seen_headings and len(alt) <= 64:
|
|
||||||
heading = alt
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
seen_headings.add(heading.lower())
|
|
||||||
sec = Section(heading=heading, level=2, seed_ids=[node["id"]])
|
|
||||||
# stash the planned propositions on the section for REALIZE
|
|
||||||
sec.__dict__["_planned_props"] = props
|
|
||||||
sec.__dict__["_node"] = node
|
|
||||||
doc.sections.append(sec)
|
|
||||||
made += 1
|
|
||||||
|
|
||||||
return doc
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
"""base.py — the SurfaceProjector interface + registry.
|
|
||||||
|
|
||||||
THE key abstraction of the efferent projector: a projector is a pure function
|
|
||||||
from the surface-neutral, geometry-carrying DocumentIR to bytes on a target
|
|
||||||
SURFACE. The surface is a PARAMETER. Adding a surface = registering one more
|
|
||||||
projector; nothing upstream (plan/realize/cohere) changes.
|
|
||||||
|
|
||||||
DocumentIR --project--> bytes (per surface)
|
|
||||||
|
|
||||||
A TEXT projector reads ``block.sentences``. A NON-TEXT projector (music, image,
|
|
||||||
video) reads ``block.provenance`` — the geometry the IR carries — and decodes it
|
|
||||||
onto its surface. Both consume the SAME IR. That symmetry is the whole design:
|
|
||||||
the realizer generalizes into a multimodal projector, geometry -> any surface.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Protocol, runtime_checkable
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
from document_ir import DocumentIR # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
@runtime_checkable
|
|
||||||
class SurfaceProjector(Protocol):
|
|
||||||
"""Geometry-document -> one surface. Implementations MUST be pure & faithful.
|
|
||||||
|
|
||||||
THE ONE SHARED SEAM. Every surface — text, music, image, video — conforms to
|
|
||||||
this single contract:
|
|
||||||
|
|
||||||
project(frame: DocumentIR) -> bytes
|
|
||||||
|
|
||||||
where ``frame`` is the geometry-carrying meaning-geometry (the SemFrame at
|
|
||||||
document scale; a single utterance is the degenerate one-section frame).
|
|
||||||
|
|
||||||
RECOMMENDED INTERNAL SHAPE (the peer music/text decomposition, blessed here
|
|
||||||
so all surfaces share it): a projector may split ``project`` into
|
|
||||||
|
|
||||||
spec = self.plan(frame) # meaning-geometry -> surface-specific spec
|
|
||||||
bytes = self.realize(spec) # spec -> surface, via this projector's PROFILE
|
|
||||||
|
|
||||||
``project`` is then ``realize(plan(frame))``. The PROFILE (a text lang-profile,
|
|
||||||
a music instr/mode-profile, an image layout-profile) is a property of the
|
|
||||||
projector instance — the pluggable knob. See :class:`TwoStageProjector`.
|
|
||||||
|
|
||||||
A TEXT projector's plan reads ``frame`` sentences; a MUSIC/IMAGE projector's
|
|
||||||
plan reads ``frame.all_provenance()`` — the geometry — and derives its spec
|
|
||||||
(pitch/harmony/rhythm, or layout) FROM the meaning, deterministically. Same
|
|
||||||
frame, different profile.
|
|
||||||
"""
|
|
||||||
|
|
||||||
surface: str # "markdown" | "docx" | "midi" | "audio" | "image" | "video"
|
|
||||||
media_type: str # MIME type of the emitted bytes
|
|
||||||
ext: str # file extension (no dot)
|
|
||||||
modality: str # "text" | "audio" | "image" | "video"
|
|
||||||
profile: object # the pluggable per-surface profile (may be None)
|
|
||||||
|
|
||||||
def project(self, doc: DocumentIR) -> bytes:
|
|
||||||
"""Emit the document on this surface. Returns raw bytes."""
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class TwoStageProjector:
|
|
||||||
"""Optional base for the peer plan()/realize() decomposition.
|
|
||||||
|
|
||||||
Subclasses implement ``plan(frame) -> spec`` and ``realize(spec) -> bytes``;
|
|
||||||
``project`` is their composition. This is exactly the peer music interface
|
|
||||||
(spec = plan(frame, profile); surface = realize(spec, profile)) expressed so
|
|
||||||
that it still satisfies the single ``SurfaceProjector.project`` seam. Text,
|
|
||||||
music, and image projectors can all subclass this and remain interchangeable.
|
|
||||||
"""
|
|
||||||
|
|
||||||
surface: str = ""
|
|
||||||
media_type: str = ""
|
|
||||||
ext: str = ""
|
|
||||||
modality: str = ""
|
|
||||||
profile: object = None
|
|
||||||
|
|
||||||
def plan(self, doc: DocumentIR): # -> spec
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def realize(self, spec) -> bytes:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def project(self, doc: DocumentIR) -> bytes:
|
|
||||||
return self.realize(self.plan(doc))
|
|
||||||
|
|
||||||
|
|
||||||
_REGISTRY: dict[str, SurfaceProjector] = {}
|
|
||||||
|
|
||||||
|
|
||||||
def register(projector: SurfaceProjector) -> SurfaceProjector:
|
|
||||||
_REGISTRY[projector.surface] = projector
|
|
||||||
return projector
|
|
||||||
|
|
||||||
|
|
||||||
def get_projector(surface: str) -> SurfaceProjector:
|
|
||||||
if surface not in _REGISTRY:
|
|
||||||
raise KeyError(f"no projector registered for surface {surface!r}; "
|
|
||||||
f"have {sorted(_REGISTRY)}")
|
|
||||||
return _REGISTRY[surface]
|
|
||||||
|
|
||||||
|
|
||||||
def available_surfaces() -> list[str]:
|
|
||||||
return sorted(_REGISTRY)
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
"""docx.py — the .docx surface projector: an OWN minimal OOXML emitter.
|
|
||||||
|
|
||||||
Own-the-core: a .docx is just a ZIP of a few XML parts (WordprocessingML). We
|
|
||||||
emit it with the standard library only — ``zipfile`` + string XML — no
|
|
||||||
python-docx, no external dependency. This proves a "richer structured format"
|
|
||||||
surface without importing anyone else's toolkit.
|
|
||||||
|
|
||||||
Parts emitted (the minimal valid set + a styles part for real headings):
|
|
||||||
[Content_Types].xml
|
|
||||||
_rels/.rels
|
|
||||||
word/_rels/document.xml.rels
|
|
||||||
word/styles.xml (Title / Heading1 / Heading2 / Normal)
|
|
||||||
word/document.xml (the content)
|
|
||||||
|
|
||||||
Like the markdown projector it reads only the IR's realized sentences; it
|
|
||||||
invents nothing. The surface differs, the faithful content does not.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import io
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import zipfile
|
|
||||||
from xml.sax.saxutils import escape
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
from document_ir import DocumentIR # noqa: E402
|
|
||||||
from projectors.base import register # noqa: E402
|
|
||||||
|
|
||||||
_CONTENT_TYPES = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
||||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
||||||
<Default Extension="xml" ContentType="application/xml"/>
|
|
||||||
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
|
||||||
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
|
|
||||||
</Types>"""
|
|
||||||
|
|
||||||
_RELS = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
||||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
|
||||||
</Relationships>"""
|
|
||||||
|
|
||||||
_DOC_RELS = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
||||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
||||||
</Relationships>"""
|
|
||||||
|
|
||||||
_W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
||||||
|
|
||||||
_STYLES = f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<w:styles xmlns:w="{_W}">
|
|
||||||
<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/>
|
|
||||||
<w:rPr><w:sz w:val="22"/></w:rPr></w:style>
|
|
||||||
<w:style w:type="paragraph" w:styleId="Title"><w:name w:val="Title"/>
|
|
||||||
<w:pPr><w:spacing w:after="240"/></w:pPr>
|
|
||||||
<w:rPr><w:b/><w:sz w:val="52"/></w:rPr></w:style>
|
|
||||||
<w:style w:type="paragraph" w:styleId="Subtitle"><w:name w:val="Subtitle"/>
|
|
||||||
<w:rPr><w:i/><w:sz w:val="28"/><w:color w:val="555555"/></w:rPr></w:style>
|
|
||||||
<w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/>
|
|
||||||
<w:pPr><w:spacing w:before="240" w:after="120"/><w:outlineLvl w:val="0"/></w:pPr>
|
|
||||||
<w:rPr><w:b/><w:sz w:val="34"/></w:rPr></w:style>
|
|
||||||
<w:style w:type="paragraph" w:styleId="Heading2"><w:name w:val="heading 2"/>
|
|
||||||
<w:pPr><w:spacing w:before="200" w:after="100"/><w:outlineLvl w:val="1"/></w:pPr>
|
|
||||||
<w:rPr><w:b/><w:sz w:val="28"/></w:rPr></w:style>
|
|
||||||
</w:styles>"""
|
|
||||||
|
|
||||||
|
|
||||||
def _para(text: str, style: str | None = None) -> str:
|
|
||||||
ppr = f"<w:pPr><w:pStyle w:val=\"{style}\"/></w:pPr>" if style else ""
|
|
||||||
return (f"<w:p>{ppr}<w:r><w:t xml:space=\"preserve\">"
|
|
||||||
f"{escape(text)}</w:t></w:r></w:p>")
|
|
||||||
|
|
||||||
|
|
||||||
class DocxProjector:
|
|
||||||
surface = "docx"
|
|
||||||
media_type = ("application/vnd.openxmlformats-officedocument."
|
|
||||||
"wordprocessingml.document")
|
|
||||||
ext = "docx"
|
|
||||||
modality = "text"
|
|
||||||
|
|
||||||
def _document_xml(self, doc: DocumentIR) -> str:
|
|
||||||
body: list[str] = [_para(doc.title, "Title")]
|
|
||||||
if doc.subtitle:
|
|
||||||
body.append(_para(doc.subtitle, "Subtitle"))
|
|
||||||
abstract = doc.meta.get("abstract")
|
|
||||||
if abstract is not None and abstract.sentences:
|
|
||||||
body.append(_para(abstract.text()))
|
|
||||||
for sec in doc.sections:
|
|
||||||
style = "Heading1" if sec.level <= 1 else "Heading2"
|
|
||||||
body.append(_para(sec.heading, style))
|
|
||||||
for block in sec.blocks:
|
|
||||||
t = block.text()
|
|
||||||
if t:
|
|
||||||
body.append(_para(t))
|
|
||||||
return (f"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
|
|
||||||
f"<w:document xmlns:w=\"{_W}\"><w:body>"
|
|
||||||
+ "".join(body)
|
|
||||||
+ "<w:sectPr><w:pgSz w:w=\"12240\" w:h=\"15840\"/>"
|
|
||||||
"<w:pgMar w:top=\"1440\" w:right=\"1440\" w:bottom=\"1440\" "
|
|
||||||
"w:left=\"1440\"/></w:sectPr></w:body></w:document>")
|
|
||||||
|
|
||||||
def project(self, doc: DocumentIR) -> bytes:
|
|
||||||
buf = io.BytesIO()
|
|
||||||
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z:
|
|
||||||
z.writestr("[Content_Types].xml", _CONTENT_TYPES)
|
|
||||||
z.writestr("_rels/.rels", _RELS)
|
|
||||||
z.writestr("word/_rels/document.xml.rels", _DOC_RELS)
|
|
||||||
z.writestr("word/styles.xml", _STYLES)
|
|
||||||
z.writestr("word/document.xml", self._document_xml(doc))
|
|
||||||
return buf.getvalue()
|
|
||||||
|
|
||||||
|
|
||||||
register(DocxProjector())
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
"""markdown.py — the Markdown surface projector (text facet).
|
|
||||||
|
|
||||||
The most tractable surface, and the reference implementation: reads the IR's
|
|
||||||
realized sentences and lays them out as Markdown. Introduces no content — it is
|
|
||||||
pure typography over the faithful text the realizer produced.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
from document_ir import DocumentIR # noqa: E402
|
|
||||||
from projectors.base import register # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
class MarkdownProjector:
|
|
||||||
surface = "markdown"
|
|
||||||
media_type = "text/markdown"
|
|
||||||
ext = "md"
|
|
||||||
modality = "text"
|
|
||||||
|
|
||||||
def render_str(self, doc: DocumentIR) -> str:
|
|
||||||
lines: list[str] = [f"# {doc.title}"]
|
|
||||||
if doc.subtitle:
|
|
||||||
lines.append(f"\n*{doc.subtitle}*")
|
|
||||||
abstract = doc.meta.get("abstract")
|
|
||||||
if abstract is not None and abstract.sentences:
|
|
||||||
lines.append("")
|
|
||||||
lines.append(abstract.text())
|
|
||||||
for sec in doc.sections:
|
|
||||||
lines.append("")
|
|
||||||
lines.append(f"{'#' * max(2, sec.level)} {sec.heading}")
|
|
||||||
for block in sec.blocks:
|
|
||||||
body = block.text()
|
|
||||||
if body:
|
|
||||||
lines.append("")
|
|
||||||
lines.append(body)
|
|
||||||
return "\n".join(lines) + "\n"
|
|
||||||
|
|
||||||
def project(self, doc: DocumentIR) -> bytes:
|
|
||||||
return self.render_str(doc).encode("utf-8")
|
|
||||||
|
|
||||||
|
|
||||||
register(MarkdownProjector())
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
"""midi.py — the MUSIC surface projector: geometry -> symbolic music (MIDI).
|
|
||||||
|
|
||||||
The first NON-TEXT surface, and the proof of the general shape. "Music is
|
|
||||||
language and it is math" (Will): symbolic music is tractable and geometry-native,
|
|
||||||
so it is the natural efferent twin to try first after text.
|
|
||||||
|
|
||||||
CRUCIALLY this projector does NOT read the realized sentences. It reads the IR's
|
|
||||||
GEOMETRY facet — ``block.provenance`` — and DECODES each edge onto a musical
|
|
||||||
surface. That is the whole thesis of the multimodal projector: the same
|
|
||||||
geometry-carrying IR drives text AND music; a text projector reads the words, a
|
|
||||||
music projector reads the meaning-geometry. The mapping is deterministic and
|
|
||||||
faithful to the geometry's structure:
|
|
||||||
|
|
||||||
relation lemma -> scale degree (same relation -> same pitch class;
|
|
||||||
meaning has a consistent sonic form)
|
|
||||||
polarity -> mode (aff = major third above; neg = minor
|
|
||||||
third / lowered — SACRED polarity is
|
|
||||||
audible, a negated edge sounds negated)
|
|
||||||
confidence -> note duration (stronger grounding rings longer)
|
|
||||||
importance -> velocity (more important source = louder)
|
|
||||||
section -> phrase + register shift (structure becomes musical form)
|
|
||||||
|
|
||||||
Own-the-core: a Standard MIDI File is a header chunk + a track chunk of
|
|
||||||
delta-timed events. We emit the raw bytes with ``struct`` — no external MIDI
|
|
||||||
library. Format 0, one track.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import io
|
|
||||||
import os
|
|
||||||
import struct
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
from document_ir import DocumentIR, Provenance # noqa: E402
|
|
||||||
from projectors.base import TwoStageProjector, register # noqa: E402
|
|
||||||
|
|
||||||
_TICKS = 480 # ticks per quarter note
|
|
||||||
_C_MAJOR = [0, 2, 4, 5, 7, 9, 11] # semitone offsets of a diatonic scale
|
|
||||||
|
|
||||||
|
|
||||||
def _vlq(n: int) -> bytes:
|
|
||||||
"""MIDI variable-length quantity encoding of a delta time."""
|
|
||||||
if n == 0:
|
|
||||||
return b"\x00"
|
|
||||||
out = bytearray()
|
|
||||||
out.append(n & 0x7F)
|
|
||||||
n >>= 7
|
|
||||||
while n:
|
|
||||||
out.insert(0, (n & 0x7F) | 0x80)
|
|
||||||
n >>= 7
|
|
||||||
return bytes(out)
|
|
||||||
|
|
||||||
|
|
||||||
def _degree_for(relation: str) -> int:
|
|
||||||
"""Stable scale degree for a relation lemma (same relation -> same pitch)."""
|
|
||||||
if not relation:
|
|
||||||
return 0
|
|
||||||
return sum(ord(c) for c in relation.lower()) % len(_C_MAJOR)
|
|
||||||
|
|
||||||
|
|
||||||
def _note_for(p: Provenance, base: int) -> tuple[int, int, int]:
|
|
||||||
"""(pitch, velocity, duration_ticks) for one geometry edge."""
|
|
||||||
root = base + _C_MAJOR[_degree_for(p.relation)]
|
|
||||||
# polarity -> mode: affirmed edges take the bright major third, negated edges
|
|
||||||
# take the darker minor third. The negation is AUDIBLE and never dropped.
|
|
||||||
third = 4 if p.polarity == "aff" else 3
|
|
||||||
pitch = max(24, min(96, root + (third if p.confidence >= 0.5 else 0)))
|
|
||||||
velocity = int(56 + 60 * min(1.0, max(0.0, p.importance)))
|
|
||||||
velocity = max(40, min(120, velocity))
|
|
||||||
# confidence -> duration: quarter .. dotted-half
|
|
||||||
dur = int(_TICKS * (0.5 + 1.5 * min(1.0, max(0.0, p.confidence))))
|
|
||||||
return pitch, velocity, dur
|
|
||||||
|
|
||||||
|
|
||||||
# a mode-profile: the pluggable musical knob (the peer's mode_profile). Scale +
|
|
||||||
# tempo. Swapping this profile re-voices the SAME geometry — surface as parameter.
|
|
||||||
_DEFAULT_PROFILE = {"scale": _C_MAJOR, "tempo_us": 500000,
|
|
||||||
"registers": [60, 55, 64, 50, 67, 48], "program": 0}
|
|
||||||
|
|
||||||
|
|
||||||
class MidiProjector(TwoStageProjector):
|
|
||||||
"""geometry -> symbolic music, in the shared two-stage shape.
|
|
||||||
|
|
||||||
``plan(frame)`` -> a music_spec: an ordered list of note dicts derived
|
|
||||||
deterministically from the frame's provenance geometry
|
|
||||||
(the peer's ``plan(frame, profile) -> spec``).
|
|
||||||
``realize(spec)`` -> Standard MIDI File bytes (the peer's
|
|
||||||
``realize(spec, profile) -> surface``; here the surface
|
|
||||||
is symbolic MIDI, the minimal audio proof — a richer
|
|
||||||
additive-synth audio projector conforms identically).
|
|
||||||
"""
|
|
||||||
|
|
||||||
surface = "midi"
|
|
||||||
media_type = "audio/midi"
|
|
||||||
ext = "mid"
|
|
||||||
modality = "audio"
|
|
||||||
|
|
||||||
def __init__(self, profile: dict | None = None):
|
|
||||||
self.profile = profile or _DEFAULT_PROFILE
|
|
||||||
|
|
||||||
# -- stage 1: meaning-geometry -> music_spec (reads the GEOMETRY facet) -- #
|
|
||||||
def plan(self, doc: DocumentIR) -> list[dict]:
|
|
||||||
registers = self.profile["registers"]
|
|
||||||
spec: list[dict] = []
|
|
||||||
for si, sec in enumerate(doc.sections):
|
|
||||||
base = registers[si % len(registers)]
|
|
||||||
provs = [p for p in sec.all_provenance()
|
|
||||||
if p.kind in ("fact", "interpretation")]
|
|
||||||
for i, p in enumerate(provs):
|
|
||||||
pitch, vel, dur = _note_for(p, base)
|
|
||||||
spec.append({"pitch": pitch, "velocity": vel, "dur": dur,
|
|
||||||
"rest_before": (_TICKS // 2) if (si > 0 and i == 0) else 0,
|
|
||||||
"relation": p.relation, "polarity": p.polarity})
|
|
||||||
return spec
|
|
||||||
|
|
||||||
# -- stage 2: music_spec -> MIDI bytes (own-core, no library) ------------ #
|
|
||||||
def realize(self, spec: list[dict]) -> bytes:
|
|
||||||
ev = bytearray()
|
|
||||||
ev += _vlq(0) + b"\xFF\x51\x03" + struct.pack(">I", self.profile["tempo_us"])[1:]
|
|
||||||
ev += _vlq(0) + bytes([0xC0, self.profile["program"] & 0x7F])
|
|
||||||
for note in spec:
|
|
||||||
ev += _vlq(note["rest_before"]) + bytes([0x90, note["pitch"], note["velocity"]])
|
|
||||||
ev += _vlq(note["dur"]) + bytes([0x80, note["pitch"], 0])
|
|
||||||
ev += _vlq(0) + b"\xFF\x2F\x00"
|
|
||||||
track = bytes(ev)
|
|
||||||
buf = io.BytesIO()
|
|
||||||
buf.write(b"MThd" + struct.pack(">IHHH", 6, 0, 1, _TICKS))
|
|
||||||
buf.write(b"MTrk" + struct.pack(">I", len(track)) + track)
|
|
||||||
return buf.getvalue()
|
|
||||||
|
|
||||||
|
|
||||||
register(MidiProjector())
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
"""seams.py — documented efferent seams for IMAGE and VIDEO surfaces.
|
|
||||||
|
|
||||||
These are NOT implemented (per the build rails: architect, do not overbuild).
|
|
||||||
They are registered as first-class seams so the interface PROVES it accepts
|
|
||||||
future non-text projectors without any upstream change. Each documents exactly
|
|
||||||
what its decoder would read from the geometry-carrying IR, making the multimodal
|
|
||||||
generalization concrete rather than hand-wavy.
|
|
||||||
|
|
||||||
The symmetry that guarantees these are possible, not moonshots: they are the
|
|
||||||
efferent twins of multimodal INGEST. If meaning can HOLD an image (ingest as
|
|
||||||
first-class geometry), meaning can PROJECT one back. Video = image x sound x
|
|
||||||
TIME, and the engram already stores time (chronoception). So video falls out of
|
|
||||||
an image projector + the music projector + the stored temporal ordering.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
from document_ir import DocumentIR # noqa: E402
|
|
||||||
from projectors.base import register # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
class _Seam:
|
|
||||||
"""A registered-but-unimplemented projector. Names its decoder contract."""
|
|
||||||
|
|
||||||
def project(self, doc: DocumentIR) -> bytes: # pragma: no cover - seam
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"{self.surface!r} projector is a documented seam, not yet built. "
|
|
||||||
f"Decoder contract: {self.decoder_contract}")
|
|
||||||
|
|
||||||
|
|
||||||
class ImageProjector(_Seam):
|
|
||||||
surface = "image"
|
|
||||||
media_type = "image/png"
|
|
||||||
ext = "png"
|
|
||||||
modality = "image"
|
|
||||||
decoder_contract = (
|
|
||||||
"reads block.provenance as a spatial layout — nodes become regions, edges "
|
|
||||||
"become adjacencies; salience/importance drive size/contrast; polarity "
|
|
||||||
"drives figure/ground. The efferent twin of image ingest (a geometry->raster "
|
|
||||||
"decoder, learned or engineered), exactly mirroring the embedder that turned "
|
|
||||||
"the image INTO geometry.")
|
|
||||||
|
|
||||||
|
|
||||||
class VideoProjector(_Seam):
|
|
||||||
surface = "video"
|
|
||||||
media_type = "video/mp4"
|
|
||||||
ext = "mp4"
|
|
||||||
modality = "video"
|
|
||||||
decoder_contract = (
|
|
||||||
"image x sound x TIME. Composes the image projector (per-keyframe geometry "
|
|
||||||
"layout) with the midi/music projector (score) along the geometry's stored "
|
|
||||||
"temporal ordering (chronoception). Needs no new principle once image + music "
|
|
||||||
"exist — only a muxer.")
|
|
||||||
|
|
||||||
|
|
||||||
register(ImageProjector())
|
|
||||||
register(VideoProjector())
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
"""provenance.py — the faithfulness audit + geometry->section trace.
|
|
||||||
|
|
||||||
A document projected from geometry is only worth anything if every claim traces
|
|
||||||
back. This module walks the DocumentIR and proves the discipline held:
|
|
||||||
|
|
||||||
* ZERO ungrounded claims (every fact/interpretation has a real node id),
|
|
||||||
* every emitted sentence maps to a geometry edge (or is a marked connective),
|
|
||||||
* SACRED polarity survived (negations are reported, never silently dropped),
|
|
||||||
* COHERE introduced no new geometry (connectives carry no claim).
|
|
||||||
|
|
||||||
It emits both a machine verdict and a human-readable geometry->section table.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from document_ir import DocumentIR
|
|
||||||
|
|
||||||
|
|
||||||
def audit(doc: DocumentIR) -> dict:
|
|
||||||
provs = doc.all_provenance()
|
|
||||||
facts = [p for p in provs if p.kind in ("fact", "interpretation")]
|
|
||||||
connectives = [p for p in provs if p.kind == "connective"]
|
|
||||||
ungrounded = [p for p in facts if not p.node_id]
|
|
||||||
negations = [p for p in facts if p.polarity == "neg"]
|
|
||||||
node_ids = sorted({p.node_id for p in facts if p.node_id})
|
|
||||||
return {
|
|
||||||
"claims": len(facts),
|
|
||||||
"connectives": len(connectives),
|
|
||||||
"ungrounded_claims": len(ungrounded),
|
|
||||||
"negations_preserved": len(negations),
|
|
||||||
"distinct_source_nodes": len(node_ids),
|
|
||||||
"faithful": len(ungrounded) == 0,
|
|
||||||
"source_nodes": node_ids,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def trace_table(doc: DocumentIR) -> str:
|
|
||||||
"""Human-readable geometry -> section -> claim provenance table."""
|
|
||||||
lines = ["# Provenance — every claim traces geometry", ""]
|
|
||||||
lines.append(f"**Document:** {doc.title}")
|
|
||||||
a = audit(doc)
|
|
||||||
lines.append(f"**Claims:** {a['claims']} · **Ungrounded:** "
|
|
||||||
f"{a['ungrounded_claims']} · **Negations preserved:** "
|
|
||||||
f"{a['negations_preserved']} · **Source nodes:** "
|
|
||||||
f"{a['distinct_source_nodes']} · **Faithful:** "
|
|
||||||
f"{'YES' if a['faithful'] else 'NO'}")
|
|
||||||
lines.append("")
|
|
||||||
for si, sec in enumerate(doc.sections, 1):
|
|
||||||
lines.append(f"## {si}. {sec.heading}")
|
|
||||||
lines.append(f"_seed nodes: {', '.join(i[:8] for i in sec.seed_ids)}_")
|
|
||||||
lines.append("")
|
|
||||||
lines.append("| # | realized claim | traces geometry edge |")
|
|
||||||
lines.append("|---|----------------|----------------------|")
|
|
||||||
n = 0
|
|
||||||
for block in sec.blocks:
|
|
||||||
for sent, prov in zip(block.sentences, block.provenance):
|
|
||||||
if prov.kind == "connective":
|
|
||||||
continue
|
|
||||||
n += 1
|
|
||||||
edge = prov.trace().replace("|", "\\|")
|
|
||||||
s = sent.replace("|", "\\|")
|
|
||||||
lines.append(f"| {n} | {s} | {edge} |")
|
|
||||||
lines.append("")
|
|
||||||
return "\n".join(lines) + "\n"
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
"""realize.py — REALIZE stage: fill each planned section with faithful passages.
|
|
||||||
|
|
||||||
Scales the PROVEN realizer from a single assertion to a passage. For each
|
|
||||||
planned proposition we build a realizer-ready clause (the proven
|
|
||||||
``_prop_to_clause`` mapping) and run it through the proven engine
|
|
||||||
(``engine.realize``), which is a deterministic grammar with the SACRED negation
|
|
||||||
contract — it never invents. Each realized sentence is paired with a
|
|
||||||
:class:`Provenance` that pins it to the exact geometry edge it came from.
|
|
||||||
|
|
||||||
"Passage, not a list of sentences": within a section we lightly vary sentence
|
|
||||||
openings and group related claims, but we add NO content the geometry did not
|
|
||||||
assert. The only non-geometry words are function words the grammar already owns
|
|
||||||
(articles, "and", conjunction of same-subject claims). Document-level flow is
|
|
||||||
COHERE's job; this stage owns intra-section fluency + fidelity.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
_NT = os.path.expanduser("~/Desktop/neuron-talk")
|
|
||||||
_LR = os.path.expanduser("~/Desktop/lang-realizers")
|
|
||||||
for _p in (_NT, _LR):
|
|
||||||
if _p not in sys.path:
|
|
||||||
sys.path.insert(0, _p)
|
|
||||||
|
|
||||||
import engine # noqa: E402 (the proven no-LLM realizer)
|
|
||||||
from dialogue import _prop_to_clause # noqa: E402 (proven prop -> clause)
|
|
||||||
|
|
||||||
from document_ir import Block, DocumentIR, Provenance, Section # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
def _provenance_from(p, kind: str = "fact") -> Provenance:
|
|
||||||
return Provenance(
|
|
||||||
subj_id=p.source_node_id, subject=p.subject, relation=p.predicate,
|
|
||||||
obj=p.object, polarity=p.polarity, confidence=round(float(p.confidence), 3),
|
|
||||||
node_id=p.source_node_id, kind=kind,
|
|
||||||
importance=float(getattr(p, "node_importance", 0.0) or 0.0),
|
|
||||||
salience=0.0,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
import re as _re
|
|
||||||
|
|
||||||
# a well-formed declarative opens with a determiner, a proper noun, "I", or a
|
|
||||||
# capitalized head — not a mis-parsed object pronoun or a copula fragment.
|
|
||||||
_BAD_OPENERS = _re.compile(r"^(Me |It is I|There is|This is it|That is it)\b")
|
|
||||||
_VACUOUS = _re.compile(r"^\w+ (is|are|was|were) (it|no|nothing|empty|those|this|that)\.?$",
|
|
||||||
_re.I)
|
|
||||||
|
|
||||||
|
|
||||||
def _good_sentence(text: str) -> bool:
|
|
||||||
"""Fluency gate — drops degenerate realizations. NEVER loosens faithfulness;
|
|
||||||
it only refuses to SPEAK a claim whose surface came out malformed."""
|
|
||||||
words = text.rstrip(".").split()
|
|
||||||
if len(words) < 3:
|
|
||||||
return False
|
|
||||||
if _BAD_OPENERS.search(text):
|
|
||||||
return False
|
|
||||||
if _VACUOUS.match(text):
|
|
||||||
return False
|
|
||||||
# a sentence that is mostly one-letter/two-letter tokens is a parse artifact
|
|
||||||
short = sum(1 for w in words if len(w.strip(".,'")) <= 2)
|
|
||||||
if short > len(words) / 2:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _realize_prop(p, lang: str = "en") -> tuple[str, Provenance] | None:
|
|
||||||
"""One proposition -> (faithful sentence, provenance) or None if it drops."""
|
|
||||||
clause = _prop_to_clause(p)
|
|
||||||
text = engine.realize(clause, lang)
|
|
||||||
if not text or not text.strip():
|
|
||||||
return None
|
|
||||||
text = text.strip()
|
|
||||||
if not text.endswith((".", "!", "?")):
|
|
||||||
text += "."
|
|
||||||
# capitalize first character (proper nouns / "I" already handled by grammar)
|
|
||||||
text = text[0].upper() + text[1:]
|
|
||||||
if not _good_sentence(text):
|
|
||||||
return None
|
|
||||||
return text, _provenance_from(p)
|
|
||||||
|
|
||||||
|
|
||||||
def realize_document(doc: DocumentIR, lang: str = "en") -> DocumentIR:
|
|
||||||
"""Fill every planned section's blocks with faithful, realized passages."""
|
|
||||||
for sec in doc.sections:
|
|
||||||
planned = sec.__dict__.get("_planned_props", [])
|
|
||||||
block = Block(role="body")
|
|
||||||
summary_bits: list[str] = []
|
|
||||||
for p in planned:
|
|
||||||
r = _realize_prop(p, lang)
|
|
||||||
if r is None:
|
|
||||||
continue
|
|
||||||
text, prov = r
|
|
||||||
block.sentences.append(text)
|
|
||||||
block.provenance.append(prov)
|
|
||||||
if len(summary_bits) < 1:
|
|
||||||
# a short grounded gloss for TOC / pptx bullets
|
|
||||||
obj = (prov.obj or "").strip().rstrip(".")
|
|
||||||
if obj:
|
|
||||||
summary_bits.append(obj)
|
|
||||||
if block.sentences:
|
|
||||||
sec.blocks.append(block)
|
|
||||||
sec.summary = summary_bits[0] if summary_bits else ""
|
|
||||||
# drop the transient planning payload; the IR is now self-contained
|
|
||||||
sec.__dict__.pop("_planned_props", None)
|
|
||||||
sec.__dict__.pop("_node", None)
|
|
||||||
|
|
||||||
# prune sections that realized to nothing
|
|
||||||
doc.sections = [s for s in doc.sections if s.blocks]
|
|
||||||
return doc
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
// accent.el - A British-RP ACCENT as an INGESTED TRANSFORM-GEOMETRY, composed
|
|
||||||
// onto the voice (voice (+) accent, SEPARABLE). Reads elp/data/british-accent.psv
|
|
||||||
// into an accent MANIFOLD in the engram (override nodes + a shared accent hub),
|
|
||||||
// and the render reads the RP formant overrides + the non-rhotic rule back from
|
|
||||||
// that geometry. NO accent targets live in code — same discipline as the base
|
|
||||||
// phonetics. PROVENANCE NOTE: the RP Hz values are PROVISIONAL (reconstructed-
|
|
||||||
// from-knowledge approximations, cite Deterding1997 / Hawkins&Midgley2005 /
|
|
||||||
// Wells1982) pending transcription from the published tables — the PIPELINE is
|
|
||||||
// the deliverable; exact values are being source-verified separately.
|
|
||||||
|
|
||||||
fn ingest_accent(path: String) -> [String] {
|
|
||||||
let content: String = fs_read(path)
|
|
||||||
let lines: [String] = str_split(content, "\n")
|
|
||||||
let nl: Int = native_list_len(lines)
|
|
||||||
let amap: [String] = native_list_empty()
|
|
||||||
let hub: String = engram_node("accent british-rp prov=PROVISIONAL cite=Deterding1997-HawkinsMidgley2005-Wells1982", "Accent", 80)
|
|
||||||
let li: Int = 0
|
|
||||||
while li < nl {
|
|
||||||
let line: String = native_list_get(lines, li)
|
|
||||||
let ll: Int = str_len(line)
|
|
||||||
let skip: Int = 0
|
|
||||||
if ll < 3 {
|
|
||||||
skip = 1
|
|
||||||
}
|
|
||||||
if skip == 0 {
|
|
||||||
let first: Int = str_char_code(line, 0)
|
|
||||||
if first == 35 {
|
|
||||||
skip = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if skip == 0 {
|
|
||||||
let f: [String] = str_split(line, "|")
|
|
||||||
let nf: Int = native_list_len(f)
|
|
||||||
if nf >= 6 {
|
|
||||||
let key: String = native_list_get(f, 0)
|
|
||||||
let f1: String = native_list_get(f, 1)
|
|
||||||
let f2: String = native_list_get(f, 2)
|
|
||||||
let f3: String = native_list_get(f, 3)
|
|
||||||
let kind: String = native_list_get(f, 4)
|
|
||||||
let set: String = native_list_get(f, 5)
|
|
||||||
let cont: String = "accent british-rp " + key + " f1=" + f1 + " f2=" + f2 + " f3=" + f3 + " kind=" + kind + " set=" + set + " prov=PROVISIONAL cite=Deterding1997-HawkinsMidgley2005-Wells1982"
|
|
||||||
let id: String = engram_node(cont, "AccentTarget", 80)
|
|
||||||
amap = native_list_append(amap, key)
|
|
||||||
amap = native_list_append(amap, cont)
|
|
||||||
engram_connect(id, hub, 80, "of_accent")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
li = li + 1
|
|
||||||
}
|
|
||||||
return amap
|
|
||||||
}
|
|
||||||
|
|
||||||
// RP formant override for a phoneme, read from the accent manifold. Returns
|
|
||||||
// [f1,f2,f3] for a vowel_override record, or an empty list if none / a rule.
|
|
||||||
fn accent_formants(amap: [String], code: String) -> [Int] {
|
|
||||||
let out: [Int] = native_list_empty()
|
|
||||||
let id: String = sp_map_get(amap, code)
|
|
||||||
if str_eq(id, "") {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
let j: String = id
|
|
||||||
let isrule: Int = str_index_of(j, "drop_coda")
|
|
||||||
if isrule >= 0 {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
let f1: Int = parse_uint_from(j, "f1=")
|
|
||||||
if f1 <= 0 {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
let out = native_list_append(out, f1)
|
|
||||||
let out = native_list_append(out, parse_uint_from(j, "f2="))
|
|
||||||
let out = native_list_append(out, parse_uint_from(j, "f3="))
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is this accent non-rhotic? (reads the R rule node from the manifold)
|
|
||||||
fn is_nonrhotic(amap: [String]) -> Int {
|
|
||||||
let id: String = sp_map_get(amap, "R")
|
|
||||||
if str_eq(id, "") {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
let hit: Int = str_index_of(id, "drop_coda")
|
|
||||||
if hit >= 0 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is this symbol a vowel? Membership in the vowel-set derived from the phonetics
|
|
||||||
// source's class column (phonological structure — the FORMANT NUMBERS still come
|
|
||||||
// from the organ manifold; this is only the categorical class for the rule).
|
|
||||||
fn is_vowel_sym(vset: [String], sym: String) -> Int {
|
|
||||||
let n: Int = native_list_len(vset)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
if str_eq(native_list_get(vset, i), sym) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
i = i + 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-rhotic transform: drop a post-vocalic CODA /R/ — an R whose next non-SIL
|
|
||||||
// phoneme is NOT a vowel (a consonant, or end of utterance). Keep INTERVOCALIC/
|
|
||||||
// onset R (next non-SIL phoneme is a vowel, e.g. the medial R in N UW R AA N).
|
|
||||||
fn apply_rhoticity(codes: [String], vset: [String]) -> [String] {
|
|
||||||
let n: Int = native_list_len(codes)
|
|
||||||
let out: [String] = native_list_empty()
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let c: String = native_list_get(codes, i)
|
|
||||||
let keep: Int = 1
|
|
||||||
if str_eq(c, "R") {
|
|
||||||
let jx: Int = i + 1
|
|
||||||
let nextv: Int = 0
|
|
||||||
while jx < n {
|
|
||||||
let ncode: String = native_list_get(codes, jx)
|
|
||||||
if str_eq(ncode, "SIL") {
|
|
||||||
jx = jx + 1
|
|
||||||
} else {
|
|
||||||
nextv = is_vowel_sym(vset, ncode)
|
|
||||||
jx = n + 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if nextv == 0 {
|
|
||||||
keep = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if keep == 1 {
|
|
||||||
out = native_list_append(out, c)
|
|
||||||
}
|
|
||||||
i = i + 1
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
// audio-demo.el - Drive the native audio surface: render a tone per instrument
|
|
||||||
// from its LEARNED signature, then render a small meaning-phrase "piece".
|
|
||||||
// Entry point: top-level statement calls main() (same convention as the
|
|
||||||
// examples' top-level println(run_test())).
|
|
||||||
|
|
||||||
fn micros_to_str(xs: [Int]) -> String {
|
|
||||||
let n: Int = native_list_len(xs)
|
|
||||||
let out: String = ""
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
if i > 0 { let out: String = out + "," }
|
|
||||||
let out: String = out + int_to_str(native_list_get(xs, i))
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render a 1.0s A4 (midi 69) tone from a signature file, print the parsed
|
|
||||||
// partials (proving the numbers came from the engram .sig), write the WAV.
|
|
||||||
fn render_tone(name: String, sigpath: String, outpath: String, table: [Int]) -> Int {
|
|
||||||
let lines: [String] = sig_load(sigpath)
|
|
||||||
let partials: [Int] = parse_micros(sig_field(lines, "partials"))
|
|
||||||
println("[" + name + "] partials_n=" + sig_field(lines, "partials_n") + " parsed_partials_micro(scale 1e6)=" + micros_to_str(partials))
|
|
||||||
println("[" + name + "] raw partials line from .sig = " + sig_field(lines, "partials"))
|
|
||||||
let freq: Int = freq_of_midi(69)
|
|
||||||
let note: [Int] = synth_from_sig(lines, freq, 1000, 900, 44100, table)
|
|
||||||
let n: Int = native_list_len(note)
|
|
||||||
let ok: Int = wav_write(outpath, note, n, 44100)
|
|
||||||
println("[" + name + "] rendered " + int_to_str(n) + " samples -> " + outpath + " (write_ok=" + int_to_str(ok) + ")")
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_demo() -> Int {
|
|
||||||
let table: [Int] = sin_table()
|
|
||||||
fs_mkdir("/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out")
|
|
||||||
|
|
||||||
println("=== TONES: render A4 (midi 69) from each learned signature ===")
|
|
||||||
render_tone("flute", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/sig/flute.sig", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/tone-flute.wav", table)
|
|
||||||
render_tone("clarinet", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/sig/clarinet.sig", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/tone-clarinet.wav", table)
|
|
||||||
render_tone("violin", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/sig/violin.sig", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/tone-violin.wav", table)
|
|
||||||
render_tone("piano", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/sig/piano.sig", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/tone-piano.wav", table)
|
|
||||||
render_tone("organ", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/sig/organ.sig", "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/tone-organ.wav", table)
|
|
||||||
|
|
||||||
println("")
|
|
||||||
println("=== PIECE: a 6-frame meaning phrase (incl. a NEG frame) ===")
|
|
||||||
let frames: [[String]] = native_list_empty()
|
|
||||||
let frames: [[String]] = native_list_append(frames, audio_frame("agent", "aff", "0.9", "0.8", "0", "s1"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, audio_frame("theme", "aff", "0.7", "0.6", "0", "s2"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, audio_frame("cause", "aff", "0.8", "0.9", "1", "s3"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, audio_frame("negation", "neg", "0.85", "0.7", "0", "s4"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, audio_frame("goal", "aff", "0.6", "0.5", "1", "s5"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, audio_frame("result", "aff", "0.95", "1.0", "0", "s6"))
|
|
||||||
|
|
||||||
// Print the plan so the NEG frame's minor third (+3) vs major (+4) is visible.
|
|
||||||
let nf: Int = native_list_len(frames)
|
|
||||||
let fi: Int = 0
|
|
||||||
while fi < nf {
|
|
||||||
let frame: [String] = native_list_get(frames, fi)
|
|
||||||
let plan: [Int] = plan_note(frame)
|
|
||||||
let pol: String = surface_get(frame, "polarity")
|
|
||||||
let third_name: String = "major(+4)"
|
|
||||||
if str_eq(pol, "neg") { let third_name: String = "MINOR(+3)" }
|
|
||||||
println("frame " + int_to_str(fi) + " relation=" + surface_get(frame, "relation") + " polarity=" + pol + " -> midi=" + int_to_str(native_list_get(plan, 0)) + " dur_ms=" + int_to_str(native_list_get(plan, 1)) + " amp_pm=" + int_to_str(native_list_get(plan, 2)) + " third=" + third_name)
|
|
||||||
let fi: Int = fi + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let piano_lines: [String] = sig_load("/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/sig/piano.sig")
|
|
||||||
let total: Int = realize_audio(frames, piano_lines, "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/piece.wav", 44100, table)
|
|
||||||
println("PIECE rendered " + int_to_str(total) + " samples -> /Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/piece.wav")
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
println("audio-demo main returned samples=" + int_to_str(run_demo()))
|
|
||||||
@@ -1,400 +0,0 @@
|
|||||||
// audio-surface.el - Native own-core additive-synthesis audio surface.
|
|
||||||
//
|
|
||||||
// The AUDIO efferent seam, native, no Python and no library. This renders real
|
|
||||||
// PCM .wav bytes from instrument SIGNATURES read from engram-sourced .sig data
|
|
||||||
// files (elp/faculty/sig/*.sig) - the partial amplitudes are NEVER literals in
|
|
||||||
// this source; they are parsed from the learned signature at run time. That is
|
|
||||||
// the whole proof: render-from-learned-signatures.
|
|
||||||
//
|
|
||||||
// EL has no float arithmetic operator (codegen emits raw int64 ops for + - * /
|
|
||||||
// on the shared 64-bit slot) and no float-arithmetic natives - so ALL synthesis
|
|
||||||
// math here is own-core INTEGER fixed-point. Angles use a quarter-wave sine
|
|
||||||
// table (scale 10000) from a fixed-point Taylor series; amplitudes are parsed to
|
|
||||||
// micro (scale 1e6) straight from the .sig text; frequencies are milliHz ints.
|
|
||||||
//
|
|
||||||
// Pipeline mirrors the two-stage projector (midi.py): plan_note(frame) reads a
|
|
||||||
// frame's meaning-geometry slot-map and derives (pitch, duration, amplitude);
|
|
||||||
// realize_audio SUPERPOSES the signature's partials (the compose op) and
|
|
||||||
// serialises RIFF/WAVE. Same frame -> midi OR audio.
|
|
||||||
|
|
||||||
// -- integer decimal + string helpers -----------------------------------------
|
|
||||||
|
|
||||||
fn str_to_int_el(s: String) -> Int {
|
|
||||||
let n: Int = str_len(s)
|
|
||||||
let i: Int = 0
|
|
||||||
let v: Int = 0
|
|
||||||
let neg: Bool = false
|
|
||||||
while i < n {
|
|
||||||
let c: Int = str_char_code(s, i)
|
|
||||||
if c == 45 { let neg: Bool = true }
|
|
||||||
if c >= 48 {
|
|
||||||
if c < 58 {
|
|
||||||
let v: Int = v * 10 + (c - 48)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
if neg { return 0 - v }
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_micro(s: String) -> Int {
|
|
||||||
let dot: Int = str_index_of(s, ".")
|
|
||||||
if dot < 0 {
|
|
||||||
return str_to_int_el(s) * 1000000
|
|
||||||
}
|
|
||||||
let n: Int = str_len(s)
|
|
||||||
let ipart: String = str_slice(s, 0, dot)
|
|
||||||
let fpart: String = str_slice(s, dot + 1, n)
|
|
||||||
let iv: Int = str_to_int_el(ipart)
|
|
||||||
let fv: Int = 0
|
|
||||||
let scale: Int = 100000
|
|
||||||
let fn2: Int = str_len(fpart)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < 6 {
|
|
||||||
let d: Int = 0
|
|
||||||
if i < fn2 {
|
|
||||||
let d: Int = str_char_code(fpart, i) - 48
|
|
||||||
}
|
|
||||||
let fv: Int = fv + d * scale
|
|
||||||
let scale: Int = scale / 10
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return iv * 1000000 + fv
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- signature (engram data file) loader ---------------------------------------
|
|
||||||
|
|
||||||
fn sig_load(path: String) -> [String] {
|
|
||||||
let text: String = fs_read(path)
|
|
||||||
return str_split(text, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sig_field(lines: [String], key: String) -> String {
|
|
||||||
let pref: String = key + ": "
|
|
||||||
let n: Int = native_list_len(lines)
|
|
||||||
let plen: Int = str_len(pref)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let ln: String = native_list_get(lines, i)
|
|
||||||
if str_starts_with(ln, pref) {
|
|
||||||
return str_slice(ln, plen, str_len(ln))
|
|
||||||
}
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_micros(csv: String) -> [Int] {
|
|
||||||
let parts: [String] = str_split(csv, ",")
|
|
||||||
let n: Int = native_list_len(parts)
|
|
||||||
let out: [Int] = native_list_empty()
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let out: [Int] = native_list_append(out, parse_micro(native_list_get(parts, i)))
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- fixed-point sine (own-core, quarter-wave Taylor table, scale 10000) --------
|
|
||||||
|
|
||||||
fn sin_table() -> [Int] {
|
|
||||||
let HP: Int = 1570796
|
|
||||||
let t: [Int] = native_list_empty()
|
|
||||||
let q: Int = 0
|
|
||||||
while q < 257 {
|
|
||||||
let x: Int = q * HP / 256
|
|
||||||
let x2: Int = x * x / 1000000
|
|
||||||
let x3: Int = x2 * x / 1000000
|
|
||||||
let x5: Int = x3 * x2 / 1000000
|
|
||||||
let x7: Int = x5 * x2 / 1000000
|
|
||||||
let x9: Int = x7 * x2 / 1000000
|
|
||||||
let s: Int = x - x3 / 6 + x5 / 120 - x7 / 5040 + x9 / 362880
|
|
||||||
let t: [Int] = native_list_append(t, s / 100)
|
|
||||||
let q: Int = q + 1
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sin_lookup(t: [Int], phase: Int) -> Int {
|
|
||||||
let p: Int = phase % 1024
|
|
||||||
if p < 0 { let p: Int = p + 1024 }
|
|
||||||
let quad: Int = p / 256
|
|
||||||
let r: Int = p % 256
|
|
||||||
if quad == 0 { return native_list_get(t, r) }
|
|
||||||
if quad == 1 { return native_list_get(t, 256 - r) }
|
|
||||||
if quad == 2 { return 0 - native_list_get(t, r) }
|
|
||||||
return 0 - native_list_get(t, 256 - r)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isqrt_int(n: Int) -> Int {
|
|
||||||
if n <= 0 { return 0 }
|
|
||||||
let x: Int = n
|
|
||||||
let y: Int = (x + 1) / 2
|
|
||||||
while y < x {
|
|
||||||
let x: Int = y
|
|
||||||
let y: Int = (x + n / x) / 2
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// freq_of_midi: equal-tempered frequency in milliHz. 440000 mHz at midi 69.
|
|
||||||
fn freq_of_midi(m: Int) -> Int {
|
|
||||||
let f: Int = 440000
|
|
||||||
if m > 69 {
|
|
||||||
let k: Int = m - 69
|
|
||||||
let i: Int = 0
|
|
||||||
while i < k {
|
|
||||||
let f: Int = f * 1059463 / 1000000
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
if m < 69 {
|
|
||||||
let k: Int = 69 - m
|
|
||||||
let i: Int = 0
|
|
||||||
while i < k {
|
|
||||||
let f: Int = f * 1000000 / 1059463
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- envelope (ADSR), scale 1000 -----------------------------------------------
|
|
||||||
|
|
||||||
fn adsr_env(i: Int, total: Int, atk_n: Int, dec_n: Int, sus_pm: Int, rel_n: Int) -> Int {
|
|
||||||
if i < atk_n {
|
|
||||||
if atk_n == 0 { return 1000 }
|
|
||||||
return 1000 * i / atk_n
|
|
||||||
}
|
|
||||||
if i < atk_n + dec_n {
|
|
||||||
if dec_n == 0 { return sus_pm }
|
|
||||||
return 1000 - (1000 - sus_pm) * (i - atk_n) / dec_n
|
|
||||||
}
|
|
||||||
let rel_start: Int = total - rel_n
|
|
||||||
if i < rel_start {
|
|
||||||
return sus_pm
|
|
||||||
}
|
|
||||||
if rel_n == 0 { return 0 }
|
|
||||||
let left: Int = total - i
|
|
||||||
return sus_pm * left / rel_n
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- note synthesis: SUPERPOSE the learned partials -> [Int] samples -----------
|
|
||||||
fn note_samples(freq_mHz: Int, dur_ms: Int, rate: Int, partials: [Int], sumP: Int, b_micro: Int, vib_rate: Int, vib_cents: Int, atk_ms: Int, dec_ms: Int, sus_pm: Int, rel_ms: Int, amp_pm: Int, table: [Int]) -> [Int] {
|
|
||||||
let total: Int = dur_ms * rate / 1000
|
|
||||||
let atk_n: Int = atk_ms * rate / 1000
|
|
||||||
let dec_n: Int = dec_ms * rate / 1000
|
|
||||||
let rel_n: Int = rel_ms * rate / 1000
|
|
||||||
let np: Int = native_list_len(partials)
|
|
||||||
let half_mhz: Int = rate * 1000 / 2
|
|
||||||
let out: [Int] = native_list_empty()
|
|
||||||
let i: Int = 0
|
|
||||||
while i < total {
|
|
||||||
let acc: Int = 0
|
|
||||||
let k: Int = 0
|
|
||||||
while k < np {
|
|
||||||
let harm: Int = k + 1
|
|
||||||
let amp_k: Int = native_list_get(partials, k)
|
|
||||||
let factor: Int = 1000000
|
|
||||||
if b_micro > 0 {
|
|
||||||
let val: Int = 1000000 + b_micro * harm * harm
|
|
||||||
let factor: Int = isqrt_int(val * 1000000)
|
|
||||||
}
|
|
||||||
let fn_mhz: Int = freq_mHz * harm
|
|
||||||
let fn_mhz: Int = fn_mhz * factor / 1000000
|
|
||||||
if vib_cents > 0 {
|
|
||||||
if vib_rate > 0 {
|
|
||||||
let vphase: Int = i * vib_rate * 1024 / rate
|
|
||||||
let vs: Int = sin_lookup(table, vphase)
|
|
||||||
let vibf: Int = 1000000 + (vib_cents * vs * 833) / 10000
|
|
||||||
let fn_mhz: Int = fn_mhz * vibf / 1000000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if fn_mhz <= half_mhz {
|
|
||||||
let phase: Int = i * fn_mhz * 1024 / (rate * 1000)
|
|
||||||
let sv: Int = sin_lookup(table, phase)
|
|
||||||
let acc: Int = acc + sv * amp_k / 1000000
|
|
||||||
}
|
|
||||||
let k: Int = k + 1
|
|
||||||
}
|
|
||||||
let env: Int = adsr_env(i, total, atk_n, dec_n, sus_pm, rel_n)
|
|
||||||
let s16: Int = acc * 2800000 / sumP
|
|
||||||
let s16: Int = s16 * env / 1000
|
|
||||||
let s16: Int = s16 * amp_pm / 1000
|
|
||||||
if s16 > 32767 { let s16: Int = 32767 }
|
|
||||||
if s16 < 0 - 32767 { let s16: Int = 0 - 32767 }
|
|
||||||
let out: [Int] = native_list_append(out, s16)
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
fn synth_from_sig(lines: [String], freq_mHz: Int, dur_ms: Int, amp_pm: Int, rate: Int, table: [Int]) -> [Int] {
|
|
||||||
let partials: [Int] = parse_micros(sig_field(lines, "partials"))
|
|
||||||
let np: Int = native_list_len(partials)
|
|
||||||
let sumP: Int = 0
|
|
||||||
let j: Int = 0
|
|
||||||
while j < np {
|
|
||||||
let pj: Int = native_list_get(partials, j)
|
|
||||||
let sumP: Int = sumP + pj
|
|
||||||
let j: Int = j + 1
|
|
||||||
}
|
|
||||||
if sumP <= 0 { let sumP: Int = 1000000 }
|
|
||||||
let adsr: [String] = str_split(sig_field(lines, "adsr"), ",")
|
|
||||||
let atk_ms: Int = parse_micro(native_list_get(adsr, 0)) / 1000
|
|
||||||
let dec_ms: Int = parse_micro(native_list_get(adsr, 1)) / 1000
|
|
||||||
let sus_pm: Int = parse_micro(native_list_get(adsr, 2)) / 1000
|
|
||||||
let rel_ms: Int = parse_micro(native_list_get(adsr, 3)) / 1000
|
|
||||||
let b_micro: Int = parse_micro(sig_field(lines, "inharmonicity_B"))
|
|
||||||
let vib_rate: Int = str_to_int_el(sig_field(lines, "vibrato_rate_hz"))
|
|
||||||
let vib_cents: Int = str_to_int_el(sig_field(lines, "vibrato_depth_cents"))
|
|
||||||
return note_samples(freq_mHz, dur_ms, rate, partials, sumP, b_micro, vib_rate, vib_cents, atk_ms, dec_ms, sus_pm, rel_ms, amp_pm, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- byte-buffer helpers (own-core, no library) --------------------------------
|
|
||||||
|
|
||||||
fn put_tag(buf: String, pos: Int, s: String) -> String {
|
|
||||||
let n: Int = str_len(s)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let buf: String = __str_set_char(buf, pos + i, str_char_code(s, i))
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn put_u32le(buf: String, pos: Int, v: Int) -> String {
|
|
||||||
let buf: String = __str_set_char(buf, pos, v % 256)
|
|
||||||
let buf: String = __str_set_char(buf, pos + 1, (v / 256) % 256)
|
|
||||||
let buf: String = __str_set_char(buf, pos + 2, (v / 65536) % 256)
|
|
||||||
let buf: String = __str_set_char(buf, pos + 3, (v / 16777216) % 256)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn put_u16le(buf: String, pos: Int, v: Int) -> String {
|
|
||||||
let buf: String = __str_set_char(buf, pos, v % 256)
|
|
||||||
let buf: String = __str_set_char(buf, pos + 1, (v / 256) % 256)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- WAV serializer: own-core RIFF/WAVE, PCM mono 16-bit -----------------------
|
|
||||||
|
|
||||||
fn wav_write(path: String, samples: [Int], n: Int, rate: Int) -> Int {
|
|
||||||
let data_len: Int = n * 2
|
|
||||||
let total: Int = 44 + data_len
|
|
||||||
let buf: String = __str_alloc(total)
|
|
||||||
let buf: String = put_tag(buf, 0, "RIFF")
|
|
||||||
let buf: String = put_u32le(buf, 4, 36 + data_len)
|
|
||||||
let buf: String = put_tag(buf, 8, "WAVE")
|
|
||||||
let buf: String = put_tag(buf, 12, "fmt ")
|
|
||||||
let buf: String = put_u32le(buf, 16, 16)
|
|
||||||
let buf: String = put_u16le(buf, 20, 1)
|
|
||||||
let buf: String = put_u16le(buf, 22, 1)
|
|
||||||
let buf: String = put_u32le(buf, 24, rate)
|
|
||||||
let buf: String = put_u32le(buf, 28, rate * 2)
|
|
||||||
let buf: String = put_u16le(buf, 32, 2)
|
|
||||||
let buf: String = put_u16le(buf, 34, 16)
|
|
||||||
let buf: String = put_tag(buf, 36, "data")
|
|
||||||
let buf: String = put_u32le(buf, 40, data_len)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let v: Int = native_list_get(samples, i)
|
|
||||||
if v < 0 { let v: Int = v + 65536 }
|
|
||||||
let buf: String = __str_set_char(buf, 44 + i * 2, v % 256)
|
|
||||||
let buf: String = __str_set_char(buf, 44 + i * 2 + 1, (v / 256) % 256)
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
let ok: Int = fs_write_bytes(path, buf, total)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- plan: frame slot-map -> note atom (pitch, duration, amplitude) ------------
|
|
||||||
|
|
||||||
fn audio_frame(relation: String, polarity: String, confidence: String, importance: String, salience: String, subj_id: String) -> [String] {
|
|
||||||
let f: [String] = native_list_empty()
|
|
||||||
let f: [String] = native_list_append(f, "relation")
|
|
||||||
let f: [String] = native_list_append(f, relation)
|
|
||||||
let f: [String] = native_list_append(f, "polarity")
|
|
||||||
let f: [String] = native_list_append(f, polarity)
|
|
||||||
let f: [String] = native_list_append(f, "confidence")
|
|
||||||
let f: [String] = native_list_append(f, confidence)
|
|
||||||
let f: [String] = native_list_append(f, "importance")
|
|
||||||
let f: [String] = native_list_append(f, importance)
|
|
||||||
let f: [String] = native_list_append(f, "salience")
|
|
||||||
let f: [String] = native_list_append(f, salience)
|
|
||||||
let f: [String] = native_list_append(f, "subj_id")
|
|
||||||
let f: [String] = native_list_append(f, subj_id)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
fn degree_offset(deg: Int) -> Int {
|
|
||||||
if deg == 0 { return 0 }
|
|
||||||
if deg == 1 { return 2 }
|
|
||||||
if deg == 2 { return 4 }
|
|
||||||
if deg == 3 { return 5 }
|
|
||||||
if deg == 4 { return 7 }
|
|
||||||
if deg == 5 { return 9 }
|
|
||||||
return 11
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns [midi, dur_ms, amp_pm]
|
|
||||||
fn plan_note(frame: [String]) -> [Int] {
|
|
||||||
let relation: String = surface_get(frame, "relation")
|
|
||||||
let polarity: String = surface_get(frame, "polarity")
|
|
||||||
let confidence: String = surface_get(frame, "confidence")
|
|
||||||
let importance: String = surface_get(frame, "importance")
|
|
||||||
let salience: String = surface_get(frame, "salience")
|
|
||||||
let rn: Int = str_len(relation)
|
|
||||||
let csum: Int = 0
|
|
||||||
let i: Int = 0
|
|
||||||
while i < rn {
|
|
||||||
let cc: Int = str_char_code(relation, i)
|
|
||||||
let csum: Int = csum + cc
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
let deg: Int = csum % 7
|
|
||||||
let third: Int = 4
|
|
||||||
if str_eq(polarity, "neg") { let third: Int = 3 }
|
|
||||||
let sal_oct: Int = str_to_int_el(salience)
|
|
||||||
let doff: Int = degree_offset(deg)
|
|
||||||
let midi: Int = 60 + sal_oct * 12 + doff + third
|
|
||||||
let conf_micro: Int = parse_micro(confidence)
|
|
||||||
let dur_ms: Int = 200 + conf_micro / 1000
|
|
||||||
let imp_micro: Int = parse_micro(importance)
|
|
||||||
let amp_pm: Int = 400 + imp_micro / 2000
|
|
||||||
let out: [Int] = native_list_empty()
|
|
||||||
let out: [Int] = native_list_append(out, midi)
|
|
||||||
let out: [Int] = native_list_append(out, dur_ms)
|
|
||||||
let out: [Int] = native_list_append(out, amp_pm)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
fn realize_audio(frames: [[String]], sig_lines: [String], path: String, rate: Int, table: [Int]) -> Int {
|
|
||||||
let nf: Int = native_list_len(frames)
|
|
||||||
let all: [Int] = native_list_empty()
|
|
||||||
let count: Int = 0
|
|
||||||
let fi: Int = 0
|
|
||||||
while fi < nf {
|
|
||||||
let frame: [String] = native_list_get(frames, fi)
|
|
||||||
let plan: [Int] = plan_note(frame)
|
|
||||||
let midi: Int = native_list_get(plan, 0)
|
|
||||||
let dur_ms: Int = native_list_get(plan, 1)
|
|
||||||
let amp_pm: Int = native_list_get(plan, 2)
|
|
||||||
let freq: Int = freq_of_midi(midi)
|
|
||||||
let note: [Int] = synth_from_sig(sig_lines, freq, dur_ms, amp_pm, rate, table)
|
|
||||||
let nn: Int = native_list_len(note)
|
|
||||||
let j: Int = 0
|
|
||||||
while j < nn {
|
|
||||||
let all: [Int] = native_list_append(all, native_list_get(note, j))
|
|
||||||
let j: Int = j + 1
|
|
||||||
}
|
|
||||||
let count: Int = count + nn
|
|
||||||
let fi: Int = fi + 1
|
|
||||||
}
|
|
||||||
let ok: Int = wav_write(path, all, count, rate)
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
|||||||
// comprehend.elh — public surface of the ELP comprehension front-end.
|
|
||||||
// text → meaning-spec (the input half of the ELP; inverse of the realizer).
|
|
||||||
extern fn parse_spec(text: String) -> [String]
|
|
||||||
extern fn parse_spec_lang(text: String, lang: String) -> [String]
|
|
||||||
extern fn parse_json(text: String) -> String
|
|
||||||
extern fn parse_json_lang(text: String, lang: String) -> String
|
|
||||||
// Analysis primitives (invertible morphology + deterministic grammar helpers):
|
|
||||||
extern fn cp_tokenize(text: String) -> [String]
|
|
||||||
extern fn cp_pron_concept(w: String) -> String
|
|
||||||
extern fn cp_is_negation(w: String) -> Bool
|
|
||||||
extern fn cp_is_neg_adverb(w: String) -> Bool
|
|
||||||
extern fn cp_irr2(surface: String) -> [String]
|
|
||||||
extern fn cp_reg_verb(w: String) -> [String]
|
|
||||||
extern fn cp_analyze_verb(surface: String) -> [String]
|
|
||||||
extern fn cp_verb_start(toks: [String], end: Int) -> Int
|
|
||||||
extern fn cp_subord_start(toks: [String], n: Int) -> Int
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
// dialogue.el — SUMMON-THROUGH-SELF, native el. Port of dialogue.py's core.
|
|
||||||
//
|
|
||||||
// THE WHOLE DIALOGUE IS ONE OPERATION. A fact is never merely *fetched*: the
|
|
||||||
// query is PROJECTED into the engram's self + memory geometry, LANDS in a region,
|
|
||||||
// and the reply is READ OUT / the region MATERIALIZED from wherever it landed.
|
|
||||||
//
|
|
||||||
// project(query) -> land on a region -> read out from that region
|
|
||||||
//
|
|
||||||
// • lands in the SELF region -> grounded identity/presence, read out of
|
|
||||||
// the real self nodes (self_region.el)
|
|
||||||
// • lands on a memory NEIGHBORHOOD -> MATERIALIZE it: walk the neighborhood
|
|
||||||
// (engram_neighbors_json) and read out the
|
|
||||||
// region's connected members
|
|
||||||
// • lands nowhere close -> HONEST ABSENCE (an empty region, not a
|
|
||||||
// fabricated answer, not an error)
|
|
||||||
//
|
|
||||||
// CRITICAL INVARIANTS (enforced structurally, not by convention):
|
|
||||||
// * ONE operation — there is NO intent classifier and NO separate
|
|
||||||
// fact-retrieval branch. Identity is nearest-region proximity, not a switch.
|
|
||||||
// * MATERIALIZE by walking the neighborhood, never by fetching top-props.
|
|
||||||
// * HONEST ABSENCE when the region is thin.
|
|
||||||
// * NEGATION is SACRED: the readout is the stored prose VERBATIM, so a negated
|
|
||||||
// memory stays negated — we never paraphrase a polarity away.
|
|
||||||
// * NO ECHO: the old "I noted that X. That relates to Y." template is gone.
|
|
||||||
// The summon path materializes or honestly declines — it never echoes.
|
|
||||||
// * DIRECTIVE OVERRIDE: a meta-directive ("answer in English") overrides the
|
|
||||||
// reply language while the content language is still auto-detected.
|
|
||||||
//
|
|
||||||
// Depends on: comprehend (parse_spec_lang, cp_tokenize), multilingual (ml_detect,
|
|
||||||
// ml_tr, ml_term), propositions (prop_split_sentences), self_region
|
|
||||||
// (sr_available, sr_readout), the engram + json runtime builtins.
|
|
||||||
|
|
||||||
// ── directive override ────────────────────────────────────────────────────────
|
|
||||||
// Return [target_lang, content]. target_lang is "" when no directive is present.
|
|
||||||
// A directive names an output language; we strip it and keep the remaining text
|
|
||||||
// as the content (whose OWN language is still auto-detected downstream).
|
|
||||||
|
|
||||||
fn dlg_dir_hit(low: String, phrase: String) -> Bool {
|
|
||||||
return str_contains(low, phrase)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dlg_parse_directive(text: String) -> [String] {
|
|
||||||
let low: String = str_to_lower(text)
|
|
||||||
let lang: String = ""
|
|
||||||
let phrase: String = ""
|
|
||||||
// English target
|
|
||||||
if dlg_dir_hit(low, "in english") { let lang = "en"; let phrase = "in english" }
|
|
||||||
if dlg_dir_hit(low, "em inglês") { let lang = "en"; let phrase = "em inglês" }
|
|
||||||
if dlg_dir_hit(low, "em ingles") { let lang = "en"; let phrase = "em ingles" }
|
|
||||||
if dlg_dir_hit(low, "en inglés") { let lang = "en"; let phrase = "en inglés" }
|
|
||||||
// Portuguese target
|
|
||||||
if dlg_dir_hit(low, "in portuguese") { let lang = "pt"; let phrase = "in portuguese" }
|
|
||||||
if dlg_dir_hit(low, "em português") { let lang = "pt"; let phrase = "em português" }
|
|
||||||
// Spanish target
|
|
||||||
if dlg_dir_hit(low, "in spanish") { let lang = "es"; let phrase = "in spanish" }
|
|
||||||
if dlg_dir_hit(low, "en español") { let lang = "es"; let phrase = "en español" }
|
|
||||||
// Italian target
|
|
||||||
if dlg_dir_hit(low, "in italian") { let lang = "it"; let phrase = "in italian" }
|
|
||||||
|
|
||||||
let content: String = text
|
|
||||||
if !str_eq(phrase, "") {
|
|
||||||
// strip the directive phrase (and a common "answer"/"responda" lead-in),
|
|
||||||
// leaving the real question as content.
|
|
||||||
let idx: Int = str_index_of(low, phrase)
|
|
||||||
if idx >= 0 {
|
|
||||||
let before: String = str_slice(text, 0, idx)
|
|
||||||
let after: String = str_slice(text, idx + str_len(phrase), str_len(text))
|
|
||||||
let content = str_trim(before + " " + after)
|
|
||||||
}
|
|
||||||
// trim a leading "answer"/"responda"/"reply" and stray colon/comma.
|
|
||||||
let cl: String = str_to_lower(content)
|
|
||||||
if str_starts_with(cl, "answer") { let content = str_trim(str_slice(content, 6, str_len(content))) }
|
|
||||||
if str_starts_with(cl, "responda") { let content = str_trim(str_slice(content, 8, str_len(content))) }
|
|
||||||
if str_starts_with(cl, "reply") { let content = str_trim(str_slice(content, 5, str_len(content))) }
|
|
||||||
if str_starts_with(content, ":") { let content = str_trim(str_slice(content, 1, str_len(content))) }
|
|
||||||
if str_starts_with(content, ",") { let content = str_trim(str_slice(content, 1, str_len(content))) }
|
|
||||||
}
|
|
||||||
let r: [String] = native_list_empty()
|
|
||||||
let r = native_list_append(r, lang)
|
|
||||||
let r = native_list_append(r, content)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── identity landing (a region proximity, not a classifier switch) ────────────
|
|
||||||
// The query lands in the SELF region when it takes an identity/presence shape.
|
|
||||||
// Cross-lingual forms are included because the engram's lexical probe is
|
|
||||||
// English-leaning. This is the SELF attractor of the single operation.
|
|
||||||
|
|
||||||
fn dlg_is_identity(content: String) -> Bool {
|
|
||||||
let low: String = str_to_lower(str_trim(content))
|
|
||||||
if str_contains(low, "who are you") { return true }
|
|
||||||
if str_contains(low, "what are you") { return true }
|
|
||||||
if str_contains(low, "who i am") { return true }
|
|
||||||
if str_contains(low, "your name") { return true }
|
|
||||||
if str_contains(low, "about yourself") { return true }
|
|
||||||
if str_contains(low, "are you conscious") { return true }
|
|
||||||
if str_contains(low, "are you there") { return true }
|
|
||||||
// cross-lingual identity question-forms
|
|
||||||
if str_contains(low, "quem é você") { return true }
|
|
||||||
if str_contains(low, "quem es voce") { return true }
|
|
||||||
if str_contains(low, "quién eres") { return true }
|
|
||||||
if str_contains(low, "quien eres") { return true }
|
|
||||||
if str_contains(low, "chi sei") { return true }
|
|
||||||
if str_contains(low, "qui es-tu") { return true }
|
|
||||||
if str_contains(low, "wer bist du") { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── readout helpers ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
fn dlg_first_sentence(content: String) -> String {
|
|
||||||
let sents: [String] = prop_split_sentences(content)
|
|
||||||
let n: Int = native_list_len(sents)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let s: String = str_trim(native_list_get(sents, i))
|
|
||||||
// drop a leading markdown heading marker for a clean read-out line
|
|
||||||
if str_starts_with(s, "# ") { let s = str_trim(str_slice(s, 2, str_len(s))) }
|
|
||||||
if str_len(s) > 0 { return s }
|
|
||||||
let i = i + 1
|
|
||||||
}
|
|
||||||
return str_trim(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip trailing/leading punctuation from a token.
|
|
||||||
fn dlg_clean_tok(w: String) -> String {
|
|
||||||
let s: String = str_trim(w)
|
|
||||||
let s = str_strip_suffix(s, ".")
|
|
||||||
let s = str_strip_suffix(s, ",")
|
|
||||||
let s = str_strip_suffix(s, "?")
|
|
||||||
let s = str_strip_suffix(s, "!")
|
|
||||||
let s = str_strip_suffix(s, ":")
|
|
||||||
let s = str_strip_suffix(s, ";")
|
|
||||||
return str_trim(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// closed-class across the supported languages (union) — a word we must NOT treat
|
|
||||||
// as a retrieval topic. Also drops the meta verbs of a request ("tell", "prove",
|
|
||||||
// "show") so the TOPIC, not the speech act, is what projects into memory.
|
|
||||||
fn dlg_is_stop(w: String) -> Bool {
|
|
||||||
if ml_stop_en(w) { return true }
|
|
||||||
if ml_stop_es(w) { return true }
|
|
||||||
if ml_stop_pt(w) { return true }
|
|
||||||
if ml_stop_it(w) { return true }
|
|
||||||
if str_eq(w, "tell") { return true }
|
|
||||||
if str_eq(w, "show") { return true }
|
|
||||||
if str_eq(w, "about") { return true }
|
|
||||||
if str_eq(w, "sobre") { return true }
|
|
||||||
if str_eq(w, "acerca") { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// The CONTENT TERMS the query projects into memory: content words only, cleaned,
|
|
||||||
// cross-lingually mapped to the engram's English vocabulary, ≥3 chars. This is
|
|
||||||
// the geometry probe — the speech-act verbs and function words are stripped so a
|
|
||||||
// PP topic ("tell me ABOUT Lisbon") projects on "lisbon", not "tell"/"me".
|
|
||||||
fn dlg_content_terms(content: String, lang: String) -> [String] {
|
|
||||||
let toks: [String] = cp_tokenize(content)
|
|
||||||
let n: Int = native_list_len(toks)
|
|
||||||
let out: [String] = native_list_empty()
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let w: String = str_to_lower(dlg_clean_tok(native_list_get(toks, i)))
|
|
||||||
if str_len(w) >= 3 {
|
|
||||||
if !dlg_is_stop(w) {
|
|
||||||
let out = native_list_append(out, ml_term(w, lang))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let i = i + 1
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does this landed node lexically overlap the query's content terms? This is the
|
|
||||||
// RELEVANCE FLOOR: activation always returns the store's most salient nodes, so
|
|
||||||
// without this a query about nothing would "land" on the self/top node. A node
|
|
||||||
// that shares no content term with the query is "nowhere close" -> honest absence.
|
|
||||||
fn dlg_node_matches(node: String, terms: [String]) -> Bool {
|
|
||||||
let hay: String = str_to_lower(json_get_string(node, "content") + " " + json_get_string(node, "label"))
|
|
||||||
let n: Int = native_list_len(terms)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let t: String = native_list_get(terms, i)
|
|
||||||
if str_len(t) >= 3 {
|
|
||||||
if str_contains(hay, t) { return true }
|
|
||||||
}
|
|
||||||
let i = i + 1
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// MATERIALIZE the landed region: read out the landed fact, then WALK the
|
|
||||||
// neighborhood and read out its connected members (real edges, not top-props).
|
|
||||||
fn dlg_materialize(top_node: String, reply_lang: String) -> String {
|
|
||||||
let id: String = json_get_string(top_node, "id")
|
|
||||||
let content: String = json_get_string(top_node, "content")
|
|
||||||
let lead: String = dlg_first_sentence(content)
|
|
||||||
|
|
||||||
let nb: String = engram_neighbors_json(id, 2, "both")
|
|
||||||
let m: Int = json_array_len(nb)
|
|
||||||
let parts: [String] = native_list_empty()
|
|
||||||
let parts = native_list_append(parts, lead)
|
|
||||||
let added: Int = 0
|
|
||||||
let i: Int = 0
|
|
||||||
while i < m {
|
|
||||||
if added < 3 {
|
|
||||||
let rec: String = json_array_get(nb, i)
|
|
||||||
let node: String = json_get_raw(rec, "node")
|
|
||||||
let nc: String = json_get_string(node, "content")
|
|
||||||
if !str_eq(nc, "") {
|
|
||||||
let sent: String = dlg_first_sentence(nc)
|
|
||||||
if !str_eq(sent, "") {
|
|
||||||
let parts = native_list_append(parts, sent)
|
|
||||||
let added = added + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let i = i + 1
|
|
||||||
}
|
|
||||||
// The readout is the region's OWN prose, verbatim — negation SACRED, no echo.
|
|
||||||
return str_join(parts, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── THE single operation ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
fn dlg_respond(text: String) -> String {
|
|
||||||
// directive override: reply language may differ from content language.
|
|
||||||
let dir: [String] = dlg_parse_directive(text)
|
|
||||||
let target_lang: String = native_list_get(dir, 0)
|
|
||||||
let content: String = native_list_get(dir, 1)
|
|
||||||
|
|
||||||
let content_lang: String = ml_detect(content)
|
|
||||||
let reply_lang: String = content_lang
|
|
||||||
if !str_eq(target_lang, "") { let reply_lang = target_lang }
|
|
||||||
|
|
||||||
// comprehend the content (SACRED polarity carried in the spec).
|
|
||||||
let spec: [String] = parse_spec_lang(content, content_lang)
|
|
||||||
|
|
||||||
// ── PROJECT + LAND: SELF region ───────────────────────────────────────────
|
|
||||||
// Identity/presence shape lands in the self region; read out the REAL self
|
|
||||||
// nodes (self_region.el), never a template. Same single operation — this is
|
|
||||||
// just the self attractor winning the landing.
|
|
||||||
if dlg_is_identity(content) {
|
|
||||||
if sr_available() {
|
|
||||||
// read out the REAL self nodes when replying in their own language
|
|
||||||
// (the soul's prose is English); for another reply language we cannot
|
|
||||||
// translate real content without an LLM, so we answer with the
|
|
||||||
// localized SACRED identity anchor — honest, in-language, no fabrication.
|
|
||||||
if str_eq(reply_lang, "en") { return sr_readout("en") }
|
|
||||||
return ml_tr("identity", reply_lang)
|
|
||||||
}
|
|
||||||
// self region thin — honest localized identity (logged fallback shape).
|
|
||||||
return ml_tr("identity", reply_lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── PROJECT into MEMORY geometry ──────────────────────────────────────────
|
|
||||||
let terms: [String] = dlg_content_terms(content, content_lang)
|
|
||||||
let qterm: String = str_join(terms, " ")
|
|
||||||
let act: String = engram_activate_json(qterm, 12)
|
|
||||||
let n: Int = json_array_len(act)
|
|
||||||
|
|
||||||
// ── LAND: the highest-activation node that ACTUALLY overlaps the query's
|
|
||||||
// content terms (the relevance floor). Activation always returns the most
|
|
||||||
// salient nodes, so we walk the ranked list and take the first that is
|
|
||||||
// genuinely "close"; if none is, the query landed nowhere. ───────────────
|
|
||||||
let landing: String = ""
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
if str_eq(landing, "") {
|
|
||||||
let rec: String = json_array_get(act, i)
|
|
||||||
let node: String = json_get_raw(rec, "node")
|
|
||||||
if dlg_node_matches(node, terms) {
|
|
||||||
let landing = node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let i = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── HONEST ABSENCE: nothing close — an empty region, not a fabricated answer,
|
|
||||||
// not an "I noted that" echo. ────────────────────────────────────────────
|
|
||||||
if str_eq(landing, "") {
|
|
||||||
return ml_tr("no_memory", reply_lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── MATERIALIZE the landing by WALKING its neighborhood. ──────────────────
|
|
||||||
return dlg_materialize(landing, reply_lang)
|
|
||||||
}
|
|
||||||
@@ -63,9 +63,6 @@ import "morphology-cop.el"
|
|||||||
import "grammar.el"
|
import "grammar.el"
|
||||||
import "realizer.el"
|
import "realizer.el"
|
||||||
import "semantics.el"
|
import "semantics.el"
|
||||||
|
|
||||||
// ── Comprehension front-end (input half: text → meaning-spec) ─────────────────
|
|
||||||
import "comprehend.el"
|
|
||||||
//
|
//
|
||||||
// Entry points:
|
// Entry points:
|
||||||
//
|
//
|
||||||
@@ -120,9 +117,6 @@ fn build_form_from_json(semantic_form_json: String, lang_code: String) -> [Strin
|
|||||||
let location: String = sem_get(semantic_form_json, "location")
|
let location: String = sem_get(semantic_form_json, "location")
|
||||||
let tense: String = sem_get(semantic_form_json, "tense")
|
let tense: String = sem_get(semantic_form_json, "tense")
|
||||||
let aspect: String = sem_get(semantic_form_json, "aspect")
|
let aspect: String = sem_get(semantic_form_json, "aspect")
|
||||||
let polarity: String = sem_get(semantic_form_json, "polarity")
|
|
||||||
let neg_word: String = sem_get(semantic_form_json, "neg_word")
|
|
||||||
let iobj: String = sem_get(semantic_form_json, "iobj")
|
|
||||||
|
|
||||||
let form: [String] = native_list_empty()
|
let form: [String] = native_list_empty()
|
||||||
let form = native_list_append(form, "intent")
|
let form = native_list_append(form, "intent")
|
||||||
@@ -133,19 +127,12 @@ fn build_form_from_json(semantic_form_json: String, lang_code: String) -> [Strin
|
|||||||
let form = native_list_append(form, predicate)
|
let form = native_list_append(form, predicate)
|
||||||
let form = native_list_append(form, "patient")
|
let form = native_list_append(form, "patient")
|
||||||
let form = native_list_append(form, patient)
|
let form = native_list_append(form, patient)
|
||||||
let form = native_list_append(form, "iobj")
|
|
||||||
let form = native_list_append(form, iobj)
|
|
||||||
let form = native_list_append(form, "location")
|
let form = native_list_append(form, "location")
|
||||||
let form = native_list_append(form, location)
|
let form = native_list_append(form, location)
|
||||||
let form = native_list_append(form, "tense")
|
let form = native_list_append(form, "tense")
|
||||||
let form = native_list_append(form, tense)
|
let form = native_list_append(form, tense)
|
||||||
let form = native_list_append(form, "aspect")
|
let form = native_list_append(form, "aspect")
|
||||||
let form = native_list_append(form, aspect)
|
let form = native_list_append(form, aspect)
|
||||||
// SACRED: polarity crosses the JSON boundary and is never inferred away.
|
|
||||||
let form = native_list_append(form, "polarity")
|
|
||||||
let form = native_list_append(form, polarity)
|
|
||||||
let form = native_list_append(form, "neg_word")
|
|
||||||
let form = native_list_append(form, neg_word)
|
|
||||||
let form = native_list_append(form, "lang")
|
let form = native_list_append(form, "lang")
|
||||||
let form = native_list_append(form, lang_code)
|
let form = native_list_append(form, lang_code)
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn sem_get(json: String, key: String) -> String
|
extern fn sem_get(json: String, key: String) -> String
|
||||||
extern fn generate_frame(frame: [String]) -> String
|
extern fn generate_frame(frame: Any) -> String
|
||||||
extern fn generate_frame_lang(frame: [String], lang_code: String) -> String
|
extern fn generate_frame_lang(frame: Any, lang_code: String) -> String
|
||||||
extern fn build_form_from_json(semantic_form_json: String, lang_code: String) -> [String]
|
extern fn build_form_from_json(semantic_form_json: String, lang_code: String) -> Any
|
||||||
extern fn generate(semantic_form_json: String) -> String
|
extern fn generate(semantic_form_json: String) -> String
|
||||||
extern fn generate_lang(semantic_form_json: String, lang_code: String) -> String
|
extern fn generate_lang(semantic_form_json: String, lang_code: String) -> String
|
||||||
|
|||||||
+28
-28
@@ -1,22 +1,22 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn slots_get(slots: [String], key: String) -> String
|
extern fn slots_get(slots: Any, key: String) -> String
|
||||||
extern fn slots_set(slots: [String], key: String, val: String) -> [String]
|
extern fn slots_set(slots: Any, key: String, val: String) -> Any
|
||||||
extern fn make_slots(k0: String, v0: String) -> [String]
|
extern fn make_slots(k0: String, v0: String) -> Any
|
||||||
extern fn make_slots2(k0: String, v0: String, k1: String, v1: String) -> [String]
|
extern fn make_slots2(k0: String, v0: String, k1: String, v1: String) -> Any
|
||||||
extern fn make_slots3(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String) -> [String]
|
extern fn make_slots3(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String) -> Any
|
||||||
extern fn make_slots4(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String) -> [String]
|
extern fn make_slots4(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String) -> Any
|
||||||
extern fn make_slots5(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String, k4: String, v4: String) -> [String]
|
extern fn make_slots5(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String, k4: String, v4: String) -> Any
|
||||||
extern fn rule_id(rule: [String]) -> String
|
extern fn rule_id(rule: Any) -> String
|
||||||
extern fn rule_lhs(rule: [String]) -> String
|
extern fn rule_lhs(rule: Any) -> String
|
||||||
extern fn rule_rhs_len(rule: [String]) -> Int
|
extern fn rule_rhs_len(rule: Any) -> Int
|
||||||
extern fn rule_rhs(rule: [String], idx: Int) -> String
|
extern fn rule_rhs(rule: Any, idx: Int) -> String
|
||||||
extern fn make_rule(id: String, lhs: String, r0: String) -> [String]
|
extern fn make_rule(id: String, lhs: String, r0: String) -> Any
|
||||||
extern fn make_rule2(id: String, lhs: String, r0: String, r1: String) -> [String]
|
extern fn make_rule2(id: String, lhs: String, r0: String, r1: String) -> Any
|
||||||
extern fn make_rule3(id: String, lhs: String, r0: String, r1: String, r2: String) -> [String]
|
extern fn make_rule3(id: String, lhs: String, r0: String, r1: String, r2: String) -> Any
|
||||||
extern fn make_rule4(id: String, lhs: String, r0: String, r1: String, r2: String, r3: String) -> [String]
|
extern fn make_rule4(id: String, lhs: String, r0: String, r1: String, r2: String, r3: String) -> Any
|
||||||
extern fn build_rules() -> [[String]]
|
extern fn build_rules() -> Any
|
||||||
extern fn get_rules() -> [[String]]
|
extern fn get_rules() -> Any
|
||||||
extern fn find_rule(rule_id_str: String) -> [String]
|
extern fn find_rule(rule_id_str: String) -> Any
|
||||||
extern fn make_leaf(label: String, word: String) -> String
|
extern fn make_leaf(label: String, word: String) -> String
|
||||||
extern fn make_node1(label: String, child0: String) -> String
|
extern fn make_node1(label: String, child0: String) -> String
|
||||||
extern fn make_node2(label: String, child0: String, child1: String) -> String
|
extern fn make_node2(label: String, child0: String, child1: String) -> String
|
||||||
@@ -24,15 +24,15 @@ extern fn make_node3(label: String, child0: String, child1: String, child2: Stri
|
|||||||
extern fn make_node4(label: String, child0: String, child1: String, child2: String, child3: String) -> String
|
extern fn make_node4(label: String, child0: String, child1: String, child2: String, child3: String) -> String
|
||||||
extern fn nlg_is_ws(c: String) -> Bool
|
extern fn nlg_is_ws(c: String) -> Bool
|
||||||
extern fn skip_ws(s: String, pos: Int) -> Int
|
extern fn skip_ws(s: String, pos: Int) -> Int
|
||||||
extern fn scan_token(s: String, start: Int) -> [String]
|
extern fn scan_token(s: String, start: Int) -> Any
|
||||||
extern fn render_tree(tree: String) -> String
|
extern fn render_tree(tree: String) -> String
|
||||||
extern fn gram_word_order(profile: [String]) -> String
|
extern fn gram_word_order(profile: Any) -> String
|
||||||
extern fn gram_order_constituents(subj: String, verb: String, obj: String, profile: [String]) -> String
|
extern fn gram_order_constituents(subj: String, verb: String, obj: String, profile: Any) -> String
|
||||||
extern fn gram_build_vp(verb: String, aux: String, profile: [String]) -> String
|
extern fn gram_build_vp(verb: String, aux: String, profile: Any) -> String
|
||||||
extern fn gram_question_strategy(profile: [String]) -> String
|
extern fn gram_question_strategy(profile: Any) -> String
|
||||||
extern fn is_pronoun(word: String) -> Bool
|
extern fn is_pronoun(word: String) -> Bool
|
||||||
extern fn build_np(referent: String, slots: [String]) -> String
|
extern fn build_np(referent: String, slots: Any) -> String
|
||||||
extern fn build_pp(loc: String) -> String
|
extern fn build_pp(loc: String) -> String
|
||||||
extern fn build_vp_body(slots: [String]) -> String
|
extern fn build_vp_body(slots: Any) -> String
|
||||||
extern fn build_vp_from_slots(slots: [String]) -> String
|
extern fn build_vp_from_slots(slots: Any) -> String
|
||||||
extern fn generate_tree(rule_id_str: String, slots: [String]) -> String
|
extern fn generate_tree(rule_id_str: String, slots: Any) -> String
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
// image-demo.el - Drive the native PNG surface: plan a scene from a small
|
|
||||||
// meaning phrase (incl. a NEG frame) and emit a byte-valid 64x64 PNG whose
|
|
||||||
// palette is read from elp/faculty/sig/scene.basis.
|
|
||||||
|
|
||||||
fn img_frame(relation: String, polarity: String, confidence: String, importance: String, salience: String, subj_id: String) -> [String] {
|
|
||||||
let f: [String] = native_list_empty()
|
|
||||||
let f: [String] = native_list_append(f, "relation")
|
|
||||||
let f: [String] = native_list_append(f, relation)
|
|
||||||
let f: [String] = native_list_append(f, "polarity")
|
|
||||||
let f: [String] = native_list_append(f, polarity)
|
|
||||||
let f: [String] = native_list_append(f, "confidence")
|
|
||||||
let f: [String] = native_list_append(f, confidence)
|
|
||||||
let f: [String] = native_list_append(f, "importance")
|
|
||||||
let f: [String] = native_list_append(f, importance)
|
|
||||||
let f: [String] = native_list_append(f, "salience")
|
|
||||||
let f: [String] = native_list_append(f, salience)
|
|
||||||
let f: [String] = native_list_append(f, "subj_id")
|
|
||||||
let f: [String] = native_list_append(f, subj_id)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rgb_str(c: [Int]) -> String {
|
|
||||||
return int_to_str(native_list_get(c, 0)) + "," + int_to_str(native_list_get(c, 1)) + "," + int_to_str(native_list_get(c, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_image() -> Int {
|
|
||||||
fs_mkdir("/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out")
|
|
||||||
let table: [Int] = crc_table()
|
|
||||||
println("crc_table[1]=" + int_to_str(native_list_get(table, 1)) + " (expect 1996959894 / 0x77073096)")
|
|
||||||
|
|
||||||
let basis: [String] = basis_load("/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/sig/scene.basis")
|
|
||||||
let warm: [Int] = parse_rgb(basis_field(basis, "warm"))
|
|
||||||
let cool: [Int] = parse_rgb(basis_field(basis, "cool"))
|
|
||||||
let bg: [Int] = parse_rgb(basis_field(basis, "bg"))
|
|
||||||
println("basis warm=" + rgb_str(warm) + " cool=" + rgb_str(cool) + " bg=" + rgb_str(bg) + " (read from scene.basis)")
|
|
||||||
|
|
||||||
let frames: [[String]] = native_list_empty()
|
|
||||||
let frames: [[String]] = native_list_append(frames, img_frame("agent", "aff", "0.9", "0.8", "0", "s1"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, img_frame("theme", "aff", "0.7", "0.6", "1", "s2"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, img_frame("cause", "aff", "0.8", "0.9", "0", "s3"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, img_frame("negation", "neg", "0.85", "0.7", "1", "s4"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, img_frame("goal", "aff", "0.6", "0.5", "0", "s5"))
|
|
||||||
let frames: [[String]] = native_list_append(frames, img_frame("result", "aff", "0.95", "1.0", "1", "s6"))
|
|
||||||
|
|
||||||
let shapes: [[Int]] = plan_scene(frames, warm, cool)
|
|
||||||
let ns: Int = native_list_len(shapes)
|
|
||||||
println("planned " + int_to_str(ns) + " shapes:")
|
|
||||||
let si: Int = 0
|
|
||||||
while si < ns {
|
|
||||||
let sh: [Int] = native_list_get(shapes, si)
|
|
||||||
let pol: String = surface_get(native_list_get(frames, si), "polarity")
|
|
||||||
println(" shape " + int_to_str(si) + " type=" + int_to_str(native_list_get(sh, 0)) + " x=" + int_to_str(native_list_get(sh, 1)) + " y=" + int_to_str(native_list_get(sh, 2)) + " size=" + int_to_str(native_list_get(sh, 3)) + " rgb=" + int_to_str(native_list_get(sh, 4)) + "," + int_to_str(native_list_get(sh, 5)) + "," + int_to_str(native_list_get(sh, 6)) + " polarity=" + pol)
|
|
||||||
let si: Int = si + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let raw: [Int] = rasterize(64, 64, shapes, bg)
|
|
||||||
println("rasterized raw (filtered scanlines) bytes=" + int_to_str(native_list_len(raw)) + " (expect 12352)")
|
|
||||||
let png: [Int] = png_build(64, 64, raw, table)
|
|
||||||
let plen: Int = native_list_len(png)
|
|
||||||
let ok: Int = png_write("/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/scene.png", png)
|
|
||||||
println("PNG bytes=" + int_to_str(plen) + " -> /Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-aaf04b0a9714c4070/elp/faculty/out/scene.png (write_ok=" + int_to_str(ok) + ")")
|
|
||||||
return plen
|
|
||||||
}
|
|
||||||
|
|
||||||
println("image-demo returned png_bytes=" + int_to_str(run_image()))
|
|
||||||
@@ -1,412 +0,0 @@
|
|||||||
// image-surface.el - Native own-core raster PNG surface (the image efferent
|
|
||||||
// twin of audio). Renders a 64x64 RGB scene deterministically from a frame's
|
|
||||||
// meaning-geometry, then serialises a byte-valid PNG entirely own-core:
|
|
||||||
// 8-byte magic, IHDR, IDAT (zlib STORED/uncompressed DEFLATE + Adler32), IEND,
|
|
||||||
// with a per-chunk CRC32 computed via software xor32 (EL has no bitwise ops).
|
|
||||||
//
|
|
||||||
// The RGB palette basis is read from elp/faculty/sig/scene.basis (data, not
|
|
||||||
// literals) - the same read-from-learned discipline as the audio signatures.
|
|
||||||
// Integer-only throughout; pixels are composed functionally (painter's order)
|
|
||||||
// so no list mutation is needed.
|
|
||||||
|
|
||||||
// -- small int/parse helpers (self-contained) ----------------------------------
|
|
||||||
|
|
||||||
fn i_str_to_int(s: String) -> Int {
|
|
||||||
let n: Int = str_len(s)
|
|
||||||
let i: Int = 0
|
|
||||||
let v: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let c: Int = str_char_code(s, i)
|
|
||||||
if c >= 48 {
|
|
||||||
if c < 58 {
|
|
||||||
let v: Int = v * 10 + (c - 48)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
fn basis_load(path: String) -> [String] {
|
|
||||||
return str_split(fs_read(path), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn basis_field(lines: [String], key: String) -> String {
|
|
||||||
let pref: String = key + ": "
|
|
||||||
let n: Int = native_list_len(lines)
|
|
||||||
let plen: Int = str_len(pref)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let ln: String = native_list_get(lines, i)
|
|
||||||
if str_starts_with(ln, pref) {
|
|
||||||
return str_slice(ln, plen, str_len(ln))
|
|
||||||
}
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_rgb(csv: String) -> [Int] {
|
|
||||||
let parts: [String] = str_split(csv, ",")
|
|
||||||
let out: [Int] = native_list_empty()
|
|
||||||
let n: Int = native_list_len(parts)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let v: Int = i_str_to_int(native_list_get(parts, i))
|
|
||||||
let out: [Int] = native_list_append(out, v)
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- software 32-bit XOR (no bitwise ops in EL) --------------------------------
|
|
||||||
|
|
||||||
fn xor32(a: Int, b: Int) -> Int {
|
|
||||||
let r: Int = 0
|
|
||||||
let bit: Int = 1
|
|
||||||
let i: Int = 0
|
|
||||||
while i < 32 {
|
|
||||||
let abit: Int = (a / bit) % 2
|
|
||||||
let bbit: Int = (b / bit) % 2
|
|
||||||
if abit != bbit {
|
|
||||||
let add: Int = bit
|
|
||||||
let r: Int = r + add
|
|
||||||
}
|
|
||||||
let bit: Int = bit * 2
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- CRC32 (table-driven, table built with xor32) ------------------------------
|
|
||||||
|
|
||||||
fn crc_table() -> [Int] {
|
|
||||||
let t: [Int] = native_list_empty()
|
|
||||||
let n: Int = 0
|
|
||||||
while n < 256 {
|
|
||||||
let c: Int = n
|
|
||||||
let k: Int = 0
|
|
||||||
while k < 8 {
|
|
||||||
if c % 2 == 1 {
|
|
||||||
let h: Int = c / 2
|
|
||||||
let c: Int = xor32(h, 3988292384)
|
|
||||||
} else {
|
|
||||||
let c: Int = c / 2
|
|
||||||
}
|
|
||||||
let k: Int = k + 1
|
|
||||||
}
|
|
||||||
let t: [Int] = native_list_append(t, c)
|
|
||||||
let n: Int = n + 1
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
fn crc32_of(bytes: [Int], table: [Int]) -> Int {
|
|
||||||
let crc: Int = 4294967295
|
|
||||||
let n: Int = native_list_len(bytes)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let b: Int = native_list_get(bytes, i)
|
|
||||||
let lo: Int = crc % 256
|
|
||||||
let idx: Int = xor32(lo, b) % 256
|
|
||||||
let tv: Int = native_list_get(table, idx)
|
|
||||||
let hi: Int = crc / 256
|
|
||||||
let crc: Int = xor32(hi, tv)
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return xor32(crc, 4294967295)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Adler32 (for the zlib trailer) --------------------------------------------
|
|
||||||
|
|
||||||
fn adler32_of(bytes: [Int]) -> Int {
|
|
||||||
let a: Int = 1
|
|
||||||
let b: Int = 0
|
|
||||||
let n: Int = native_list_len(bytes)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let byte: Int = native_list_get(bytes, i)
|
|
||||||
let a: Int = (a + byte) % 65521
|
|
||||||
let b: Int = (b + a) % 65521
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return b * 65536 + a
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- byte-list append helpers --------------------------------------------------
|
|
||||||
|
|
||||||
fn app_u32be(dst: [Int], v: Int) -> [Int] {
|
|
||||||
let dst: [Int] = native_list_append(dst, (v / 16777216) % 256)
|
|
||||||
let dst: [Int] = native_list_append(dst, (v / 65536) % 256)
|
|
||||||
let dst: [Int] = native_list_append(dst, (v / 256) % 256)
|
|
||||||
let dst: [Int] = native_list_append(dst, v % 256)
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
fn app_tag(dst: [Int], s: String) -> [Int] {
|
|
||||||
let n: Int = str_len(s)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let dst: [Int] = native_list_append(dst, str_char_code(s, i))
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
fn app_all(dst: [Int], src: [Int]) -> [Int] {
|
|
||||||
let n: Int = native_list_len(src)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let dst: [Int] = native_list_append(dst, native_list_get(src, i))
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- plan: frame meaning-geometry -> shape atoms -------------------------------
|
|
||||||
// shape = [type, x, y, size, r, g, b] (type 0=rect 1=disc 2=triangle)
|
|
||||||
|
|
||||||
fn charsum(s: String) -> Int {
|
|
||||||
let n: Int = str_len(s)
|
|
||||||
let i: Int = 0
|
|
||||||
let acc: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let c: Int = str_char_code(s, i)
|
|
||||||
let acc: Int = acc + c
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
fn micro_of(s: String) -> Int {
|
|
||||||
let dot: Int = str_index_of(s, ".")
|
|
||||||
if dot < 0 { return i_str_to_int(s) * 1000000 }
|
|
||||||
let n: Int = str_len(s)
|
|
||||||
let fp: String = str_slice(s, dot + 1, n)
|
|
||||||
let ip: String = str_slice(s, 0, dot)
|
|
||||||
let iv: Int = i_str_to_int(ip)
|
|
||||||
let fv: Int = 0
|
|
||||||
let scale: Int = 100000
|
|
||||||
let fl: Int = str_len(fp)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < 6 {
|
|
||||||
let d: Int = 0
|
|
||||||
if i < fl { let d: Int = str_char_code(fp, i) - 48 }
|
|
||||||
let fv: Int = fv + d * scale
|
|
||||||
let scale: Int = scale / 10
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
return iv * 1000000 + fv
|
|
||||||
}
|
|
||||||
|
|
||||||
fn plan_scene(frames: [[String]], warm: [Int], cool: [Int]) -> [[Int]] {
|
|
||||||
let shapes: [[Int]] = native_list_empty()
|
|
||||||
let nf: Int = native_list_len(frames)
|
|
||||||
let fi: Int = 0
|
|
||||||
while fi < nf {
|
|
||||||
let fr: [String] = native_list_get(frames, fi)
|
|
||||||
let relation: String = surface_get(fr, "relation")
|
|
||||||
let polarity: String = surface_get(fr, "polarity")
|
|
||||||
let confidence: String = surface_get(fr, "confidence")
|
|
||||||
let importance: String = surface_get(fr, "importance")
|
|
||||||
let salience: String = surface_get(fr, "salience")
|
|
||||||
// relation -> shape type
|
|
||||||
let stype: Int = charsum(relation) % 3
|
|
||||||
// confidence -> size (8..22)
|
|
||||||
let cmi: Int = micro_of(confidence)
|
|
||||||
let size: Int = 8 + cmi / 71428
|
|
||||||
// salience -> y
|
|
||||||
let sal: Int = i_str_to_int(salience)
|
|
||||||
let y: Int = 6 + sal * 26
|
|
||||||
// subj_id/index -> x
|
|
||||||
let x: Int = 4 + (fi * 10) % 48
|
|
||||||
// polarity -> warm/cool base color
|
|
||||||
let br: Int = native_list_get(warm, 0)
|
|
||||||
let bg2: Int = native_list_get(warm, 1)
|
|
||||||
let bb: Int = native_list_get(warm, 2)
|
|
||||||
if str_eq(polarity, "neg") {
|
|
||||||
let br: Int = native_list_get(cool, 0)
|
|
||||||
let bg2: Int = native_list_get(cool, 1)
|
|
||||||
let bb: Int = native_list_get(cool, 2)
|
|
||||||
}
|
|
||||||
// importance -> brightness (500..1000 permille)
|
|
||||||
let imi: Int = micro_of(importance)
|
|
||||||
let bpm: Int = 500 + imi / 2000
|
|
||||||
let r: Int = br * bpm / 1000
|
|
||||||
let g: Int = bg2 * bpm / 1000
|
|
||||||
let b: Int = bb * bpm / 1000
|
|
||||||
let sh: [Int] = native_list_empty()
|
|
||||||
let sh: [Int] = native_list_append(sh, stype)
|
|
||||||
let sh: [Int] = native_list_append(sh, x)
|
|
||||||
let sh: [Int] = native_list_append(sh, y)
|
|
||||||
let sh: [Int] = native_list_append(sh, size)
|
|
||||||
let sh: [Int] = native_list_append(sh, r)
|
|
||||||
let sh: [Int] = native_list_append(sh, g)
|
|
||||||
let sh: [Int] = native_list_append(sh, b)
|
|
||||||
let shapes: [[Int]] = native_list_append(shapes, sh)
|
|
||||||
let fi: Int = fi + 1
|
|
||||||
}
|
|
||||||
return shapes
|
|
||||||
}
|
|
||||||
|
|
||||||
// covers: is (px,py) inside this shape?
|
|
||||||
fn covers(sh: [Int], px: Int, py: Int) -> Bool {
|
|
||||||
let stype: Int = native_list_get(sh, 0)
|
|
||||||
let sx: Int = native_list_get(sh, 1)
|
|
||||||
let sy: Int = native_list_get(sh, 2)
|
|
||||||
let size: Int = native_list_get(sh, 3)
|
|
||||||
let cx: Int = sx + size / 2
|
|
||||||
if stype == 0 {
|
|
||||||
if px >= sx {
|
|
||||||
if px < sx + size {
|
|
||||||
if py >= sy {
|
|
||||||
if py < sy + size {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if stype == 1 {
|
|
||||||
let rad: Int = size / 2
|
|
||||||
let dx: Int = px - cx
|
|
||||||
let dy: Int = py - (sy + rad)
|
|
||||||
if dx * dx + dy * dy <= rad * rad {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// triangle: apex at top (sy), base at sy+size
|
|
||||||
if py >= sy {
|
|
||||||
if py < sy + size {
|
|
||||||
let dyv: Int = py - sy
|
|
||||||
let halfw: Int = dyv / 2
|
|
||||||
let dxv: Int = px - cx
|
|
||||||
let adx: Int = dxv
|
|
||||||
if adx < 0 { let adx: Int = 0 - dxv }
|
|
||||||
if adx <= halfw {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// pixel_color: painter's algorithm - last covering shape wins. Returns [r,g,b].
|
|
||||||
fn pixel_color(px: Int, py: Int, shapes: [[Int]], bg: [Int]) -> [Int] {
|
|
||||||
let r: Int = native_list_get(bg, 0)
|
|
||||||
let g: Int = native_list_get(bg, 1)
|
|
||||||
let b: Int = native_list_get(bg, 2)
|
|
||||||
let n: Int = native_list_len(shapes)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let sh: [Int] = native_list_get(shapes, i)
|
|
||||||
if covers(sh, px, py) {
|
|
||||||
let r: Int = native_list_get(sh, 4)
|
|
||||||
let g: Int = native_list_get(sh, 5)
|
|
||||||
let b: Int = native_list_get(sh, 6)
|
|
||||||
}
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
let out: [Int] = native_list_empty()
|
|
||||||
let out: [Int] = native_list_append(out, r)
|
|
||||||
let out: [Int] = native_list_append(out, g)
|
|
||||||
let out: [Int] = native_list_append(out, b)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// rasterize: build the raw (filtered) scanline byte stream, filter byte 0 / row.
|
|
||||||
fn rasterize(w: Int, h: Int, shapes: [[Int]], bg: [Int]) -> [Int] {
|
|
||||||
let raw: [Int] = native_list_empty()
|
|
||||||
let y: Int = 0
|
|
||||||
while y < h {
|
|
||||||
let raw: [Int] = native_list_append(raw, 0)
|
|
||||||
let x: Int = 0
|
|
||||||
while x < w {
|
|
||||||
let col: [Int] = pixel_color(x, y, shapes, bg)
|
|
||||||
let raw: [Int] = native_list_append(raw, native_list_get(col, 0))
|
|
||||||
let raw: [Int] = native_list_append(raw, native_list_get(col, 1))
|
|
||||||
let raw: [Int] = native_list_append(raw, native_list_get(col, 2))
|
|
||||||
let x: Int = x + 1
|
|
||||||
}
|
|
||||||
let y: Int = y + 1
|
|
||||||
}
|
|
||||||
return raw
|
|
||||||
}
|
|
||||||
|
|
||||||
// zlib stream with a single STORED (uncompressed) DEFLATE block + Adler32.
|
|
||||||
fn zlib_store(raw: [Int]) -> [Int] {
|
|
||||||
let z: [Int] = native_list_empty()
|
|
||||||
let z: [Int] = native_list_append(z, 120)
|
|
||||||
let z: [Int] = native_list_append(z, 1)
|
|
||||||
let z: [Int] = native_list_append(z, 1)
|
|
||||||
let len: Int = native_list_len(raw)
|
|
||||||
let nlen: Int = 65535 - len
|
|
||||||
let z: [Int] = native_list_append(z, len % 256)
|
|
||||||
let z: [Int] = native_list_append(z, (len / 256) % 256)
|
|
||||||
let z: [Int] = native_list_append(z, nlen % 256)
|
|
||||||
let z: [Int] = native_list_append(z, (nlen / 256) % 256)
|
|
||||||
let z: [Int] = app_all(z, raw)
|
|
||||||
let ad: Int = adler32_of(raw)
|
|
||||||
let z: [Int] = app_u32be(z, ad)
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
// append a full PNG chunk: length + (type+data) + crc32(type+data).
|
|
||||||
fn app_chunk(png: [Int], type_and_data: [Int], table: [Int]) -> [Int] {
|
|
||||||
let total: Int = native_list_len(type_and_data)
|
|
||||||
let dlen: Int = total - 4
|
|
||||||
let png: [Int] = app_u32be(png, dlen)
|
|
||||||
let png: [Int] = app_all(png, type_and_data)
|
|
||||||
let crc: Int = crc32_of(type_and_data, table)
|
|
||||||
let png: [Int] = app_u32be(png, crc)
|
|
||||||
return png
|
|
||||||
}
|
|
||||||
|
|
||||||
fn png_build(w: Int, h: Int, raw: [Int], table: [Int]) -> [Int] {
|
|
||||||
let png: [Int] = native_list_empty()
|
|
||||||
// 8-byte signature
|
|
||||||
let png: [Int] = native_list_append(png, 137)
|
|
||||||
let png: [Int] = native_list_append(png, 80)
|
|
||||||
let png: [Int] = native_list_append(png, 78)
|
|
||||||
let png: [Int] = native_list_append(png, 71)
|
|
||||||
let png: [Int] = native_list_append(png, 13)
|
|
||||||
let png: [Int] = native_list_append(png, 10)
|
|
||||||
let png: [Int] = native_list_append(png, 26)
|
|
||||||
let png: [Int] = native_list_append(png, 10)
|
|
||||||
// IHDR
|
|
||||||
let ihdr: [Int] = native_list_empty()
|
|
||||||
let ihdr: [Int] = app_tag(ihdr, "IHDR")
|
|
||||||
let ihdr: [Int] = app_u32be(ihdr, w)
|
|
||||||
let ihdr: [Int] = app_u32be(ihdr, h)
|
|
||||||
let ihdr: [Int] = native_list_append(ihdr, 8)
|
|
||||||
let ihdr: [Int] = native_list_append(ihdr, 2)
|
|
||||||
let ihdr: [Int] = native_list_append(ihdr, 0)
|
|
||||||
let ihdr: [Int] = native_list_append(ihdr, 0)
|
|
||||||
let ihdr: [Int] = native_list_append(ihdr, 0)
|
|
||||||
let png: [Int] = app_chunk(png, ihdr, table)
|
|
||||||
// IDAT
|
|
||||||
let z: [Int] = zlib_store(raw)
|
|
||||||
let idat: [Int] = native_list_empty()
|
|
||||||
let idat: [Int] = app_tag(idat, "IDAT")
|
|
||||||
let idat: [Int] = app_all(idat, z)
|
|
||||||
let png: [Int] = app_chunk(png, idat, table)
|
|
||||||
// IEND
|
|
||||||
let iend: [Int] = native_list_empty()
|
|
||||||
let iend: [Int] = app_tag(iend, "IEND")
|
|
||||||
let png: [Int] = app_chunk(png, iend, table)
|
|
||||||
return png
|
|
||||||
}
|
|
||||||
|
|
||||||
fn png_write(path: String, png: [Int]) -> Int {
|
|
||||||
let n: Int = native_list_len(png)
|
|
||||||
let buf: String = __str_alloc(n)
|
|
||||||
let i: Int = 0
|
|
||||||
while i < n {
|
|
||||||
let buf: String = __str_set_char(buf, i, native_list_get(png, i))
|
|
||||||
let i: Int = i + 1
|
|
||||||
}
|
|
||||||
let ok: Int = fs_write_bytes(path, buf, n)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
;;; lang_profile_ca.el — Catalan language profile for ELP.
|
|
||||||
;;; Mirrors lang_profile_it / _es / _pt; keys the realizer's construction switches.
|
|
||||||
;;; Catalan is the CLOSEST Romance sibling to the shared engine (~85% conceptual
|
|
||||||
;;; reuse). The deltas: PRONOMS FEBLES with four position allomorphs, l'-elision,
|
|
||||||
;;; del/al/pel contractions, the periphrastic preterite (vaig+INF), and NO
|
|
||||||
;;; essere/avere split (perfect aux is always HAVER; ser/estar is only the copula).
|
|
||||||
|
|
||||||
(lang_profile_ca
|
|
||||||
(language "Catalan")
|
|
||||||
(iso639 "ca")
|
|
||||||
(family "Romance")
|
|
||||||
|
|
||||||
;; ── core typology flags ────────────────────────────────────────────────
|
|
||||||
(pro-drop yes) ; null subjects default; overt pronoun = emphatic
|
|
||||||
(obligatory-subject no)
|
|
||||||
(grammatical-gender yes) ; m/f; full NP agreement (art + adj + participle)
|
|
||||||
(do-support no)
|
|
||||||
(subject-aux-inversion no) ; yes/no Q = declarative order + '?'; no inversion
|
|
||||||
(article-selection "el/la/l'/els/les ; un/una/uns/unes") ; l'-ELISION:
|
|
||||||
; el/la -> l' before vowel or (silent) h, glued to
|
|
||||||
; the next word (l'home, l'illa); de -> d' before vowel
|
|
||||||
(article-drives-contraction yes) ; article choice feeds prep+article contraction
|
|
||||||
(adjective-position "postnominal-default + small prenominal class") ; bo/bon,
|
|
||||||
; mal, gran, nou, vell, primer, molt... prenominal
|
|
||||||
(question-punct plain) ; ? and ! only (no inverted ¿ ¡)
|
|
||||||
|
|
||||||
;; ── MANDATORY prep+article contractions ────────────────────────────────
|
|
||||||
(contractions ((de el del) (de els dels)
|
|
||||||
(a el al) (a els als)
|
|
||||||
(per el pel) (per els pels)))
|
|
||||||
(contraction-mandatory yes) ; *de el -> del obligatory
|
|
||||||
(contraction-blocked-before-elision yes) ; de l'home / a l'home (NO *del home)
|
|
||||||
|
|
||||||
;; ── clitic system: PRONOMS FEBLES (the headline delta) ──────────────────
|
|
||||||
(clitics yes)
|
|
||||||
(clitic-allomorphy four-position) ; per pronoun, form varies by position+onset:
|
|
||||||
; reinforced (em, et, el) proclitic before a consonant
|
|
||||||
; elided (m', t', l', n') proclitic before a vowel/h
|
|
||||||
; full (-me, -lo, -li) enclitic after a consonant/-r
|
|
||||||
; reduced ('m, 't, 'l, 'ns) enclitic after a vowel
|
|
||||||
(clitic-placement ((finite proclitic) ; el veig, no m'ho dóna
|
|
||||||
(imperative-affirmative enclitic) ; dóna'm, digues-me
|
|
||||||
(imperative-negative present-subjunctive) ; no parlis (delta)
|
|
||||||
(infinitive enclitic) ; ajudar-me, veure'l
|
|
||||||
(gerund enclitic))) ; fent-ho
|
|
||||||
(clitic-combination ((me el "me'l") (te el "te'l") (se el "se'l")
|
|
||||||
(me la "me la") (me en "me'n")
|
|
||||||
(li el "l'hi") (li en "n'hi"))) ; dative+accusative clusters
|
|
||||||
(clitic-particles (hi en ho)) ; locative hi, partitive/genitive en, neuter ho
|
|
||||||
|
|
||||||
;; ── verb / aspect system ───────────────────────────────────────────────
|
|
||||||
(finite-agreement "person+number (6-way)")
|
|
||||||
(tenses (present imperfet preterit-simple perifrastic-preterit futur
|
|
||||||
condicional subjuntiu-present subjuntiu-imperfet imperatiu))
|
|
||||||
(periphrastic-preterite "vaig/vas/va/vam/vau/van + INFINITIVE") ; << hallmark CA
|
|
||||||
; (vaig cantar = 'I sang'); coexists w/ synthetic pret.
|
|
||||||
(compound-past "pretèrit perfet = haver(present) + participle")
|
|
||||||
(perfect-aux "HAVER only") ; << NO essere/avere split (simpler than IT)
|
|
||||||
(participle-agreement ((haver preceding-acc-clitic))) ; les he vistes; else invariable
|
|
||||||
(progressive-aux "estar + gerundi")
|
|
||||||
(copula "ser / estar") ; ser: identity/essential/origin; estar:
|
|
||||||
; location + transient state (estic cansat, és a casa)
|
|
||||||
(passive-aux "ser (+ per-agent)")
|
|
||||||
(future inflectional) ; cantaré, serà
|
|
||||||
(comparative "més/menys ADJ que")
|
|
||||||
|
|
||||||
;; ── SACRED safety bar (shared with es/pt/it/en) ────────────────────────
|
|
||||||
(negation-faithful yes) ; polarity never dropped/inverted; unplaceable -> FLAG
|
|
||||||
(negation "no (preverbal) + optional 'pas' + concord") ; no...res/
|
|
||||||
; ningú/mai/cap/gens/enlloc
|
|
||||||
(negative-concord yes) ; preverbal negative subject (ningú) keeps 'no'
|
|
||||||
(neg-reinforcer pas)) ; optional (no ho faré pas)
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
;;; lang_profile_de.el — German language profile for ELP.
|
|
||||||
;;; Mirrors lang_profile_en / lang_profile_es. Keys the realizer's construction
|
|
||||||
;;; switches. German is the largest Germanic delta from the EN engine: V2 word
|
|
||||||
;;; order, four morphological cases, and separable-prefix verbs.
|
|
||||||
|
|
||||||
(lang_profile_de
|
|
||||||
(language "German")
|
|
||||||
(iso639 "de")
|
|
||||||
(family "Germanic")
|
|
||||||
(neighbor-base "en") ; realized by extending the English (Germanic) engine
|
|
||||||
|
|
||||||
;; ── core typology flags ────────────────────────────────────────────────
|
|
||||||
(pro-drop no) ; obligatory subject in finite clauses
|
|
||||||
(obligatory-subject yes)
|
|
||||||
(grammatical-gender (m f n)) ; three genders; drives article + adj declension
|
|
||||||
(case-system (nom acc dat gen)) ; four cases on articles/adjs/nouns
|
|
||||||
(word-order V2) ; finite verb 2nd in main clause
|
|
||||||
(subordinate-order verb-final) ; "..., dass er den Hund SIEHT."
|
|
||||||
(separable-verbs yes) ; aufstehen -> "steht ... auf"; ppart "aufgestanden"
|
|
||||||
(do-support no) ; German negates/questions the finite verb directly
|
|
||||||
(subject-verb-inversion yes) ; yes/no Q fronts finite verb; wh-Q fills Vorfeld
|
|
||||||
(article-selection "der/die/das + ein/kein") ; declined by case x gender x number
|
|
||||||
(adjective-position prenominal)
|
|
||||||
(adjective-declension (strong weak mixed)) ; chosen by the determiner type
|
|
||||||
(noun-capitalization yes)
|
|
||||||
|
|
||||||
;; ── verb / aspect system ───────────────────────────────────────────────
|
|
||||||
(finite-agreement "person-and-number") ; full present/past paradigm
|
|
||||||
(auxiliary-order (modal tense-aux perfect passive main))
|
|
||||||
(perfect-aux (haben sein)) ; sein for intransitive motion/change verbs
|
|
||||||
(passive-aux "werden")
|
|
||||||
(future "werden + infinitive")
|
|
||||||
(comparative "synthetic (-er / -st, with umlaut)")
|
|
||||||
|
|
||||||
;; ── negation ───────────────────────────────────────────────────────────
|
|
||||||
(negation-markers (nicht kein)) ; kein- negates an indefinite NP; nicht else
|
|
||||||
(negation-faithful yes) ; SACRED: polarity never dropped/inverted -> FLAG
|
|
||||||
|
|
||||||
;; ── lexicon provenance ─────────────────────────────────────────────────
|
|
||||||
(lexicon-source "UniMorph deu (primary) + kaikki.org German (gender override)")
|
|
||||||
(lexicon-license "CC-BY-SA 3.0 / GFDL"))
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
;;; lang_profile_en.el — English language profile for ELP.
|
|
||||||
;;; Mirrors lang_profile_es / lang_profile_pt; keys the realizer's construction
|
|
||||||
;;; switches. English is typologically distinct from the Romance builds, so the
|
|
||||||
;;; flags differ where the grammar differs.
|
|
||||||
|
|
||||||
(lang_profile_en
|
|
||||||
(language "English")
|
|
||||||
(iso639 "en")
|
|
||||||
(family "Germanic")
|
|
||||||
|
|
||||||
;; ── core typology flags ────────────────────────────────────────────────
|
|
||||||
(pro-drop no) ; OBLIGATORY subjects — missing subject is FLAGGED
|
|
||||||
(obligatory-subject yes)
|
|
||||||
(grammatical-gender no) ; natural gender only (he/she/it), no NP agreement
|
|
||||||
(do-support yes) ; negation & questions of lexical verbs insert do/does/did
|
|
||||||
(subject-aux-inversion yes) ; yes/no + non-subject wh questions invert the operator
|
|
||||||
(article-selection "a/an/the") ; a/an resolved PHONOLOGICALLY (an hour, a university)
|
|
||||||
(adjective-position prenominal) ; attributive adjectives precede the noun; invariant
|
|
||||||
(has-tag-questions yes) ; "...doesn't he?" — operator + reversed polarity
|
|
||||||
(has-there-existential yes) ; "there is/are/have been ..."
|
|
||||||
(possessive-clitic "'s") ; saxon genitive; plural in -s -> bare apostrophe
|
|
||||||
(question-punct plain) ; ? and ! only (no inverted marks)
|
|
||||||
|
|
||||||
;; ── verb / aspect system ───────────────────────────────────────────────
|
|
||||||
(finite-agreement "3sg-present-only") ; only 3sg present -s (+ suppletive be)
|
|
||||||
(auxiliary-order (modal perfect progressive passive main))
|
|
||||||
(perfect-aux "have") ; have + past participle
|
|
||||||
(progressive-aux "be") ; be + present participle
|
|
||||||
(passive-aux "be") ; be + past participle (+ by-agent)
|
|
||||||
(future "will + base") ; no inflectional future
|
|
||||||
(comparative "synthetic-or-periphrastic") ; -er/-est vs more/most by syllables
|
|
||||||
|
|
||||||
;; ── SACRED safety bar (shared with es/pt) ──────────────────────────────
|
|
||||||
(negation-faithful yes) ; polarity never dropped/inverted; unplaceable -> FLAG
|
|
||||||
|
|
||||||
;; ── DIALECT overlay (post-realization, one core -> US/UK/AU) ────────────
|
|
||||||
(dialect US) ; default; profile field switches the overlay
|
|
||||||
(dialects (US UK AU))
|
|
||||||
(dialect-canonical US) ; core is authored in US orthography
|
|
||||||
(dialect-overlay "dialect_en.to_dialect") ; orthography + lexis + grammar prefs
|
|
||||||
(dialect-covers (spelling lexis collective-agreement gotten/got)))
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
;;; lang_profile_es.el — Spanish language profile for ELP.
|
|
||||||
;;; Keys the realizer's construction switches. Mirrors lang_profile_en / _pt.
|
|
||||||
|
|
||||||
(lang_profile_es
|
|
||||||
(language "Spanish")
|
|
||||||
(iso639 "es")
|
|
||||||
(family "Romance")
|
|
||||||
|
|
||||||
;; -- core typology flags -------------------------------------------------
|
|
||||||
(pro-drop yes) ; subjects routinely dropped; agreement carries person
|
|
||||||
(obligatory-subject no)
|
|
||||||
(grammatical-gender yes) ; m/f on every noun; article+adjective AGREE
|
|
||||||
(gender-source lexicon); REAL per-noun gender from UniMorph — NOT a heuristic
|
|
||||||
(do-support no)
|
|
||||||
(subject-aux-inversion no) ; questions by intonation/punctuation, not inversion
|
|
||||||
(question-strategy intonation)
|
|
||||||
(article-selection "el/la/los/las un/una/unos/unas")
|
|
||||||
(stressed-a-rule yes) ; fem sg noun in stressed a-/ha- takes el/un (el agua)
|
|
||||||
(adjective-position postnominal) ; default post; a few prenominal + apocope
|
|
||||||
(adjective-agreement "gender+number")
|
|
||||||
(question-punct inverted) ; opening ¿ ¡ required
|
|
||||||
|
|
||||||
;; -- MANDATORY CONTRACTIONS (coordinator quality bar) --------------------
|
|
||||||
(contractions ((de el "del") (a el "al")))
|
|
||||||
(contraction-mandatory yes) ; 'de el'/'a el' MUST surface as del/al
|
|
||||||
|
|
||||||
;; -- verb / aspect system ------------------------------------------------
|
|
||||||
(verb-classes (ar er ir))
|
|
||||||
(tenses (present preterite imperfect future conditional))
|
|
||||||
(moods (ind sbjv imp))
|
|
||||||
(finite-agreement "person+number (6 slots)")
|
|
||||||
(perfect-aux "haber") ; haber + past participle (invariant -o)
|
|
||||||
(progressive-aux "estar") ; estar + gerund
|
|
||||||
(passive-aux "ser") ; ser + participle (agrees) + por-agent
|
|
||||||
(copula-split "ser/estar") ; permanent vs stage-level
|
|
||||||
(future "infinitive + é/ás/á/emos/éis/án")
|
|
||||||
|
|
||||||
;; -- clitics / government ------------------------------------------------
|
|
||||||
(object-clitics yes) ; me te lo la le nos os los las; proclisis/enclisis
|
|
||||||
(clitic-order "se II I III (le+lo -> se lo)")
|
|
||||||
(enclisis "imperative/infinitive/gerund + accent repair (dá+me+lo->dámelo)")
|
|
||||||
(verb-prep-government yes) ; verbs select prep (protestar+contra, escapar+de)
|
|
||||||
|
|
||||||
;; -- SACRED safety bar (shared with en/pt) -------------------------------
|
|
||||||
(negation-faithful yes)) ; polarity never dropped/inverted; unplaceable -> FLAG
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
;;; lang_profile_fr.el — French language profile for ELP.
|
|
||||||
;;; Mirrors lang_profile_it / lang_profile_es; keys the realizer's construction
|
|
||||||
;;; switches. French is a Romance sibling (~54% of the realizer code and the whole
|
|
||||||
;;; clause-engine architecture reused), but carries the family's biggest surface
|
|
||||||
;;; deltas: NOT pro-drop, DISCONTINUOUS negation, and an orthography/phonology
|
|
||||||
;;; mismatch (elision, liaison) that makes exact-match genuinely hard.
|
|
||||||
|
|
||||||
(lang_profile_fr
|
|
||||||
(language "French")
|
|
||||||
(iso639 "fr")
|
|
||||||
(family "Romance")
|
|
||||||
|
|
||||||
;; ── core typology flags ────────────────────────────────────────────────
|
|
||||||
(pro-drop no) ; << French-specific: subject clitic OBLIGATORY
|
|
||||||
(obligatory-subject yes) ; je/tu/il/elle/nous/vous/ils/elles always overt
|
|
||||||
(grammatical-gender yes) ; m/f; full NP agreement (art + adj + participle)
|
|
||||||
(do-support no)
|
|
||||||
(subject-aux-inversion optional) ; est-ce que (default) OR clitic inversion (vas-tu)
|
|
||||||
(article-selection "le/la/l'/les ; un/une/des ; PARTITIVE du/de la/de l'/des")
|
|
||||||
(article-drives-contraction yes) ; à+le=au, de+le=du feed off article choice
|
|
||||||
(adjective-position "postnominal-default + prenominal-BAGS") ; beau/bon/grand/
|
|
||||||
; petit/jeune/vieux/nouveau + ordinals prenominal
|
|
||||||
; (beau->bel, nouveau->nouvel, vieux->vieil / vowel)
|
|
||||||
(question-punct "space-before") ; French typography: ' ?' ' !' (no ¿¡)
|
|
||||||
|
|
||||||
;; ── elision (orthography/phonology mismatch — French-specific) ──────────
|
|
||||||
(elision ((le l') (la l') (je j') (ne n') (de d') (que qu')
|
|
||||||
(me m') (te t') (se s') (ce c'))) ; before vowel / h-muet
|
|
||||||
(elision-h-muet yes) ; l'homme, l'hôpital (h-aspiré exception list kept)
|
|
||||||
(liaison noted-not-modeled) ; phonological, not written in surface
|
|
||||||
|
|
||||||
;; ── MANDATORY prep+article contractions ────────────────────────────────
|
|
||||||
(contractions ((à le au) (à les aux) (de le du) (de les des)))
|
|
||||||
(contraction-mandatory yes) ; *à le -> au obligatory; à la / à l' uncontracted
|
|
||||||
(partitive ((m-sg du) (f-sg "de la") (vowel "de l'") (pl des)))
|
|
||||||
(partitive-under-neg "de") ; << gap in current build: 'ne … pas de pain'
|
|
||||||
|
|
||||||
;; ── clitic system ──────────────────────────────────────────────────────
|
|
||||||
(clitics yes)
|
|
||||||
(clitic-order (me te se nous vous | le la les | lui leur | y | en))
|
|
||||||
(clitic-placement ((finite proclitic) ; je le lui donne
|
|
||||||
(imperative-affirmative enclitic-hyphen) ; donne-le-moi
|
|
||||||
(imperative-negative "ne+proclitic+verb+pas") ; ne le donne pas
|
|
||||||
(infinitive enclitic))) ; PARTIAL: clitic-climbing
|
|
||||||
; onto infinitive under modal
|
|
||||||
(clitic-imperative-shift ((me moi) (te toi))) ; final me/te -> moi/toi (donne-moi)
|
|
||||||
(clitic-particles (y en)) ; locative y, partitive/genitive en
|
|
||||||
|
|
||||||
;; ── verb / aspect system ───────────────────────────────────────────────
|
|
||||||
(finite-agreement "person+number (written; many homophones)")
|
|
||||||
(tenses (présent imparfait passé-simple futur conditionnel
|
|
||||||
subjonctif-présent subjonctif-imparfait impératif))
|
|
||||||
(compound-past "passé-composé = aux(present) + participe passé")
|
|
||||||
(perfect-aux "être/avoir (LEXICAL selection)") ; << French-specific
|
|
||||||
(etre-aux-class "intransitive motion/change (aller venir arriver partir
|
|
||||||
entrer sortir monter descendre naître mourir rester
|
|
||||||
tomber retourner passer devenir revenir rentrer) + ALL
|
|
||||||
pronominal verbs")
|
|
||||||
(participle-agreement ((être subject) ; elle est allée / elles venues
|
|
||||||
(avoir preceding-direct-object))) ; je les ai vus
|
|
||||||
(progressive "être en train de + infinitif") ; no dedicated aux
|
|
||||||
(copula "être (single; no ser/estar, no essere/stare)")
|
|
||||||
(passive-aux "être (+ par-agent)")
|
|
||||||
(future inflectional) ; parlera, sera
|
|
||||||
(comparative "plus/moins ADJ que")
|
|
||||||
(superlative "le/la plus ADJ (de …)") ; PARTIAL word-order in build
|
|
||||||
|
|
||||||
;; ── SACRED safety bar (shared with es/pt/it/en) ────────────────────────
|
|
||||||
(negation-faithful yes) ; polarity never dropped/inverted; unplaceable -> FLAG
|
|
||||||
(negation "DISCONTINUOUS: ne (preverbal) … pas/jamais/rien/personne/
|
|
||||||
plus/guère/que (postverbal)") ; << biggest structural delta
|
|
||||||
(negation-ne-elides yes) ; ne -> n' before vowel (n'ai pas vu)
|
|
||||||
(negation-passe-composé "ne + aux + pas + participe") ; n'ai pas vu
|
|
||||||
(negative-concord partial)) ; personne/rien as arguments post-participle
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
;;; lang_profile_it.el — Italian language profile for ELP.
|
|
||||||
;;; Mirrors lang_profile_es / lang_profile_pt; keys the realizer's construction
|
|
||||||
;;; switches. Italian is a Romance sibling, so ~85% of the flags match ES/PT; the
|
|
||||||
;;; essere/avere auxiliary split and phonological article selection are the deltas.
|
|
||||||
|
|
||||||
(lang_profile_it
|
|
||||||
(language "Italian")
|
|
||||||
(iso639 "it")
|
|
||||||
(family "Romance")
|
|
||||||
|
|
||||||
;; ── core typology flags ────────────────────────────────────────────────
|
|
||||||
(pro-drop yes) ; null subjects default; overt pronoun = emphatic
|
|
||||||
(obligatory-subject no)
|
|
||||||
(grammatical-gender yes) ; m/f; full NP agreement (art + adj + participle)
|
|
||||||
(do-support no)
|
|
||||||
(subject-aux-inversion no) ; yes/no Q = declarative order + '?'; no inversion
|
|
||||||
(article-selection "il/lo/l'/i/gli + la/l'/le ; un/uno/un'/una") ; PHONOLOGICAL:
|
|
||||||
; lo/gli/uno before s+cons, z, gn, ps, pn, x, y, i+V;
|
|
||||||
; l'/un' before a vowel (elision, glued to next word)
|
|
||||||
(article-drives-contraction yes) ; article choice feeds the prep+art contraction
|
|
||||||
(adjective-position "postnominal-default + prenominal-class") ; bello/buono/grande
|
|
||||||
; /nuovo/vecchio/primo... prenominal (with apocope)
|
|
||||||
(question-punct plain) ; ? and ! only (no inverted ¿ ¡)
|
|
||||||
|
|
||||||
;; ── MANDATORY prep+article contractions ────────────────────────────────
|
|
||||||
(contractions ((di il del) (di lo dello) (di la della) (di i dei)
|
|
||||||
(di gli degli) (di le delle) (di l' dell')
|
|
||||||
(a il al) (a lo allo) (a la alla) (a i ai) (a gli agli)
|
|
||||||
(a le alle) (a l' all')
|
|
||||||
(da il dal) (da la dalla) (da gli dagli) (da l' dall')
|
|
||||||
(in il nel) (in la nella) (in gli negli) (in l' nell')
|
|
||||||
(su il sul) (su la sulla) (su gli sugli) (su l' sull')))
|
|
||||||
(contraction-mandatory yes) ; *di il -> del is obligatory, never uncontracted
|
|
||||||
(prep-no-contract (per tra fra)) ; per la strada (NOT *perla)
|
|
||||||
|
|
||||||
;; ── clitic system ──────────────────────────────────────────────────────
|
|
||||||
(clitics yes)
|
|
||||||
(clitic-placement ((finite proclitic) ; lo vedo, non me lo dà
|
|
||||||
(imperative-affirmative enclitic) ; dammelo, guardalo
|
|
||||||
(imperative-negative-tu non+infinitive) ; non parlare / non lo fare
|
|
||||||
(infinitive enclitic) ; vederlo, aiutarmi (drop -e)
|
|
||||||
(gerund enclitic))) ; dandolo
|
|
||||||
(clitic-combination ((mi lo "me lo") (ti lo "te lo") (ci lo "ce lo")
|
|
||||||
(vi lo "ve lo") (si lo "se lo")
|
|
||||||
(gli lo "glielo") (le lo "glielo"))) ; glielo = ONE word
|
|
||||||
(clitic-particles (ci ne)) ; locative ci, partitive ne
|
|
||||||
(raddoppiamento (da fa di va sta)) ; monosyllabic imper double clitic: dammelo
|
|
||||||
|
|
||||||
;; ── verb / aspect system ───────────────────────────────────────────────
|
|
||||||
(finite-agreement "person+number (6-way)")
|
|
||||||
(tenses (presente imperfetto passato-remoto futuro condizionale
|
|
||||||
congiuntivo-presente congiuntivo-imperfetto imperativo))
|
|
||||||
(compound-past "passato-prossimo = aux(present) + participle")
|
|
||||||
(perfect-aux "essere/avere (LEXICAL selection)") ; << Italian-specific
|
|
||||||
(essere-aux-class unaccusative) ; motion/change-of-state/copular/pronominal
|
|
||||||
; (andare venire nascere morire diventare piacere
|
|
||||||
; + ALL reflexives) -> essere
|
|
||||||
(participle-agreement ((essere subject) ; è andata / sono arrivati
|
|
||||||
(avere preceding-acc-clitic))) ; li ho visti
|
|
||||||
(progressive-aux "stare + gerundio") ; sto parlando
|
|
||||||
(copula "essere (default) / stare (state: sto bene)")
|
|
||||||
(passive-aux "essere / venire (+ da-agent)")
|
|
||||||
(future inflectional) ; parlerò, sarà
|
|
||||||
(comparative "più/meno ADJ di")
|
|
||||||
|
|
||||||
;; ── SACRED safety bar (shared with es/pt/en) ───────────────────────────
|
|
||||||
(negation-faithful yes) ; polarity never dropped/inverted; unplaceable -> FLAG
|
|
||||||
(negation "non (preverbal) + concord") ; non...niente/nessuno/mai/più
|
|
||||||
(negative-concord yes) ; preverbal negative word (nessuno/niente) suppresses non
|
|
||||||
(neg-adverb-position between-aux-and-participle)) ; non ho MAI visto
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
;;; lang_profile_la.el — Latin language profile for ELP.
|
|
||||||
;;; Keys the realizer's construction switches. Companion to morphology-la.el.
|
|
||||||
|
|
||||||
(lang_profile_la
|
|
||||||
(language "Latin")
|
|
||||||
(iso639 "la")
|
|
||||||
(family "Italic")
|
|
||||||
|
|
||||||
;; -- core typology flags -------------------------------------------------
|
|
||||||
(pro-drop yes) ; person carried by verb ending; subjects dropped
|
|
||||||
(obligatory-subject no)
|
|
||||||
(grammatical-gender yes) ; m/f/n; adjective AGREES in case+gender+number
|
|
||||||
(gender-source lexicon) ; REAL per-noun gender from UniMorph lat
|
|
||||||
(articles none) ; Latin has no articles
|
|
||||||
(case-system yes) ; NOM GEN DAT ACC ABL VOC (+ rare LOC)
|
|
||||||
(cases (nom gen dat acc abl voc))
|
|
||||||
(word-order "SOV (default; free order, case-marked)")
|
|
||||||
(adjective-position "either (case agreement carries the link)")
|
|
||||||
(adjective-agreement "case+gender+number")
|
|
||||||
|
|
||||||
;; -- verb / aspect system ------------------------------------------------
|
|
||||||
(verb-classes (1 2 3 3io 4)) ; four conjugations + i-stem 3rd
|
|
||||||
(tenses (present imperfect future perfect pluperfect futureperfect))
|
|
||||||
(moods (indicative subjunctive imperative infinitive))
|
|
||||||
(voices (active passive))
|
|
||||||
(finite-agreement "person+number (6 slots)")
|
|
||||||
(citation "principal parts: pres-1sg / pres-inf / perf-participle")
|
|
||||||
|
|
||||||
;; -- SACRED safety bar ---------------------------------------------------
|
|
||||||
(negation-faithful yes)) ; polarity never dropped/inverted
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
;;; lang_profile_pt.el — Portuguese language profile for ELP.
|
|
||||||
;;; Keys the realizer's construction switches. Mirrors lang_profile_es.
|
|
||||||
|
|
||||||
(lang_profile_pt
|
|
||||||
(language "Portuguese")
|
|
||||||
(iso639 "pt")
|
|
||||||
(family "Romance")
|
|
||||||
|
|
||||||
;; -- core typology flags -------------------------------------------------
|
|
||||||
(pro-drop yes) ; subjects routinely dropped; agreement carries person
|
|
||||||
(obligatory-subject no)
|
|
||||||
(grammatical-gender yes) ; m/f on every noun; article+adjective AGREE
|
|
||||||
(gender-source lexicon) ; REAL per-noun gender from UniMorph por / kaikki
|
|
||||||
(do-support no)
|
|
||||||
(subject-aux-inversion no)
|
|
||||||
(question-strategy intonation)
|
|
||||||
(article-selection "o/a/os/as um/uma/uns/umas")
|
|
||||||
(adjective-position postnominal)
|
|
||||||
(adjective-agreement "gender+number")
|
|
||||||
|
|
||||||
;; -- MANDATORY CONTRACTIONS (prep + article) -----------------------------
|
|
||||||
(contractions ((de o "do") (de a "da") (em o "no") (em a "na")
|
|
||||||
(a o "ao") (a a "à") (por o "pelo") (por a "pela")))
|
|
||||||
(contraction-mandatory yes)
|
|
||||||
|
|
||||||
;; -- verb / aspect system ------------------------------------------------
|
|
||||||
(verb-classes (ar er ir))
|
|
||||||
(tenses (present preterite imperfect future conditional))
|
|
||||||
(moods (ind sbjv imp))
|
|
||||||
(finite-agreement "person+number (6 slots)")
|
|
||||||
(perfect-aux "ter") ; ter + past participle
|
|
||||||
(copula-split "ser/estar")
|
|
||||||
(personal-infinitive yes) ; distinctive PT inflected infinitive
|
|
||||||
|
|
||||||
;; -- clitics / government ------------------------------------------------
|
|
||||||
(object-clitics yes) ; mesoclisis/enclisis/proclisis by context
|
|
||||||
(verb-prep-government yes)
|
|
||||||
|
|
||||||
;; -- SACRED safety bar ---------------------------------------------------
|
|
||||||
(negation-faithful yes))
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
;;; lang_profile_ro.el — Romanian language profile for ELP.
|
|
||||||
;;; Romanian is the BIG typological delta of the Romance family. The verb/clause
|
|
||||||
;;; engine and the SACRED negation contract mirror the ES/PT/IT core, but the
|
|
||||||
;;; NOMINAL system is genuinely new: a SUFFIXED definite article, preserved CASE,
|
|
||||||
;;; a NEUTER gender, and a VOCATIVE. Those flags mark where the shared engine was
|
|
||||||
;;; extended rather than reused.
|
|
||||||
|
|
||||||
(lang_profile_ro
|
|
||||||
(language "Romanian")
|
|
||||||
(iso639 "ro")
|
|
||||||
(family "Romance (Eastern / Balkan)")
|
|
||||||
|
|
||||||
;; ── core typology flags ────────────────────────────────────────────────
|
|
||||||
(pro-drop yes) ; null subjects default; overt pronoun = emphatic
|
|
||||||
(obligatory-subject no)
|
|
||||||
(grammatical-gender yes) ; m / f / NEUTER (n)
|
|
||||||
(neuter-gender yes) ; << ROMANIAN-SPECIFIC: masc-agreeing SG, fem-agreeing PL
|
|
||||||
; (un tren nou / două trenuri noi)
|
|
||||||
(do-support no)
|
|
||||||
(subject-aux-inversion no) ; yes/no Q = declarative order + '?'
|
|
||||||
(question-punct plain) ; ? and ! only
|
|
||||||
|
|
||||||
;; ── SUFFIXED DEFINITE ARTICLE (the headline engine extension) ───────────
|
|
||||||
(definite-article suffixed) ; << UNIQUE IN ROMANCE: enclitic on the noun
|
|
||||||
(definite-forms ((m/n sg "-ul / -le / -l : om->omul, câine->câinele, codru->codrul")
|
|
||||||
(f sg "-a / -ea / -ua : casă->casa, carte->cartea, stea->steaua")
|
|
||||||
(m pl "-i : oameni->oamenii")
|
|
||||||
(f/n pl "-le : case->casele, trenuri->trenurile")))
|
|
||||||
(article-host ((no-prenom-adj noun) ; omul bun
|
|
||||||
(prenom-adj adjective))) ; bunul om (adj carries the article)
|
|
||||||
(indefinite-article ((m/n "un") (f "o") (pl "niște") (gen/dat-pl "unor")))
|
|
||||||
|
|
||||||
;; ── CASE (preserved; NOM/ACC vs GEN/DAT) ────────────────────────────────
|
|
||||||
(case (nom/acc gen/dat vocative)) ; << ROMANIAN-SPECIFIC
|
|
||||||
(case-syncretism "nom=acc ; gen=dat")
|
|
||||||
(genitive-marking "gen/dat definite: -lui (m/n), -ei/-i (f), -lor (pl)")
|
|
||||||
(genitival-article ((m sg "al") (f sg "a") (m pl "ai") (f/n pl "ale"))) ; o carte a lui
|
|
||||||
(possession "definite-head + gen/dat possessor: casa băiatului")
|
|
||||||
(vocative ((m sg "-ule/-e : omule, băiete") (f sg "-o : Mario, fato")
|
|
||||||
(pl "-lor")))
|
|
||||||
|
|
||||||
;; ── verb / aspect system ────────────────────────────────────────────────
|
|
||||||
(finite-agreement "person+number (6-way)")
|
|
||||||
(tenses (prezent imperfect perfect-simplu conjunctiv-prezent
|
|
||||||
imperativ (periphrastic: perfect-compus viitor conditional)))
|
|
||||||
(compound-past "perfectul compus = a-avea-clitic + INVARIABLE participle")
|
|
||||||
(perfect-aux "a avea (am/ai/a/am/ați/au) — ONE auxiliary for ALL verbs")
|
|
||||||
(perfect-aux-split no) ; << SIMPLER than Italian: no essere/avere selection
|
|
||||||
(participle-agreement none) ; invariable in the perfect compus (agrees only as
|
|
||||||
; an adjective / in the passive)
|
|
||||||
(future "voi/vei/va/vom/veți/vor + infinitive (viitor literar)")
|
|
||||||
(conditional "aș/ai/ar/am/ați/ar + infinitive")
|
|
||||||
(subjunctive "conjunctiv: particle 'să' + subjunctive present")
|
|
||||||
(modal-complement "modal + să + subjunctive (vreau să merg, poți să ajuți)")
|
|
||||||
(copula "a fi")
|
|
||||||
(passive "a fi + participle (participle AGREES like an adjective)")
|
|
||||||
(comparative "mai / mai puțin ADJ decât")
|
|
||||||
|
|
||||||
;; ── clitic system (partial — see honest gaps) ───────────────────────────
|
|
||||||
(clitics yes)
|
|
||||||
(clitic-set ((acc mă te îl o ne vă îi le) (dat îmi îți îi ne vă le)
|
|
||||||
(refl mă te se ne vă se)))
|
|
||||||
(clitic-placement ((finite proclitic) ; îmi place, o văd
|
|
||||||
(perfect-compus elision) ; << m-am, l-am, i-am (PARTIAL)
|
|
||||||
(imperative-affirmative enclitic))) ; dă-mi (PARTIAL)
|
|
||||||
|
|
||||||
;; ── SACRED safety bar (shared with es/pt/it/en) ─────────────────────────
|
|
||||||
(negation-faithful yes) ; polarity never dropped/inverted; unplaceable -> FLAG
|
|
||||||
(negation "nu (single preverbal marker) + concord")
|
|
||||||
(negative-concord yes) ; nu … nimic / nimeni / niciodată / niciun
|
|
||||||
(negative-imperative "nu + INFINITIVE : nu pleca! (KNOWN GAP: uses imperative stem)"))
|
|
||||||
@@ -1,46 +1,46 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn lang_profile(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String]
|
extern fn lang_profile(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> Any
|
||||||
extern fn lang_get(profile: [String], key: String) -> String
|
extern fn lang_get(profile: Any, key: String) -> String
|
||||||
extern fn lang_profile_en() -> [String]
|
extern fn lang_profile_en() -> Any
|
||||||
extern fn lang_profile_ja() -> [String]
|
extern fn lang_profile_ja() -> Any
|
||||||
extern fn lang_profile_ar() -> [String]
|
extern fn lang_profile_ar() -> Any
|
||||||
extern fn lang_profile_zh() -> [String]
|
extern fn lang_profile_zh() -> Any
|
||||||
extern fn lang_profile_de() -> [String]
|
extern fn lang_profile_de() -> Any
|
||||||
extern fn lang_profile_es() -> [String]
|
extern fn lang_profile_es() -> Any
|
||||||
extern fn lang_profile_fi() -> [String]
|
extern fn lang_profile_fi() -> Any
|
||||||
extern fn lang_profile_sw() -> [String]
|
extern fn lang_profile_sw() -> Any
|
||||||
extern fn lang_profile_hi() -> [String]
|
extern fn lang_profile_hi() -> Any
|
||||||
extern fn lang_profile_ru() -> [String]
|
extern fn lang_profile_ru() -> Any
|
||||||
extern fn lang_profile_fr() -> [String]
|
extern fn lang_profile_fr() -> Any
|
||||||
extern fn lang_profile_la() -> [String]
|
extern fn lang_profile_la() -> Any
|
||||||
extern fn lang_profile_he() -> [String]
|
extern fn lang_profile_he() -> Any
|
||||||
extern fn lang_profile_sa() -> [String]
|
extern fn lang_profile_sa() -> Any
|
||||||
extern fn lang_profile_got() -> [String]
|
extern fn lang_profile_got() -> Any
|
||||||
extern fn lang_profile_non() -> [String]
|
extern fn lang_profile_non() -> Any
|
||||||
extern fn lang_profile_enm() -> [String]
|
extern fn lang_profile_enm() -> Any
|
||||||
extern fn lang_profile_pi() -> [String]
|
extern fn lang_profile_pi() -> Any
|
||||||
extern fn lang_profile_grc() -> [String]
|
extern fn lang_profile_grc() -> Any
|
||||||
extern fn lang_profile_ang() -> [String]
|
extern fn lang_profile_ang() -> Any
|
||||||
extern fn lang_profile_fro() -> [String]
|
extern fn lang_profile_fro() -> Any
|
||||||
extern fn lang_profile_goh() -> [String]
|
extern fn lang_profile_goh() -> Any
|
||||||
extern fn lang_profile_sga() -> [String]
|
extern fn lang_profile_sga() -> Any
|
||||||
extern fn lang_profile_txb() -> [String]
|
extern fn lang_profile_txb() -> Any
|
||||||
extern fn lang_profile_peo() -> [String]
|
extern fn lang_profile_peo() -> Any
|
||||||
extern fn lang_profile_akk() -> [String]
|
extern fn lang_profile_akk() -> Any
|
||||||
extern fn lang_profile_uga() -> [String]
|
extern fn lang_profile_uga() -> Any
|
||||||
extern fn lang_profile_egy() -> [String]
|
extern fn lang_profile_egy() -> Any
|
||||||
extern fn lang_profile_sux() -> [String]
|
extern fn lang_profile_sux() -> Any
|
||||||
extern fn lang_profile_gez() -> [String]
|
extern fn lang_profile_gez() -> Any
|
||||||
extern fn lang_profile_cop() -> [String]
|
extern fn lang_profile_cop() -> Any
|
||||||
extern fn lang_from_code(code: String) -> [String]
|
extern fn lang_from_code(code: String) -> Any
|
||||||
extern fn lang_default() -> [String]
|
extern fn lang_default() -> Any
|
||||||
extern fn lang_is_isolating(profile: [String]) -> Bool
|
extern fn lang_is_isolating(profile: Any) -> Bool
|
||||||
extern fn lang_is_agglutinative(profile: [String]) -> Bool
|
extern fn lang_is_agglutinative(profile: Any) -> Bool
|
||||||
extern fn lang_is_fusional(profile: [String]) -> Bool
|
extern fn lang_is_fusional(profile: Any) -> Bool
|
||||||
extern fn lang_is_polysynthetic(profile: [String]) -> Bool
|
extern fn lang_is_polysynthetic(profile: Any) -> Bool
|
||||||
extern fn lang_is_rtl(profile: [String]) -> Bool
|
extern fn lang_is_rtl(profile: Any) -> Bool
|
||||||
extern fn lang_has_null_subject(profile: [String]) -> Bool
|
extern fn lang_has_null_subject(profile: Any) -> Bool
|
||||||
extern fn lang_has_case(profile: [String]) -> Bool
|
extern fn lang_has_case(profile: Any) -> Bool
|
||||||
extern fn lang_has_gender(profile: [String]) -> Bool
|
extern fn lang_has_gender(profile: Any) -> Bool
|
||||||
extern fn lang_word_order(profile: [String]) -> String
|
extern fn lang_word_order(profile: Any) -> String
|
||||||
extern fn lang_code(profile: [String]) -> String
|
extern fn lang_code(profile: Any) -> String
|
||||||
|
|||||||
@@ -56,7 +56,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn akk_str_ends(s: String, suf: String) -> Bool {
|
fn akk_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn akk_str_ends(s: String, suf: String) -> Bool
|
extern fn akk_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn akk_str_len(s: String) -> Int
|
extern fn akk_str_len(s: String) -> Int
|
||||||
extern fn akk_str_drop_last(s: String, n: Int) -> String
|
extern fn akk_str_drop_last(s: String, n: Int) -> String
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn ang_str_ends(s: String, suf: String) -> Bool {
|
fn ang_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn ang_str_ends(s: String, suf: String) -> Bool
|
extern fn ang_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn ang_str_drop_last(s: String, n: Int) -> String
|
extern fn ang_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn ang_str_last_char(s: String) -> String
|
extern fn ang_str_last_char(s: String) -> String
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
// ── String helpers ────────────────────────────────────────────────────────────
|
// ── String helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn ar_str_ends(s: String, suf: String) -> Bool {
|
fn ar_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn ar_str_ends(s: String, suf: String) -> Bool
|
extern fn ar_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn ar_str_len(s: String) -> Int
|
extern fn ar_str_len(s: String) -> Int
|
||||||
extern fn ar_str_drop_last(s: String, n: Int) -> String
|
extern fn ar_str_drop_last(s: String, n: Int) -> String
|
||||||
|
|||||||
@@ -54,7 +54,6 @@
|
|||||||
|
|
||||||
// ── String helpers ──────────────────────────────────────────────────────────────
|
// ── String helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn cop_str_ends(s: String, suf: String) -> Bool {
|
fn cop_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn cop_str_ends(s: String, suf: String) -> Bool
|
extern fn cop_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn cop_str_len(s: String) -> Int
|
extern fn cop_str_len(s: String) -> Int
|
||||||
extern fn cop_drop(s: String, n: Int) -> String
|
extern fn cop_drop(s: String, n: Int) -> String
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
// Dat: dem der dem den
|
// Dat: dem der dem den
|
||||||
// Gen: des der des der
|
// Gen: des der des der
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn de_article_def(gender: String, gram_case: String, number: String) -> String {
|
fn de_article_def(gender: String, gram_case: String, number: String) -> String {
|
||||||
if str_eq(number, "pl") {
|
if str_eq(number, "pl") {
|
||||||
if str_eq(gram_case, "nom") { return "die" }
|
if str_eq(gram_case, "nom") { return "die" }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn de_article_def(gender: String, gram_case: String, number: String) -> String
|
extern fn de_article_def(gender: String, gram_case: String, number: String) -> String
|
||||||
extern fn de_article_indef(gender: String, gram_case: String, number: String) -> String
|
extern fn de_article_indef(gender: String, gram_case: String, number: String) -> String
|
||||||
extern fn de_article(gender: String, gram_case: String, number: String, definite: String) -> String
|
extern fn de_article(gender: String, gram_case: String, number: String, definite: String) -> String
|
||||||
|
|||||||
@@ -52,7 +52,6 @@
|
|||||||
|
|
||||||
// ── String helpers ──────────────────────────────────────────────────────────────
|
// ── String helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn egy_str_ends(s: String, suf: String) -> Bool {
|
fn egy_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn egy_str_ends(s: String, suf: String) -> Bool
|
extern fn egy_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn egy_str_len(s: String) -> Int
|
extern fn egy_str_len(s: String) -> Int
|
||||||
extern fn egy_drop(s: String, n: Int) -> String
|
extern fn egy_drop(s: String, n: Int) -> String
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn enm_str_ends(s: String, suf: String) -> Bool {
|
fn enm_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn enm_str_ends(s: String, suf: String) -> Bool
|
extern fn enm_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn enm_drop(s: String, n: Int) -> String
|
extern fn enm_drop(s: String, n: Int) -> String
|
||||||
extern fn enm_first_char(s: String) -> String
|
extern fn enm_first_char(s: String) -> String
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
// ── String helpers (local, matching morphology.el conventions) ────────────────
|
// ── String helpers (local, matching morphology.el conventions) ────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn es_str_ends(s: String, suf: String) -> Bool {
|
fn es_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn es_str_ends(s: String, suf: String) -> Bool
|
extern fn es_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn es_str_drop_last(s: String, n: Int) -> String
|
extern fn es_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn es_str_last_char(s: String) -> String
|
extern fn es_str_last_char(s: String) -> String
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
// If only neutral vowels are found, default to "front" (the conservative choice
|
// If only neutral vowels are found, default to "front" (the conservative choice
|
||||||
// for borrowed words and those without clear back vowels).
|
// for borrowed words and those without clear back vowels).
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn fi_harmony(word: String) -> String {
|
fn fi_harmony(word: String) -> String {
|
||||||
let n: Int = str_len(word)
|
let n: Int = str_len(word)
|
||||||
let i: Int = n - 1
|
let i: Int = n - 1
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn fi_harmony(word: String) -> String
|
extern fn fi_harmony(word: String) -> String
|
||||||
extern fn fi_suffix(base: String, harmony: String) -> String
|
extern fn fi_suffix(base: String, harmony: String) -> String
|
||||||
extern fn fi_noun_case(stem: String, gram_case: String, number: String, harmony: String) -> String
|
extern fn fi_noun_case(stem: String, gram_case: String, number: String, harmony: String) -> String
|
||||||
extern fn fi_str_last_char(s: String) -> String
|
extern fn fi_str_last_char(s: String) -> String
|
||||||
extern fn fi_apply_case(noun: String, gram_case: String, number: String) -> String
|
extern fn fi_apply_case(noun: String, gram_case: String, number: String) -> String
|
||||||
extern fn fi_verb_stem(dict_form: String) -> String
|
extern fn fi_verb_stem(dict_form: String) -> String
|
||||||
extern fn fi_irregular_verb(dict_form: String) -> [String]
|
extern fn fi_irregular_verb(dict_form: String) -> Any
|
||||||
extern fn fi_present_ending(stem: String, person: String, number: String, harmony: String) -> String
|
extern fn fi_present_ending(stem: String, person: String, number: String, harmony: String) -> String
|
||||||
extern fn fi_past_stem(stem: String) -> String
|
extern fn fi_past_stem(stem: String) -> String
|
||||||
extern fn fi_past_ending(stem: String, person: String, number: String, harmony: String) -> String
|
extern fn fi_past_ending(stem: String, person: String, number: String, harmony: String) -> String
|
||||||
@@ -14,4 +14,4 @@ extern fn fi_negative(verb: String, person: String, number: String) -> String
|
|||||||
extern fn fi_conjugate(verb: String, tense: String, person: String, number: String) -> String
|
extern fn fi_conjugate(verb: String, tense: String, person: String, number: String) -> String
|
||||||
extern fn fi_question_suffix(harmony: String) -> String
|
extern fn fi_question_suffix(harmony: String) -> String
|
||||||
extern fn fi_make_question(verb_form: String, harmony: String) -> String
|
extern fn fi_make_question(verb_form: String, harmony: String) -> String
|
||||||
extern fn fi_full_paradigm(noun: String) -> [String]
|
extern fn fi_full_paradigm(noun: String) -> Any
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
// ── String helpers (local, matching morphology.el conventions) ────────────────
|
// ── String helpers (local, matching morphology.el conventions) ────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn fr_str_ends(s: String, suf: String) -> Bool {
|
fn fr_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn fr_str_ends(s: String, suf: String) -> Bool
|
extern fn fr_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn fr_str_drop_last(s: String, n: Int) -> String
|
extern fn fr_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn fr_str_last_char(s: String) -> String
|
extern fn fr_str_last_char(s: String) -> String
|
||||||
|
|||||||
@@ -53,7 +53,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn fro_str_ends(s: String, suf: String) -> Bool {
|
fn fro_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn fro_str_ends(s: String, suf: String) -> Bool
|
extern fn fro_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn fro_drop(s: String, n: Int) -> String
|
extern fn fro_drop(s: String, n: Int) -> String
|
||||||
extern fn fro_slot(person: String, number: String) -> Int
|
extern fn fro_slot(person: String, number: String) -> Int
|
||||||
|
|||||||
@@ -64,7 +64,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn gez_str_ends(s: String, suf: String) -> Bool {
|
fn gez_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn gez_str_ends(s: String, suf: String) -> Bool
|
extern fn gez_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn gez_str_len(s: String) -> Int
|
extern fn gez_str_len(s: String) -> Int
|
||||||
extern fn gez_str_drop_last(s: String, n: Int) -> String
|
extern fn gez_str_drop_last(s: String, n: Int) -> String
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn goh_str_ends(s: String, suf: String) -> Bool {
|
fn goh_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn goh_str_ends(s: String, suf: String) -> Bool
|
extern fn goh_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn goh_drop(s: String, n: Int) -> String
|
extern fn goh_drop(s: String, n: Int) -> String
|
||||||
extern fn goh_slot(person: String, number: String) -> Int
|
extern fn goh_slot(person: String, number: String) -> Int
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn got_str_ends(s: String, suf: String) -> Bool {
|
fn got_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn got_str_ends(s: String, suf: String) -> Bool
|
extern fn got_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn got_str_drop_last(s: String, n: Int) -> String
|
extern fn got_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn got_slot(person: String, number: String) -> Int
|
extern fn got_slot(person: String, number: String) -> Int
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn grc_str_ends(s: String, suf: String) -> Bool {
|
fn grc_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn grc_str_ends(s: String, suf: String) -> Bool
|
extern fn grc_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn grc_str_drop_last(s: String, n: Int) -> String
|
extern fn grc_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn grc_str_last_char(s: String) -> String
|
extern fn grc_str_last_char(s: String) -> String
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn he_str_ends(s: String, suf: String) -> Bool {
|
fn he_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn he_str_ends(s: String, suf: String) -> Bool
|
extern fn he_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn he_str_len(s: String) -> Int
|
extern fn he_str_len(s: String) -> Int
|
||||||
extern fn he_str_drop_last(s: String, n: Int) -> String
|
extern fn he_str_drop_last(s: String, n: Int) -> String
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
// ── String helpers ────────────────────────────────────────────────────────────
|
// ── String helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn hi_str_ends(s: String, suf: String) -> Bool {
|
fn hi_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn hi_str_ends(s: String, suf: String) -> Bool
|
extern fn hi_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn hi_str_drop_last(s: String, n: Int) -> String
|
extern fn hi_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn hi_str_last_char(s: String) -> String
|
extern fn hi_str_last_char(s: String) -> String
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
// Note: this is a heuristic classifier for romanized input. For production use
|
// Note: this is a heuristic classifier for romanized input. For production use
|
||||||
// with native kana/kanji forms, the dictionary form (辞書形) must be consulted.
|
// with native kana/kanji forms, the dictionary form (辞書形) must be consulted.
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn ja_verb_group(dict_form: String) -> String {
|
fn ja_verb_group(dict_form: String) -> String {
|
||||||
// Irregular verbs (exact match on dictionary form)
|
// Irregular verbs (exact match on dictionary form)
|
||||||
if str_eq(dict_form, "する") { return "irregular" }
|
if str_eq(dict_form, "する") { return "irregular" }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn ja_verb_group(dict_form: String) -> String
|
extern fn ja_verb_group(dict_form: String) -> String
|
||||||
extern fn ja_ichidan_stem(dict_form: String) -> String
|
extern fn ja_ichidan_stem(dict_form: String) -> String
|
||||||
extern fn ja_godan_stem_change(dict_form: String, row: String) -> String
|
extern fn ja_godan_stem_change(dict_form: String, row: String) -> String
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn la_str_ends(s: String, suf: String) -> Bool {
|
fn la_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn la_str_ends(s: String, suf: String) -> Bool
|
extern fn la_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn la_str_drop_last(s: String, n: Int) -> String
|
extern fn la_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn la_str_last_char(s: String) -> String
|
extern fn la_str_last_char(s: String) -> String
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn non_str_ends(s: String, suf: String) -> Bool {
|
fn non_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn non_str_ends(s: String, suf: String) -> Bool
|
extern fn non_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn non_drop(s: String, n: Int) -> String
|
extern fn non_drop(s: String, n: Int) -> String
|
||||||
extern fn non_last(s: String) -> String
|
extern fn non_last(s: String) -> String
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn peo_drop(s: String, n: Int) -> String {
|
fn peo_drop(s: String, n: Int) -> String {
|
||||||
let len: Int = str_len(s)
|
let len: Int = str_len(s)
|
||||||
if n >= len { return "" }
|
if n >= len { return "" }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn peo_drop(s: String, n: Int) -> String
|
extern fn peo_drop(s: String, n: Int) -> String
|
||||||
extern fn peo_ends(s: String, suf: String) -> Bool
|
extern fn peo_ends(s: String, suf: String) -> Bool
|
||||||
extern fn peo_slot(person: String, number: String) -> Int
|
extern fn peo_slot(person: String, number: String) -> Int
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn pi_str_ends(s: String, suf: String) -> Bool {
|
fn pi_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn pi_str_ends(s: String, suf: String) -> Bool
|
extern fn pi_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn pi_drop(s: String, n: Int) -> String
|
extern fn pi_drop(s: String, n: Int) -> String
|
||||||
extern fn pi_last_char(s: String) -> String
|
extern fn pi_last_char(s: String) -> String
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
// The heuristic returns the most probable gender. Caller should override
|
// The heuristic returns the most probable gender. Caller should override
|
||||||
// for known exceptions (путь, рубль are masc despite -ь).
|
// for known exceptions (путь, рубль are masc despite -ь).
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn ru_gender(noun: String) -> String {
|
fn ru_gender(noun: String) -> String {
|
||||||
let n: Int = str_len(noun)
|
let n: Int = str_len(noun)
|
||||||
if n == 0 { return "m" }
|
if n == 0 { return "m" }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn ru_gender(noun: String) -> String
|
extern fn ru_gender(noun: String) -> String
|
||||||
extern fn ru_stem_type(noun: String, gender: String) -> String
|
extern fn ru_stem_type(noun: String, gender: String) -> String
|
||||||
extern fn ru_noun_case(noun: String, gender: String, gram_case: String, number: String) -> String
|
extern fn ru_noun_case(noun: String, gender: String, gram_case: String, number: String) -> String
|
||||||
|
|||||||
@@ -42,7 +42,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn sa_str_ends(s: String, suf: String) -> Bool {
|
fn sa_str_ends(s: String, suf: String) -> Bool {
|
||||||
return str_ends_with(s, suf)
|
return str_ends_with(s, suf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header - do not edit
|
||||||
extern fn sa_str_ends(s: String, suf: String) -> Bool
|
extern fn sa_str_ends(s: String, suf: String) -> Bool
|
||||||
extern fn sa_str_drop_last(s: String, n: Int) -> String
|
extern fn sa_str_drop_last(s: String, n: Int) -> String
|
||||||
extern fn sa_slot(person: String, number: String) -> Int
|
extern fn sa_slot(person: String, number: String) -> Int
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
// ── String helpers ─────────────────────────────────────────────────────────────
|
// ── String helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
import "morphology.el"
|
|
||||||
fn sga_drop(s: String, n: Int) -> String {
|
fn sga_drop(s: String, n: Int) -> String {
|
||||||
let len: Int = str_len(s)
|
let len: Int = str_len(s)
|
||||||
if n >= len { return "" }
|
if n >= len { return "" }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user