feat(ui): native vessel, HTML vessel update, native hello examples, profile card, UI tools
El SDK Release / build-and-release (pull_request) Failing after 17s

el-native vessel: El-level wrappers around __widget_* C builtins, exposing
vstack, label, button, text_field, etc. as clean El functions for application code.

el-html/main.elh: updated extern declarations for the HTML vessel's codegen API.

native-hello: cross-platform desktop example (AppKit/GTK4/Win32/SDL2) with
build scripts, Dockerfiles for Linux/Pi, and Win32 cross-compile support.

native-hello-android: Gradle project with ElBridge integration and build script.

native-hello-ios: Xcode project for the iOS UIKit target.

profile-card: manifest.el for a styling/layout/i18n example app that exercises
el-style, el-layout, el-i18n, el-config, and el-secrets vessels.

ui/tools/native-codegen: Python codegen pass (el_ui_native_codegen.py) that
lowers el-ui component DSL to el-native vessel calls, plus build script and
test fixtures.
This commit is contained in:
2026-06-29 12:40:37 -05:00
parent edff25180e
commit 58753a88d7
106 changed files with 58984 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
build/
*.xcuserstate
xcuserdata/
NativeHello.xcodeproj/xcuserdata/
@@ -0,0 +1,377 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
4C6D9832BE495DD69A8573D9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FFDE02477855A55BC5F02CE /* main.m */; };
396FD073065B58F39358D798 /* el_uikit.m in Sources */ = {isa = PBXBuildFile; fileRef = 606F96EC1EDC54CD99E7D722 /* el_uikit.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
92BBC3E2AA055FCE840BF779 /* el_seed.c in Sources */ = {isa = PBXBuildFile; fileRef = 618FBE18EAA850D9B493A20F /* el_seed.c */; };
136C7763096C5037A69077AD /* el_runtime.c in Sources */ = {isa = PBXBuildFile; fileRef = A8CCF8316D83597CBB332064 /* el_runtime.c */; };
25136DF2A63F507EA5C7EF99 /* el_native_vessel.c in Sources */ = {isa = PBXBuildFile; fileRef = 3373ABFFE7FB5EFA9343AD02 /* el_native_vessel.c */; };
C05D581DBD4E52F6B7F7303E /* native_hello.c in Sources */ = {isa = PBXBuildFile; fileRef = EDA408C749EB5F3CA1E5EEC2 /* native_hello.c */; };
C3D9A9987F0050EB859D9218 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF42D2E0FA0655149E573F7C /* UIKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
8FFDE02477855A55BC5F02CE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
606F96EC1EDC54CD99E7D722 /* el_uikit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = el_uikit.m; sourceTree = "<group>"; };
618FBE18EAA850D9B493A20F /* el_seed.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = el_seed.c; sourceTree = "<group>"; };
A8CCF8316D83597CBB332064 /* el_runtime.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = el_runtime.c; sourceTree = "<group>"; };
530AA788858357FBA923217E /* el_runtime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = el_runtime.h; sourceTree = "<group>"; };
E1A7B02609E95D1686523B80 /* el_native_target.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = el_native_target.h; sourceTree = "<group>"; };
3373ABFFE7FB5EFA9343AD02 /* el_native_vessel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = el_native_vessel.c; sourceTree = "<group>"; };
EDA408C749EB5F3CA1E5EEC2 /* native_hello.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = native_hello.c; sourceTree = "<group>"; };
08B6475D8C7952F48EF46B10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
FF42D2E0FA0655149E573F7C /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
BDC23A5F3FCD5D1098144064 /* NativeHello.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NativeHello.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
5673C72A848C5FFF94E7CF8F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C3D9A9987F0050EB859D9218 /* UIKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
05039DD2316A5B41A35E094E /* root */ = {
isa = PBXGroup;
children = (
EBC5644CBC315AFDA6A6A59E /* NativeHello */,
D1E4BC2FC3925E6FAFCD38F6 /* Frameworks */,
177F2C27230B527E80CEAED5 /* Products */,
);
sourceTree = "<group>";
};
177F2C27230B527E80CEAED5 /* Products */ = {
isa = PBXGroup;
children = (
BDC23A5F3FCD5D1098144064 /* NativeHello.app */,
);
name = Products;
sourceTree = "<group>";
};
EBC5644CBC315AFDA6A6A59E /* NativeHello */ = {
isa = PBXGroup;
children = (
8FFDE02477855A55BC5F02CE /* main.m */,
606F96EC1EDC54CD99E7D722 /* el_uikit.m */,
618FBE18EAA850D9B493A20F /* el_seed.c */,
A8CCF8316D83597CBB332064 /* el_runtime.c */,
530AA788858357FBA923217E /* el_runtime.h */,
E1A7B02609E95D1686523B80 /* el_native_target.h */,
3373ABFFE7FB5EFA9343AD02 /* el_native_vessel.c */,
EDA408C749EB5F3CA1E5EEC2 /* native_hello.c */,
08B6475D8C7952F48EF46B10 /* Info.plist */,
);
path = NativeHello;
sourceTree = "<group>";
};
D1E4BC2FC3925E6FAFCD38F6 /* Frameworks */ = {
isa = PBXGroup;
children = (
FF42D2E0FA0655149E573F7C /* UIKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
F1275C1CDFD05A40B9D6C717 /* NativeHello */ = {
isa = PBXNativeTarget;
buildConfigurationList = 07AFC970D6B05512B39EDE6A /* Build configuration list for PBXNativeTarget "NativeHello" */;
buildPhases = (
DF29C8DEE5CB5F1C83967F1F /* Sources */,
5673C72A848C5FFF94E7CF8F /* Frameworks */,
D6D57F9604C45AAEA747645E /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = NativeHello;
productName = NativeHello;
productReference = BDC23A5F3FCD5D1098144064 /* NativeHello.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
D8A823993D3254C7B6541F91 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 1500;
TargetAttributes = {
F1275C1CDFD05A40B9D6C717 = {
CreatedOnToolsVersion = 15.0;
};
};
};
buildConfigurationList = 3DAB235BBB945E0CBB3EF554 /* Build configuration list for PBXProject "NativeHello" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 05039DD2316A5B41A35E094E;
productRefGroup = 177F2C27230B527E80CEAED5 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
F1275C1CDFD05A40B9D6C717 /* NativeHello */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
D6D57F9604C45AAEA747645E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
DF29C8DEE5CB5F1C83967F1F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4C6D9832BE495DD69A8573D9 /* main.m in Sources */,
396FD073065B58F39358D798 /* el_uikit.m in Sources */,
92BBC3E2AA055FCE840BF779 /* el_seed.c in Sources */,
136C7763096C5037A69077AD /* el_runtime.c in Sources */,
25136DF2A63F507EA5C7EF99 /* el_native_vessel.c in Sources */,
C05D581DBD4E52F6B7F7303E /* native_hello.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
071FC6E313815E66BB76FC93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_EXCEPTION = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
};
name = Debug;
};
64E467FC48E757C0BDE1DFF6 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_EXCEPTION = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
5DAFA921569F54D081B65492 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"EL_TARGET_IOS=1",
"DEBUG=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/NativeHello",
"$(inherited)",
);
INFOPLIST_FILE = NativeHello/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_CFLAGS = (
"-DEL_TARGET_IOS",
"$(inherited)",
);
OTHER_LDFLAGS = (
"-ldl",
"$(inherited)",
);
PRODUCT_BUNDLE_IDENTIFIER = ai.neurontechnologies.el.nativehello;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
81898B3044815511B8C31D64 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"EL_TARGET_IOS=1",
"$(inherited)",
);
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/NativeHello",
"$(inherited)",
);
INFOPLIST_FILE = NativeHello/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_CFLAGS = (
"-DEL_TARGET_IOS",
"$(inherited)",
);
OTHER_LDFLAGS = (
"-ldl",
"$(inherited)",
);
PRODUCT_BUNDLE_IDENTIFIER = ai.neurontechnologies.el.nativehello;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
3DAB235BBB945E0CBB3EF554 /* Build configuration list for PBXProject "NativeHello" */ = {
isa = XCConfigurationList;
buildConfigurations = (
071FC6E313815E66BB76FC93 /* Debug */,
64E467FC48E757C0BDE1DFF6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
07AFC970D6B05512B39EDE6A /* Build configuration list for PBXNativeTarget "NativeHello" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5DAFA921569F54D081B65492 /* Debug */,
81898B3044815511B8C31D64 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = D8A823993D3254C7B6541F91 /* Project object */;
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F1275C1CDFD05A40B9D6C717"
BuildableName = "NativeHello.app"
BlueprintName = "NativeHello"
ReferencedContainer = "container:NativeHello.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F1275C1CDFD05A40B9D6C717"
BuildableName = "NativeHello.app"
BlueprintName = "NativeHello"
ReferencedContainer = "container:NativeHello.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F1275C1CDFD05A40B9D6C717"
BuildableName = "NativeHello.app"
BlueprintName = "NativeHello"
ReferencedContainer = "container:NativeHello.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict/>
<key>UILaunchScreen</key>
<dict/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -0,0 +1,573 @@
/*
* el_native_target.h — Native widget declarations for el programs targeting
* native desktop UI (AppKit / GTK4 / Win32).
*
* This header is designed to be included AFTER el_runtime.h without conflict:
* - It does NOT redefine el_to_float, el_from_float, or any el_runtime.h
* static inlines.
* - It does NOT redeclare __println, __print, or other functions whose
* return types differ between el_seed.h and el_runtime.h.
* - It adds: native widget builtins + float arithmetic helpers that the
* current el_runtime.h omits but elc still emits calls to.
*
* Usage:
* Inject via -include at compile time, OR #include it after el_runtime.h.
*
* clang -DEL_TARGET_MACOS -include el_native_target.h -c my_app.c ...
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
/* el_val_t must already be defined by el_runtime.h or el_seed.h. */
#ifndef EL_VAL_T_DEFINED
typedef int64_t el_val_t;
#endif
/* ── Float arithmetic helpers ───────────────────────────────────────────────
* elc emits calls to float_div / float_mul etc. for Float-typed expressions.
* These were in el_runtime.c through v1.0.0-20260501 but are missing from the
* current el_runtime.h. Redeclared here as static inline to avoid link deps.
* Only defined if not already declared (old runtimes that still have them). */
#ifndef EL_FLOAT_OPS_DEFINED
#define EL_FLOAT_OPS_DEFINED
/* el_to_float / el_from_float — bit-cast between el_val_t and double.
* Defined as static inline in both el_runtime.h and el_seed.h; we do NOT
* redefine them here. We rely on one of those headers being included first. */
static inline el_val_t float_div(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub, ur;
ua.i = a; ub.i = b;
ur.d = (ub.d != 0.0) ? (ua.d / ub.d) : 0.0;
return ur.i;
}
static inline el_val_t float_mul(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub, ur;
ua.i = a; ub.i = b; ur.d = ua.d * ub.d;
return ur.i;
}
static inline el_val_t float_add(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub, ur;
ua.i = a; ub.i = b; ur.d = ua.d + ub.d;
return ur.i;
}
static inline el_val_t float_sub(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub, ur;
ua.i = a; ub.i = b; ur.d = ua.d - ub.d;
return ur.i;
}
static inline el_val_t float_lt(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub;
ua.i = a; ub.i = b;
return (el_val_t)(ua.d < ub.d);
}
static inline el_val_t float_gt(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub;
ua.i = a; ub.i = b;
return (el_val_t)(ua.d > ub.d);
}
static inline el_val_t float_lte(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub;
ua.i = a; ub.i = b;
return (el_val_t)(ua.d <= ub.d);
}
static inline el_val_t float_gte(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub;
ua.i = a; ub.i = b;
return (el_val_t)(ua.d >= ub.d);
}
static inline el_val_t float_eq(el_val_t a, el_val_t b) {
union { double d; int64_t i; } ua, ub;
ua.i = a; ub.i = b;
return (el_val_t)(ua.d == ub.d);
}
#endif /* EL_FLOAT_OPS_DEFINED */
/* ── Native widget system (macOS AppKit) ────────────────────────────────────
* Available when compiled with -DEL_TARGET_MACOS and linked with el_appkit.m.
* Widget handles are opaque int64_t slot indices; -1 = invalid. */
#ifdef EL_TARGET_MACOS
/* Initialisation */
void __native_init(void);
void __native_run_loop(void);
/* Window */
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
el_val_t min_width, el_val_t min_height);
void __window_show(el_val_t handle);
void __window_set_title(el_val_t handle, el_val_t title);
/* Layout containers */
el_val_t __vstack_create(el_val_t spacing);
el_val_t __hstack_create(el_val_t spacing);
el_val_t __zstack_create(void);
el_val_t __scroll_create(void);
/* Widgets */
el_val_t __label_create(el_val_t text);
el_val_t __button_create(el_val_t label);
el_val_t __text_field_create(el_val_t placeholder);
el_val_t __text_area_create(el_val_t placeholder);
el_val_t __image_create(el_val_t path_or_name);
/* Widget properties */
void __widget_set_text(el_val_t handle, el_val_t text);
el_val_t __widget_get_text(el_val_t handle);
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_font(el_val_t handle, el_val_t family,
el_val_t size, el_val_t bold);
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
el_val_t bottom, el_val_t left);
void __widget_set_width(el_val_t handle, el_val_t width);
void __widget_set_height(el_val_t handle, el_val_t height);
void __widget_set_flex(el_val_t handle, el_val_t flex);
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
/* Layout / tree */
void __widget_add_child(el_val_t parent, el_val_t child);
void __widget_remove_child(el_val_t parent, el_val_t child);
void __widget_destroy(el_val_t handle);
/* Events */
void __widget_on_click(el_val_t handle, el_val_t fn_name);
void __widget_on_change(el_val_t handle, el_val_t fn_name);
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
/* Manifest reader */
el_val_t __manifest_read(el_val_t path);
#endif /* EL_TARGET_MACOS */
/* ── Native widget system (Linux GTK4) ──────────────────────────────────────
* Available when compiled with -DEL_TARGET_LINUX and linked with el_gtk4.c.
* Widget handles are opaque int64_t slot indices; -1 = invalid.
* All functions have the same signatures as EL_TARGET_MACOS above. */
#ifdef EL_TARGET_LINUX
/* Initialisation */
void __native_init(void);
void __native_run_loop(void);
/* Window */
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
el_val_t min_width, el_val_t min_height);
void __window_show(el_val_t handle);
void __window_set_title(el_val_t handle, el_val_t title);
/* Layout containers */
el_val_t __vstack_create(el_val_t spacing);
el_val_t __hstack_create(el_val_t spacing);
el_val_t __zstack_create(void);
el_val_t __scroll_create(void);
/* Widgets */
el_val_t __label_create(el_val_t text);
el_val_t __button_create(el_val_t label);
el_val_t __text_field_create(el_val_t placeholder);
el_val_t __text_area_create(el_val_t placeholder);
el_val_t __image_create(el_val_t path_or_name);
/* Widget properties */
void __widget_set_text(el_val_t handle, el_val_t text);
el_val_t __widget_get_text(el_val_t handle);
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_font(el_val_t handle, el_val_t family,
el_val_t size, el_val_t bold);
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
el_val_t bottom, el_val_t left);
void __widget_set_width(el_val_t handle, el_val_t width);
void __widget_set_height(el_val_t handle, el_val_t height);
void __widget_set_flex(el_val_t handle, el_val_t flex);
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
/* Layout / tree */
void __widget_add_child(el_val_t parent, el_val_t child);
void __widget_remove_child(el_val_t parent, el_val_t child);
void __widget_destroy(el_val_t handle);
/* Events */
void __widget_on_click(el_val_t handle, el_val_t fn_name);
void __widget_on_change(el_val_t handle, el_val_t fn_name);
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
/* Manifest reader — same JSON output as EL_TARGET_MACOS */
el_val_t __manifest_read(el_val_t path);
#endif /* EL_TARGET_LINUX */
/* ── Native widget system (Windows Win32) ───────────────────────────────────
* Available when compiled with -DEL_TARGET_WIN32 and linked with el_win32.c.
* Widget handles are opaque int64_t slot indices; -1 = invalid.
* Link: el_win32.obj comctl32.lib user32.lib gdi32.lib */
#ifdef EL_TARGET_WIN32
/* Initialisation */
void __native_init(void);
void __native_run_loop(void);
/* Window */
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
el_val_t min_width, el_val_t min_height);
void __window_show(el_val_t handle);
void __window_set_title(el_val_t handle, el_val_t title);
/* Layout containers */
el_val_t __vstack_create(el_val_t spacing);
el_val_t __hstack_create(el_val_t spacing);
el_val_t __zstack_create(void);
el_val_t __scroll_create(void);
/* Widgets */
el_val_t __label_create(el_val_t text);
el_val_t __button_create(el_val_t label);
el_val_t __text_field_create(el_val_t placeholder);
el_val_t __text_area_create(el_val_t placeholder);
el_val_t __image_create(el_val_t path_or_name);
/* Widget properties */
void __widget_set_text(el_val_t handle, el_val_t text);
el_val_t __widget_get_text(el_val_t handle);
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_font(el_val_t handle, el_val_t family,
el_val_t size, el_val_t bold);
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
el_val_t bottom, el_val_t left);
void __widget_set_width(el_val_t handle, el_val_t width);
void __widget_set_height(el_val_t handle, el_val_t height);
void __widget_set_flex(el_val_t handle, el_val_t flex);
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
/* Layout / tree */
void __widget_add_child(el_val_t parent, el_val_t child);
void __widget_remove_child(el_val_t parent, el_val_t child);
void __widget_destroy(el_val_t handle);
/* Events */
void __widget_on_click(el_val_t handle, el_val_t fn_name);
void __widget_on_change(el_val_t handle, el_val_t fn_name);
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
/* Manifest reader */
el_val_t __manifest_read(el_val_t path);
#endif /* EL_TARGET_WIN32 */
/* ── Native widget system (iOS UIKit) ───────────────────────────────────────
* Available when compiled with -DEL_TARGET_IOS and linked with el_uikit.m.
* Widget handles are opaque int64_t slot indices; -1 = invalid.
*
* iOS lifecycle note: UIApplicationMain never returns. The el program must
* store its UI-build logic in a void(*)(void) function pointer, assign it to
* el_main_entry_fn, then call __native_run_loop. ElAppDelegate invokes
* el_main_entry_fn inside didFinishLaunchingWithOptions.
* Call el_uikit_set_args(argc, argv) from main() before __native_run_loop. */
#ifdef EL_TARGET_IOS
/* Lifecycle entry-function hook — set before calling __native_run_loop. */
extern void (*el_main_entry_fn)(void);
/* Forward argc/argv from main() to UIApplicationMain. */
void el_uikit_set_args(int argc, char** argv);
/* Initialisation */
void __native_init(void);
void __native_run_loop(void);
/* Window */
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
el_val_t min_width, el_val_t min_height);
void __window_show(el_val_t handle);
void __window_set_title(el_val_t handle, el_val_t title);
/* Layout containers */
el_val_t __vstack_create(el_val_t spacing);
el_val_t __hstack_create(el_val_t spacing);
el_val_t __zstack_create(void);
el_val_t __scroll_create(void);
/* Widgets */
el_val_t __label_create(el_val_t text);
el_val_t __button_create(el_val_t label);
el_val_t __text_field_create(el_val_t placeholder);
el_val_t __text_area_create(el_val_t placeholder);
el_val_t __image_create(el_val_t path_or_name);
/* Widget properties */
void __widget_set_text(el_val_t handle, el_val_t text);
el_val_t __widget_get_text(el_val_t handle);
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_font(el_val_t handle, el_val_t family,
el_val_t size, el_val_t bold);
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
el_val_t bottom, el_val_t left);
void __widget_set_width(el_val_t handle, el_val_t width);
void __widget_set_height(el_val_t handle, el_val_t height);
void __widget_set_flex(el_val_t handle, el_val_t flex);
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
/* Layout / tree */
void __widget_add_child(el_val_t parent, el_val_t child);
void __widget_remove_child(el_val_t parent, el_val_t child);
void __widget_destroy(el_val_t handle);
/* Events */
void __widget_on_click(el_val_t handle, el_val_t fn_name);
void __widget_on_change(el_val_t handle, el_val_t fn_name);
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
/* Manifest reader */
el_val_t __manifest_read(el_val_t path);
#endif /* EL_TARGET_IOS */
/* ── Native widget system (Android JNI) ─────────────────────────────────────
* Available when compiled with -DEL_TARGET_ANDROID and linked with
* libelruntime.so (which includes el_android.c compiled by the NDK build).
* Widget handles are opaque int64_t slot indices; -1 = invalid.
*
* Java companion: ElBridge.java (package com.neuron.el) must be compiled into
* the APK. The Activity must call ElBridge.init(this) before any widget ops.
*
* Link flags (in Android.mk or CMakeLists.txt):
* -landroid -llog -ldl */
#ifdef EL_TARGET_ANDROID
/* Initialisation */
void __native_init(void);
void __native_run_loop(void); /* no-op on Android */
/* Window */
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
el_val_t min_width, el_val_t min_height);
void __window_show(el_val_t handle);
void __window_set_title(el_val_t handle, el_val_t title);
/* Layout containers */
el_val_t __vstack_create(el_val_t spacing);
el_val_t __hstack_create(el_val_t spacing);
el_val_t __zstack_create(void);
el_val_t __scroll_create(void);
/* Widgets */
el_val_t __label_create(el_val_t text);
el_val_t __button_create(el_val_t label);
el_val_t __text_field_create(el_val_t placeholder);
el_val_t __text_area_create(el_val_t placeholder);
el_val_t __image_create(el_val_t path_or_name);
/* Widget properties */
void __widget_set_text(el_val_t handle, el_val_t text);
el_val_t __widget_get_text(el_val_t handle);
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_font(el_val_t handle, el_val_t family,
el_val_t size, el_val_t bold);
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
el_val_t bottom, el_val_t left);
void __widget_set_width(el_val_t handle, el_val_t width);
void __widget_set_height(el_val_t handle, el_val_t height);
void __widget_set_flex(el_val_t handle, el_val_t flex);
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
/* Layout / tree */
void __widget_add_child(el_val_t parent, el_val_t child);
void __widget_remove_child(el_val_t parent, el_val_t child);
void __widget_destroy(el_val_t handle);
/* Events */
void __widget_on_click(el_val_t handle, el_val_t fn_name);
void __widget_on_change(el_val_t handle, el_val_t fn_name);
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
/* Manifest reader */
el_val_t __manifest_read(el_val_t path);
#endif /* EL_TARGET_ANDROID */
/* ── Native widget system (LVGL v9 — embedded / microcontroller) ─────────────
* Available when compiled with -DEL_TARGET_LVGL and linked with el_lvgl.c
* and the LVGL library (lvgl.a or lvgl source tree).
*
* Target platforms: ESP32, STM32, industrial panels. Any system with 256KB+
* RAM and an LVGL-compatible display driver. No OS required.
*
* Widget handles are opaque int64_t slot indices; -1 = invalid.
*
* Bare-metal / no dynamic linker:
* Compile with -DEL_LVGL_NO_DLSYM and provide:
* el_val_t el_lvgl_dispatch(const char *fn, el_val_t a, el_val_t b);
*
* Compile:
* gcc -DEL_TARGET_LVGL -I./lvgl el_lvgl.c -c -o el_lvgl.o
* # Then link with lvgl.a. */
#ifdef EL_TARGET_LVGL
/* Initialisation */
void __native_init(void);
void __native_run_loop(void);
/* Window */
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
el_val_t min_width, el_val_t min_height);
void __window_show(el_val_t handle);
void __window_set_title(el_val_t handle, el_val_t title);
/* Layout containers */
el_val_t __vstack_create(el_val_t spacing);
el_val_t __hstack_create(el_val_t spacing);
el_val_t __zstack_create(void);
el_val_t __scroll_create(void);
/* Widgets */
el_val_t __label_create(el_val_t text);
el_val_t __button_create(el_val_t label);
el_val_t __text_field_create(el_val_t placeholder);
el_val_t __text_area_create(el_val_t placeholder);
el_val_t __image_create(el_val_t path_or_name);
/* Widget properties */
void __widget_set_text(el_val_t handle, el_val_t text);
el_val_t __widget_get_text(el_val_t handle);
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_font(el_val_t handle, el_val_t family,
el_val_t size, el_val_t bold);
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
el_val_t bottom, el_val_t left);
void __widget_set_width(el_val_t handle, el_val_t width);
void __widget_set_height(el_val_t handle, el_val_t height);
void __widget_set_flex(el_val_t handle, el_val_t flex);
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
/* Layout / tree */
void __widget_add_child(el_val_t parent, el_val_t child);
void __widget_remove_child(el_val_t parent, el_val_t child);
void __widget_destroy(el_val_t handle);
/* Events */
void __widget_on_click(el_val_t handle, el_val_t fn_name);
void __widget_on_change(el_val_t handle, el_val_t fn_name);
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
/* Manifest reader — same JSON output as all other native targets */
el_val_t __manifest_read(el_val_t path);
#endif /* EL_TARGET_LVGL */
/* ── Native widget system (SDL2 — embedded / Pi) ────────────────────────────
* Available when compiled with -DEL_TARGET_SDL2 and linked with el_sdl2.c.
* Widget handles are opaque int64_t slot indices; -1 = invalid.
*
* Target: Raspberry Pi Zero, embedded Linux, any system with a framebuffer
* and SDL2 available. No GTK, no desktop environment required.
*
* Compile:
* gcc -DEL_TARGET_SDL2 $(sdl2-config --cflags) -c el_sdl2.c -o el_sdl2.o
* Link:
* $(sdl2-config --libs) -lSDL2_ttf -lSDL2_image -ldl */
#ifdef EL_TARGET_SDL2
/* Initialisation */
void __native_init(void);
void __native_run_loop(void);
/* Window */
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
el_val_t min_width, el_val_t min_height);
void __window_show(el_val_t handle);
void __window_set_title(el_val_t handle, el_val_t title);
/* Layout containers */
el_val_t __vstack_create(el_val_t spacing);
el_val_t __hstack_create(el_val_t spacing);
el_val_t __zstack_create(void);
el_val_t __scroll_create(void);
/* Widgets */
el_val_t __label_create(el_val_t text);
el_val_t __button_create(el_val_t label);
el_val_t __text_field_create(el_val_t placeholder);
el_val_t __text_area_create(el_val_t placeholder);
el_val_t __image_create(el_val_t path_or_name);
/* Widget properties */
void __widget_set_text(el_val_t handle, el_val_t text);
el_val_t __widget_get_text(el_val_t handle);
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
el_val_t b, el_val_t a);
void __widget_set_font(el_val_t handle, el_val_t family,
el_val_t size, el_val_t bold);
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
el_val_t bottom, el_val_t left);
void __widget_set_width(el_val_t handle, el_val_t width);
void __widget_set_height(el_val_t handle, el_val_t height);
void __widget_set_flex(el_val_t handle, el_val_t flex);
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
/* Layout / tree */
void __widget_add_child(el_val_t parent, el_val_t child);
void __widget_remove_child(el_val_t parent, el_val_t child);
void __widget_destroy(el_val_t handle);
/* Events */
void __widget_on_click(el_val_t handle, el_val_t fn_name);
void __widget_on_change(el_val_t handle, el_val_t fn_name);
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
/* Manifest reader */
el_val_t __manifest_read(el_val_t path);
#endif /* EL_TARGET_SDL2 */
@@ -0,0 +1,459 @@
#include <stdint.h>
#include <stdlib.h>
#include "el_runtime.h"
el_val_t native_init(void);
el_val_t native_run_loop(void);
el_val_t manifest_read(el_val_t path);
el_val_t manifest_title(el_val_t m);
el_val_t manifest_width(el_val_t m);
el_val_t manifest_height(el_val_t m);
el_val_t manifest_min_width(el_val_t m);
el_val_t manifest_min_height(el_val_t m);
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
el_val_t window_from_manifest(el_val_t manifest_path);
el_val_t window_show(el_val_t handle);
el_val_t window_set_title(el_val_t handle, el_val_t title);
el_val_t vstack(el_val_t spacing);
el_val_t vstack_tight(void);
el_val_t hstack(el_val_t spacing);
el_val_t zstack(void);
el_val_t scroll(void);
el_val_t label(el_val_t text);
el_val_t button(el_val_t label);
el_val_t text_field(el_val_t placeholder);
el_val_t text_area(el_val_t placeholder);
el_val_t image(el_val_t path_or_name);
el_val_t widget_set_text(el_val_t handle, el_val_t text);
el_val_t widget_get_text(el_val_t handle);
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
el_val_t widget_set_width(el_val_t handle, el_val_t width);
el_val_t widget_set_height(el_val_t handle, el_val_t height);
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
el_val_t widget_add_child(el_val_t parent, el_val_t child);
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
el_val_t widget_destroy(el_val_t handle);
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
el_val_t hex_channel(el_val_t s, el_val_t offset);
el_val_t hex_nibble(el_val_t c);
el_val_t color_hex_r(el_val_t hex);
el_val_t color_hex_g(el_val_t hex);
el_val_t color_hex_b(el_val_t hex);
el_val_t color_hex_a(el_val_t hex);
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
el_val_t style_surface(el_val_t handle);
el_val_t style_button_primary(el_val_t handle);
el_val_t style_label_body(el_val_t handle);
el_val_t style_label_heading(el_val_t handle);
el_val_t style_label_muted(el_val_t handle);
el_val_t TOKEN_PRIMARY;
el_val_t TOKEN_ON_PRIMARY;
el_val_t TOKEN_BACKGROUND;
el_val_t TOKEN_ON_BG;
el_val_t TOKEN_SURFACE;
el_val_t TOKEN_ON_SURFACE;
el_val_t TOKEN_OUTLINE;
el_val_t TOKEN_ERROR;
el_val_t native_init(void) {
__native_init();
return 0;
}
el_val_t native_run_loop(void) {
__native_run_loop();
return 0;
}
el_val_t manifest_read(el_val_t path) {
return __manifest_read(path);
return 0;
}
el_val_t manifest_title(el_val_t m) {
return json_get_string(m, EL_STR("title"));
return 0;
}
el_val_t manifest_width(el_val_t m) {
return json_get_int(m, EL_STR("width"));
return 0;
}
el_val_t manifest_height(el_val_t m) {
return json_get_int(m, EL_STR("height"));
return 0;
}
el_val_t manifest_min_width(el_val_t m) {
return json_get_int(m, EL_STR("min_width"));
return 0;
}
el_val_t manifest_min_height(el_val_t m) {
return json_get_int(m, EL_STR("min_height"));
return 0;
}
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) {
return __window_create(title, width, height, min_width, min_height);
return 0;
}
el_val_t window_from_manifest(el_val_t manifest_path) {
el_val_t m = manifest_read(manifest_path);
el_val_t title = manifest_title(m);
el_val_t w = manifest_width(m);
el_val_t h = manifest_height(m);
el_val_t mw = manifest_min_width(m);
el_val_t mh = manifest_min_height(m);
el_val_t safe_w = ({ el_val_t _if_result_1 = 0; if ((w > 0)) { _if_result_1 = (w); } else { _if_result_1 = (1200); } _if_result_1; });
el_val_t safe_h = ({ el_val_t _if_result_2 = 0; if ((h > 0)) { _if_result_2 = (h); } else { _if_result_2 = (800); } _if_result_2; });
el_val_t safe_mw = ({ el_val_t _if_result_3 = 0; if ((mw > 0)) { _if_result_3 = (mw); } else { _if_result_3 = (600); } _if_result_3; });
el_val_t safe_mh = ({ el_val_t _if_result_4 = 0; if ((mh > 0)) { _if_result_4 = (mh); } else { _if_result_4 = (400); } _if_result_4; });
el_val_t safe_t = ({ el_val_t _if_result_5 = 0; if ((str_len(title) > 0)) { _if_result_5 = (title); } else { _if_result_5 = (EL_STR("App")); } _if_result_5; });
return window_create(safe_t, safe_w, safe_h, safe_mw, safe_mh);
return 0;
}
el_val_t window_show(el_val_t handle) {
__window_show(handle);
return 0;
}
el_val_t window_set_title(el_val_t handle, el_val_t title) {
__window_set_title(handle, title);
return 0;
}
el_val_t vstack(el_val_t spacing) {
return __vstack_create(spacing);
return 0;
}
el_val_t vstack_tight(void) {
return __vstack_create(0);
return 0;
}
el_val_t hstack(el_val_t spacing) {
return __hstack_create(spacing);
return 0;
}
el_val_t zstack(void) {
return __zstack_create();
return 0;
}
el_val_t scroll(void) {
return __scroll_create();
return 0;
}
el_val_t label(el_val_t text) {
return __label_create(text);
return 0;
}
el_val_t button(el_val_t label) {
return __button_create(label);
return 0;
}
el_val_t text_field(el_val_t placeholder) {
return __text_field_create(placeholder);
return 0;
}
el_val_t text_area(el_val_t placeholder) {
return __text_area_create(placeholder);
return 0;
}
el_val_t image(el_val_t path_or_name) {
return __image_create(path_or_name);
return 0;
}
el_val_t widget_set_text(el_val_t handle, el_val_t text) {
__widget_set_text(handle, text);
return 0;
}
el_val_t widget_get_text(el_val_t handle) {
return __widget_get_text(handle);
return 0;
}
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
__widget_set_color(handle, r, g, b, a);
return 0;
}
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
__widget_set_bg_color(handle, r, g, b, a);
return 0;
}
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold) {
el_val_t bold_int = ({ el_val_t _if_result_6 = 0; if (bold) { _if_result_6 = (1); } else { _if_result_6 = (0); } _if_result_6; });
__widget_set_font(handle, family, size, bold_int);
return 0;
}
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) {
__widget_set_padding(handle, top, right, bottom, left);
return 0;
}
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p) {
__widget_set_padding(handle, p, p, p, p);
return 0;
}
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py) {
__widget_set_padding(handle, py, px, py, px);
return 0;
}
el_val_t widget_set_width(el_val_t handle, el_val_t width) {
__widget_set_width(handle, width);
return 0;
}
el_val_t widget_set_height(el_val_t handle, el_val_t height) {
__widget_set_height(handle, height);
return 0;
}
el_val_t widget_set_flex(el_val_t handle, el_val_t flex) {
__widget_set_flex(handle, flex);
return 0;
}
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius) {
__widget_set_corner_radius(handle, radius);
return 0;
}
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled) {
el_val_t d = ({ el_val_t _if_result_7 = 0; if (disabled) { _if_result_7 = (1); } else { _if_result_7 = (0); } _if_result_7; });
__widget_set_disabled(handle, d);
return 0;
}
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden) {
el_val_t h = ({ el_val_t _if_result_8 = 0; if (hidden) { _if_result_8 = (1); } else { _if_result_8 = (0); } _if_result_8; });
__widget_set_hidden(handle, h);
return 0;
}
el_val_t widget_add_child(el_val_t parent, el_val_t child) {
__widget_add_child(parent, child);
return 0;
}
el_val_t widget_remove_child(el_val_t parent, el_val_t child) {
__widget_remove_child(parent, child);
return 0;
}
el_val_t widget_destroy(el_val_t handle) {
__widget_destroy(handle);
return 0;
}
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name) {
__widget_on_click(handle, fn_name);
return 0;
}
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name) {
__widget_on_change(handle, fn_name);
return 0;
}
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name) {
__widget_on_submit(handle, fn_name);
return 0;
}
el_val_t hex_channel(el_val_t s, el_val_t offset) {
el_val_t hi = str_slice(s, offset, (offset + 1));
el_val_t lo = str_slice(s, (offset + 1), (offset + 2));
el_val_t h = hex_nibble(hi);
el_val_t l = hex_nibble(lo);
return ((h * 16) + l);
return 0;
}
el_val_t hex_nibble(el_val_t c) {
if (str_eq(c, EL_STR("0"))) {
return 0;
}
if (str_eq(c, EL_STR("1"))) {
return 1;
}
if (str_eq(c, EL_STR("2"))) {
return 2;
}
if (str_eq(c, EL_STR("3"))) {
return 3;
}
if (str_eq(c, EL_STR("4"))) {
return 4;
}
if (str_eq(c, EL_STR("5"))) {
return 5;
}
if (str_eq(c, EL_STR("6"))) {
return 6;
}
if (str_eq(c, EL_STR("7"))) {
return 7;
}
if (str_eq(c, EL_STR("8"))) {
return 8;
}
if (str_eq(c, EL_STR("9"))) {
return 9;
}
if (str_eq(c, EL_STR("a"))) {
return 10;
}
if (str_eq(c, EL_STR("b"))) {
return 11;
}
if (str_eq(c, EL_STR("c"))) {
return 12;
}
if (str_eq(c, EL_STR("d"))) {
return 13;
}
if (str_eq(c, EL_STR("e"))) {
return 14;
}
if (str_eq(c, EL_STR("f"))) {
return 15;
}
if (str_eq(c, EL_STR("A"))) {
return 10;
}
if (str_eq(c, EL_STR("B"))) {
return 11;
}
if (str_eq(c, EL_STR("C"))) {
return 12;
}
if (str_eq(c, EL_STR("D"))) {
return 13;
}
if (str_eq(c, EL_STR("E"))) {
return 14;
}
if (str_eq(c, EL_STR("F"))) {
return 15;
}
return 0;
return 0;
}
el_val_t color_hex_r(el_val_t hex) {
el_val_t s = ({ el_val_t _if_result_9 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_9 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_9 = (hex); } _if_result_9; });
el_val_t v = hex_channel(s, 0);
return float_div(int_to_float(v), el_from_float(255.0));
return 0;
}
el_val_t color_hex_g(el_val_t hex) {
el_val_t s = ({ el_val_t _if_result_10 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_10 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_10 = (hex); } _if_result_10; });
el_val_t v = hex_channel(s, 2);
return float_div(int_to_float(v), el_from_float(255.0));
return 0;
}
el_val_t color_hex_b(el_val_t hex) {
el_val_t s = ({ el_val_t _if_result_11 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_11 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_11 = (hex); } _if_result_11; });
el_val_t v = hex_channel(s, 4);
return float_div(int_to_float(v), el_from_float(255.0));
return 0;
}
el_val_t color_hex_a(el_val_t hex) {
el_val_t s = ({ el_val_t _if_result_12 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_12 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_12 = (hex); } _if_result_12; });
if (str_len(s) < 8) {
return el_from_float(1.0);
}
el_val_t v = hex_channel(s, 6);
return float_div(int_to_float(v), el_from_float(255.0));
return 0;
}
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex) {
widget_set_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
return 0;
}
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex) {
widget_set_bg_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
return 0;
}
el_val_t style_surface(el_val_t handle) {
widget_set_bg_color_hex(handle, TOKEN_SURFACE);
widget_set_corner_radius(handle, 8);
return 0;
}
el_val_t style_button_primary(el_val_t handle) {
widget_set_bg_color_hex(handle, TOKEN_PRIMARY);
widget_set_color_hex(handle, TOKEN_ON_PRIMARY);
widget_set_corner_radius(handle, 8);
widget_set_padding_xy(handle, 16, 8);
return 0;
}
el_val_t style_label_body(el_val_t handle) {
widget_set_color_hex(handle, TOKEN_ON_BG);
widget_set_font(handle, EL_STR("system"), 14, 0);
return 0;
}
el_val_t style_label_heading(el_val_t handle) {
widget_set_color_hex(handle, TOKEN_ON_BG);
widget_set_font(handle, EL_STR("system"), 20, 1);
return 0;
}
el_val_t style_label_muted(el_val_t handle) {
widget_set_color_hex(handle, TOKEN_OUTLINE);
widget_set_font(handle, EL_STR("system"), 12, 0);
return 0;
}
int el_vessel_main(int _argc, char** _argv) {
el_runtime_init_args(_argc, _argv);
TOKEN_PRIMARY = EL_STR("#60a5fa");
TOKEN_ON_PRIMARY = EL_STR("#0f172a");
TOKEN_BACKGROUND = EL_STR("#0f172a");
TOKEN_ON_BG = EL_STR("#f8fafc");
TOKEN_SURFACE = EL_STR("#1e293b");
TOKEN_ON_SURFACE = EL_STR("#f8fafc");
TOKEN_OUTLINE = EL_STR("#475569");
TOKEN_ERROR = EL_STR("#f87171");
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,883 @@
/*
* el_runtime.h — El language C runtime header
*
* Declares all built-in functions available to compiled El programs.
* Include this in every generated .c file.
*
* Value model:
* All El values are represented as el_val_t (= int64_t).
* On 64-bit systems a pointer fits in int64_t.
* String values are cast: (el_val_t)(uintptr_t)"hello"
* Integer values are stored directly.
* This lets arithmetic work naturally while still passing strings around.
*
* Type conventions (El -> C):
* String -> el_val_t (holds const char* via uintptr_t cast)
* Int -> el_val_t
* Bool -> el_val_t (0 = false, nonzero = true)
* Any -> el_val_t
* Void -> void
*
* Macros for convenience:
* EL_STR(s) cast string literal to el_val_t
* EL_CSTR(v) cast el_val_t back to const char*
* EL_INT(v) identity — el_val_t is already int64_t
* EL_NULL null / zero value
* EL_FALSE boolean false (0)
* EL_TRUE boolean true (1)
*
* Link requirements:
* -lcurl — required for the HTTP client (http_get, http_post, llm_*).
* -lpthread — required for the HTTP server (one detached thread per
* connection, capped at 64 concurrent).
* -loqs — optional; required only when liboqs is installed and the
* pq_* / sha3_256_hex entry points are needed. Detected at
* compile time via __has_include(<oqs/oqs.h>).
* -lcrypto — optional; pulled in alongside -loqs. Used for X25519 in
* pq_hybrid_* and HKDF-SHA256 derivation.
*
* Canonical compile command:
* cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
* -o <out> <prog>.c el-compiler/runtime/el_runtime.c
*
* With liboqs (post-quantum stack):
* cc -std=c11 -I el-compiler/runtime -lcurl -lpthread -loqs -lcrypto \
* -o <out> <prog>.c el-compiler/runtime/el_runtime.c
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
typedef int64_t el_val_t;
#define EL_STR(s) ((el_val_t)(uintptr_t)(s))
#define EL_CSTR(v) ((const char*)(uintptr_t)(v))
#define EL_INT(v) (v)
#define EL_NULL ((el_val_t)0)
#define EL_FALSE ((el_val_t)0)
#define EL_TRUE ((el_val_t)1)
/* Float values share the el_val_t (int64) slot via a bit-cast.
* The codegen emits Float literals as `el_from_float(<dbl>)` so the
* underlying bits represent the IEEE 754 double. Float-aware builtins
* (math, format, json) round-trip via these helpers. */
static inline double el_to_float(el_val_t v) {
union { int64_t i; double f; } u;
u.i = (int64_t)v;
return u.f;
}
static inline el_val_t el_from_float(double f) {
union { double f; int64_t i; } u;
u.f = f;
return (el_val_t)u.i;
}
#ifdef __cplusplus
extern "C" {
#endif
/* ── I/O ──────────────────────────────────────────────────────────────────── */
el_val_t println(el_val_t s);
el_val_t print(el_val_t s);
el_val_t readline(void);
/* ── String builtins ─────────────────────────────────────────────────────── */
el_val_t el_str_concat(el_val_t a, el_val_t b);
el_val_t str_eq(el_val_t a, el_val_t b);
el_val_t str_starts_with(el_val_t s, el_val_t prefix);
el_val_t str_ends_with(el_val_t s, el_val_t suffix);
el_val_t str_len(el_val_t s);
el_val_t str_concat(el_val_t a, el_val_t b);
el_val_t int_to_str(el_val_t n);
el_val_t str_to_int(el_val_t s);
el_val_t native_str_to_int(el_val_t s);
el_val_t str_slice(el_val_t s, el_val_t start, el_val_t end);
el_val_t str_contains(el_val_t s, el_val_t sub);
el_val_t str_replace(el_val_t s, el_val_t from, el_val_t to);
el_val_t str_to_upper(el_val_t s);
el_val_t str_to_lower(el_val_t s);
el_val_t str_trim(el_val_t s);
/* ── Math ────────────────────────────────────────────────────────────────── */
el_val_t el_abs(el_val_t n);
el_val_t el_max(el_val_t a, el_val_t b);
el_val_t el_min(el_val_t a, el_val_t b);
/* ── Refcount (ARC) ──────────────────────────────────────────────────────────
* Lists and Maps carry a refcount. Strings and ints do not — el_retain and
* el_release are safe no-ops on non-refcounted values (they sniff a magic
* header at offset 0 and only act if the magic matches).
*
* Codegen emits these at let-binding shadowing, function entry (params), and
* function exit (locals other than the returned value). The refcount lets
* el_list_append and el_map_set mutate in place when uniquely owned (cheap)
* and copy-on-write when shared (preserves persistent semantics across
* accumulator patterns in the compiler itself). */
void el_retain(el_val_t v);
void el_release(el_val_t v);
/* ── Scoped arena (CLI use) ───────────────────────────────────────────────── */
el_val_t el_arena_push(void);
el_val_t el_arena_pop(el_val_t mark);
/* ── List ────────────────────────────────────────────────────────────────── */
el_val_t el_list_new(el_val_t count, ...);
el_val_t el_list_len(el_val_t list);
el_val_t el_list_get(el_val_t list, el_val_t index);
el_val_t el_list_append(el_val_t list, el_val_t elem);
el_val_t el_list_empty(void);
el_val_t el_list_clone(el_val_t list);
/* ── Map ─────────────────────────────────────────────────────────────────── */
el_val_t el_map_new(el_val_t pair_count, ...);
el_val_t el_get_field(el_val_t map, el_val_t key);
el_val_t el_map_get(el_val_t map, el_val_t key);
el_val_t el_map_set(el_val_t map, el_val_t key, el_val_t value);
/* ── HTTP ─────────────────────────────────────────────────────────────────── */
el_val_t http_get(el_val_t url);
el_val_t http_post(el_val_t url, el_val_t body);
el_val_t http_post_json(el_val_t url, el_val_t json_body);
el_val_t http_get_with_headers(el_val_t url, el_val_t headers_map);
el_val_t http_post_with_headers(el_val_t url, el_val_t body, el_val_t headers_map);
el_val_t http_post_json_with_headers(el_val_t url, el_val_t headers_map, el_val_t json_body);
el_val_t http_post_form_auth(el_val_t url, el_val_t form_body, el_val_t auth_header);
el_val_t http_delete(el_val_t url);
el_val_t http_serve(el_val_t port, el_val_t handler);
el_val_t http_set_handler(el_val_t name);
/* HTTP server v2 ─────────────────────────────────────────────────────────────
* Same dispatch model as http_serve, but the handler signature is widened:
*
* el_val_t handler(method, path, headers_map, body)
*
* `headers_map` is an ElMap from lowercased header name → header value (both
* Strings). Repeated headers are joined with ", " per RFC 7230.
*
* Response value: the handler may return either
* (a) a plain body string — same auto-content-type / 200-OK behaviour as
* http_serve (3-arg) — or
* (b) a response envelope built with `http_response(status, headers_json,
* body)`. The runtime detects the envelope discriminator
* `"el_http_response":1` at the start of the returned string and
* unpacks status / headers / body before sending.
*
* The 3-arg http_serve(port, handler) remains supported unchanged for
* existing handlers (e.g. products/web/server.el): it dispatches with
* (method, path, body), hardcodes 200 OK, and auto-detects content type. */
el_val_t http_serve_v2(el_val_t port, el_val_t handler);
el_val_t http_set_handler_v2(el_val_t name);
/* Build an HTTP response envelope. `headers_json` should be a JSON object
* literal like `{"WWW-Authenticate":"Basic"}` (or "" / "{}" for none). The
* returned string carries the discriminator `{"el_http_response":1,...}`
* which the runtime's send-path detects and unpacks. Detection happens
* uniformly inside http_send_response, so a 3-arg handler may also return
* an envelope. The 3-arg variant remains documented as a fixed 200-OK
* auto-content-type contract for legacy handlers that return plain bodies. */
el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body);
/* SSE connection fd — set by http_worker_v2 before calling the El handler,
* cleared afterwards. Defined in el_seed.c; called from el_runtime.c.
* The getter is exposed as __http_conn_fd() to El programs. */
void el_seed_set_http_conn_fd(int fd);
/* HTTP timeout — every libcurl request honors EL_HTTP_TIMEOUT_MS (default
* 60000ms). Read lazily on first use, so setting the env var any time before
* the first http_* call is sufficient. */
/* Streaming variants — write the response body straight to a file via
* libcurl's CURLOPT_WRITEFUNCTION = fwrite. These bypass the el_val_t string
* wrapper entirely, so binary payloads (audio/mpeg, image/png, etc.) survive
* embedded NUL bytes that would truncate a strlen()-based code path.
*
* Both honor EL_HTTP_TIMEOUT_MS, follow redirects, and accept the same
* `headers_map` shape as http_post_with_headers (ElMap of String→String).
*
* Return value: 1 on success (file fully written), 0 on any failure
* (network, file open, partial write). On failure the output file is removed
* so callers cannot mistake a partially-written file for a valid one. */
el_val_t http_post_to_file(el_val_t url, el_val_t body, el_val_t headers_map, el_val_t output_path);
el_val_t http_get_to_file(el_val_t url, el_val_t headers_map, el_val_t output_path);
/* ── URL encoding ────────────────────────────────────────────────────────── */
el_val_t url_encode(el_val_t s); /* RFC 3986 unreserved set */
el_val_t url_decode(el_val_t s); /* '+' → space, %XX → byte */
/* ── HTML allowlist sanitizer ────────────────────────────────────────────────
* el_html_sanitize(input_html, allowlist_json) — strict allowlist HTML
* cleaner. State-machine parser; tag/attribute names compared case-
* insensitively against the allowlist; `<a href>` / `<… src>` URL schemes
* validated (http, https, mailto, fragment-only, or relative); whole-
* subtree drop for script / style / iframe / object / embed / form; HTML-
* escapes free text outside dropped subtrees.
*
* The allowlist is JSON of the form
* {"p":[],"a":["href","title"],"strong":[],...}
* where each value is the array of attribute names allowed for that tag. */
el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json);
el_val_t html_raw(el_val_t s);
el_val_t html_escape(el_val_t s);
/* ── Filesystem ──────────────────────────────────────────────────────────── */
el_val_t fs_read(el_val_t path);
el_val_t fs_write(el_val_t path, el_val_t content);
el_val_t fs_list(el_val_t path);
el_val_t fs_list_json(el_val_t path);
el_val_t fs_exists(el_val_t path);
el_val_t fs_mkdir(el_val_t path); /* mkdir -p, mode 0755 */
/* Length-explicit binary write. `length` is an Int (el_val_t holding the
* byte count). The caller knows the length from context — typically because
* `bytes` came from base64_decode (which produces a magic-tagged binary
* buffer with embedded NULs possible) and the caller already tracks the
* decoded length, OR because the bytes came from a fixed-size source
* (sha256_bytes = 32, hmac_sha256_bytes = 32). Bypasses strlen entirely.
*
* Returns 1 on success, 0 on failure (invalid path, can't open, partial
* write, negative length). On partial-write failure, the file is removed
* so callers cannot read back a truncated artefact. */
el_val_t fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t length);
/* ── JSON ────────────────────────────────────────────────────────────────── */
el_val_t json_get(el_val_t json, el_val_t key);
el_val_t json_parse(el_val_t s);
el_val_t json_stringify(el_val_t v);
el_val_t json_get_string(el_val_t json_str, el_val_t key);
el_val_t json_get_int(el_val_t json_str, el_val_t key);
el_val_t json_get_float(el_val_t json_str, el_val_t key);
el_val_t json_get_bool(el_val_t json_str, el_val_t key);
el_val_t json_get_raw(el_val_t json_str, el_val_t key);
el_val_t json_set(el_val_t json_str, el_val_t key, el_val_t value);
el_val_t json_array_len(el_val_t json_str);
el_val_t json_array_get(el_val_t json_str, el_val_t index);
el_val_t json_array_get_string(el_val_t json_str, el_val_t index);
el_val_t json_escape_string(el_val_t sv);
el_val_t json_build_object(el_val_t kvs);
el_val_t json_build_array(el_val_t items);
/* ── Time ────────────────────────────────────────────────────────────────── */
el_val_t time_now(void);
el_val_t time_now_utc(void);
el_val_t sleep_secs(el_val_t secs);
el_val_t sleep_ms(el_val_t ms);
el_val_t time_format(el_val_t ts, el_val_t fmt);
el_val_t time_to_parts(el_val_t ts);
el_val_t time_from_parts(el_val_t secs, el_val_t ns, el_val_t tz);
el_val_t time_add(el_val_t ts, el_val_t n, el_val_t unit);
el_val_t time_diff(el_val_t ts1, el_val_t ts2, el_val_t unit);
el_val_t now_ns(void);
/* ── Instant + Duration: first-class temporal types ──────────────────────────
* Both types share the el_val_t (int64) slot. Instants are nanoseconds
* since the Unix epoch; Durations are signed nanoseconds. Type discipline
* is enforced at codegen-time: BinOps on names registered as Instant or
* Duration route through the typed wrappers below; mismatches like
* Instant+Instant become #error at the C compiler.
*
* Postfix literals — `30.seconds`, `1.hour`, `500.millis`, `30.nanos` — are
* recognised by the parser as DurationLit AST nodes and lowered to literal
* int64 nanoseconds at codegen time. The runtime never sees the units. */
el_val_t el_now_instant(void);
el_val_t now(void);
el_val_t unix_seconds(el_val_t n);
el_val_t unix_millis(el_val_t n);
el_val_t instant_from_iso8601(el_val_t s);
el_val_t el_duration_from_nanos(el_val_t ns);
el_val_t duration_seconds(el_val_t n);
el_val_t duration_millis(el_val_t n);
el_val_t duration_nanos(el_val_t n);
el_val_t el_instant_add_dur(el_val_t inst, el_val_t dur);
el_val_t el_instant_sub_dur(el_val_t inst, el_val_t dur);
el_val_t el_instant_diff(el_val_t a, el_val_t b);
el_val_t el_duration_add(el_val_t a, el_val_t b);
el_val_t el_duration_sub(el_val_t a, el_val_t b);
el_val_t el_duration_scale(el_val_t dur, el_val_t scalar);
el_val_t el_duration_div(el_val_t dur, el_val_t scalar);
el_val_t el_instant_lt(el_val_t a, el_val_t b);
el_val_t el_instant_le(el_val_t a, el_val_t b);
el_val_t el_instant_gt(el_val_t a, el_val_t b);
el_val_t el_instant_ge(el_val_t a, el_val_t b);
el_val_t el_instant_eq(el_val_t a, el_val_t b);
el_val_t el_instant_ne(el_val_t a, el_val_t b);
el_val_t el_duration_lt(el_val_t a, el_val_t b);
el_val_t el_duration_le(el_val_t a, el_val_t b);
el_val_t el_duration_gt(el_val_t a, el_val_t b);
el_val_t el_duration_ge(el_val_t a, el_val_t b);
el_val_t el_duration_eq(el_val_t a, el_val_t b);
el_val_t el_duration_ne(el_val_t a, el_val_t b);
el_val_t instant_to_unix_seconds(el_val_t i);
el_val_t instant_to_unix_millis(el_val_t i);
el_val_t instant_to_iso8601(el_val_t i);
el_val_t duration_to_seconds(el_val_t d);
el_val_t duration_to_millis(el_val_t d);
el_val_t duration_to_nanos(el_val_t d);
el_val_t el_sleep_duration(el_val_t dur);
el_val_t unix_timestamp(void);
el_val_t ttl_cache_set(el_val_t key, el_val_t value);
el_val_t ttl_cache_get(el_val_t key, el_val_t max_age);
el_val_t ttl_cache_age(el_val_t key);
/* ── Calendar + CalendarTime + Rhythm + LocalDate/Time/DateTime ─────────────
* Phase 1.5 of the time system. Calendar is pluggable: EarthCalendar (IANA
* zones, Gregorian, DST) is the user-facing default; MarsCalendar,
* CycleCalendar(period), NoCycleCalendar, RelativeCalendar handle non-Earth
* domains.
*
* A Calendar interprets an Instant under a particular cycle convention and
* produces a CalendarTime. CalendarTime carries the underlying Instant and
* a back-pointer to its Calendar; arithmetic and formatting consult the
* Calendar to convert ns since epoch into year/month/day/hour/minute/second
* (or sol/phase, or cycle/phase, depending on kind).
*
* Storage convention: Calendar / CalendarTime / Rhythm / LocalDate /
* LocalDateTime are heap-allocated structs whose pointers are cast into
* el_val_t. A 24-bit magic header at offset 0 lets the runtime identify
* the kind safely. LocalTime is small enough to live in the int64 slot
* directly (nanos since midnight, signed). */
/* Zone — opaque IANA zone or fixed offset, used by EarthCalendar.
* `zone_id` is either an IANA name ("America/New_York", "UTC") or a fixed
* offset string ("+05:30", "-08:00"). The runtime resolves it via tzset()
* on first use of the owning EarthCalendar. */
el_val_t zone(el_val_t id);
el_val_t zone_utc(void);
el_val_t zone_local(void);
el_val_t zone_offset(el_val_t hours, el_val_t minutes);
/* Calendar constructors. Each returns an el_val_t pointer to a heap-
* allocated, magic-tagged Calendar struct. Calendars are interned by
* (kind, zone_id, period_ns, epoch_ns) so identical constructors return
* the same pointer — equality is reference equality. */
el_val_t earth_calendar(el_val_t z);
el_val_t earth_calendar_default(void);
el_val_t mars_calendar(void);
el_val_t cycle_calendar(el_val_t period_dur);
el_val_t no_cycle_calendar(void);
el_val_t relative_calendar(el_val_t epoch_inst);
/* CalendarTime constructors and methods. Returns a heap-allocated struct
* whose pointer fits in el_val_t. */
el_val_t now_in(el_val_t cal);
el_val_t in_calendar(el_val_t inst, el_val_t cal);
el_val_t cal_format(el_val_t ct, el_val_t pattern);
el_val_t cal_to_instant(el_val_t ct);
el_val_t cal_cycle_phase(el_val_t ct);
el_val_t cal_in(el_val_t ct, el_val_t cal);
/* LocalDate / LocalTime / LocalDateTime — calendar-agnostic value types.
* LocalTime carries nanoseconds since midnight as a signed int64 directly
* in the el_val_t slot (no allocation). LocalDate / LocalDateTime are
* heap-allocated structs with magic headers. */
el_val_t local_date(el_val_t y, el_val_t m, el_val_t d);
el_val_t local_time(el_val_t h, el_val_t m, el_val_t s, el_val_t ns);
el_val_t local_datetime(el_val_t date, el_val_t time);
el_val_t zoned(el_val_t date, el_val_t time, el_val_t cal);
el_val_t local_date_year(el_val_t ld);
el_val_t local_date_month(el_val_t ld);
el_val_t local_date_day(el_val_t ld);
el_val_t local_time_hour(el_val_t lt);
el_val_t local_time_minute(el_val_t lt);
el_val_t local_time_second(el_val_t lt);
el_val_t local_time_nanos(el_val_t lt);
el_val_t el_local_date_add_dur(el_val_t ld, el_val_t dur);
el_val_t el_local_time_add_dur(el_val_t lt, el_val_t dur);
el_val_t el_local_date_lt(el_val_t a, el_val_t b);
el_val_t el_local_date_eq(el_val_t a, el_val_t b);
/* Rhythm — pluggable recurrence AST. Returns a heap-allocated struct
* pointer in el_val_t; rhythms are immutable so callers may share them. */
el_val_t rhythm_cycle_start(void);
el_val_t rhythm_cycle_phase(el_val_t phase);
el_val_t rhythm_duration(el_val_t d);
el_val_t rhythm_session_start(void);
el_val_t rhythm_event(el_val_t name);
el_val_t rhythm_and(el_val_t a, el_val_t b);
el_val_t rhythm_or(el_val_t a, el_val_t b);
el_val_t rhythm_weekday(el_val_t day);
el_val_t rhythm_weekly_at(el_val_t day, el_val_t hour, el_val_t minute);
el_val_t rhythm_next_after(el_val_t r, el_val_t after, el_val_t cal);
el_val_t rhythm_matches(el_val_t r, el_val_t ct);
/* ── UUID ────────────────────────────────────────────────────────────────── */
el_val_t uuid_new(void);
el_val_t uuid_v4(void);
/* ── Environment ─────────────────────────────────────────────────────────── */
el_val_t env(el_val_t key);
/* ── In-process state K/V ────────────────────────────────────────────────── */
el_val_t state_set(el_val_t key, el_val_t value);
el_val_t state_get(el_val_t key);
el_val_t state_del(el_val_t key);
el_val_t state_keys(void);
el_val_t state_has(el_val_t key);
el_val_t state_get_or(el_val_t key, el_val_t default_val);
/* ── Float formatting ────────────────────────────────────────────────────── */
el_val_t float_to_str(el_val_t f);
el_val_t int_to_float(el_val_t n);
el_val_t float_to_int(el_val_t f);
el_val_t format_float(el_val_t f, el_val_t decimals);
el_val_t decimal_round(el_val_t f, el_val_t decimals);
el_val_t str_to_float(el_val_t s);
/* ── Math (Float-aware) ──────────────────────────────────────────────────── */
el_val_t math_sqrt(el_val_t f);
el_val_t math_log(el_val_t f);
el_val_t math_ln(el_val_t f);
el_val_t math_sin(el_val_t f);
el_val_t math_cos(el_val_t f);
el_val_t math_pi(void);
/* ── String additions ────────────────────────────────────────────────────── */
el_val_t str_index_of(el_val_t s, el_val_t sub);
el_val_t str_split(el_val_t s, el_val_t sep);
el_val_t str_char_at(el_val_t s, el_val_t i);
el_val_t str_char_code(el_val_t s, el_val_t i);
el_val_t str_pad_left(el_val_t s, el_val_t width, el_val_t pad);
el_val_t str_pad_right(el_val_t s, el_val_t width, el_val_t pad);
el_val_t str_format(el_val_t fmt, el_val_t data);
el_val_t str_lower(el_val_t s);
el_val_t str_upper(el_val_t s);
/* ── Text-processing primitives (Phase 1: byte/codepoint, ASCII char classes)
* Phase 2 (filed): Unicode-grapheme awareness, NFC/NFD normalization, regex.
* is_* predicates: empty input returns false; multi-char requires ALL bytes
* to match. ASCII ranges only in Phase 1. */
/* Counting */
el_val_t str_count(el_val_t s, el_val_t sub); /* non-overlapping */
el_val_t str_count_chars(el_val_t s); /* codepoint count */
el_val_t str_count_bytes(el_val_t s); /* alias of str_len */
el_val_t str_count_lines(el_val_t s);
el_val_t str_count_words(el_val_t s);
el_val_t str_count_letters(el_val_t s); /* ASCII [A-Za-z] */
el_val_t str_count_digits(el_val_t s); /* ASCII [0-9] */
/* Find / position */
el_val_t str_index_of_all(el_val_t s, el_val_t sub); /* [Int] of byte offsets */
el_val_t str_last_index_of(el_val_t s, el_val_t sub);
el_val_t str_find_chars(el_val_t s, el_val_t any_of); /* first idx of any ch */
/* Transform */
el_val_t str_repeat(el_val_t s, el_val_t n);
el_val_t str_reverse(el_val_t s); /* by codepoint */
el_val_t str_strip_prefix(el_val_t s, el_val_t prefix);
el_val_t str_strip_suffix(el_val_t s, el_val_t suffix);
el_val_t str_strip_chars(el_val_t s, el_val_t chars);
el_val_t str_lstrip(el_val_t s);
el_val_t str_rstrip(el_val_t s);
/* Char classification (Bool) */
el_val_t is_letter(el_val_t s);
el_val_t is_digit(el_val_t s);
el_val_t is_alphanumeric(el_val_t s);
el_val_t is_whitespace(el_val_t s);
el_val_t is_punctuation(el_val_t s);
el_val_t is_uppercase(el_val_t s);
el_val_t is_lowercase(el_val_t s);
/* Split / join */
el_val_t str_split_lines(el_val_t s);
el_val_t str_split_chars(el_val_t s); /* alias of native_string_chars */
el_val_t str_split_n(el_val_t s, el_val_t sep, el_val_t n);
el_val_t str_join(el_val_t list, el_val_t sep); /* alias of list_join */
/* ── List additions ──────────────────────────────────────────────────────── */
el_val_t list_push(el_val_t list, el_val_t elem);
el_val_t list_push_front(el_val_t list, el_val_t elem);
el_val_t list_join(el_val_t list, el_val_t sep);
el_val_t list_range(el_val_t start, el_val_t end);
/* ── Bool helpers ────────────────────────────────────────────────────────── */
el_val_t bool_to_str(el_val_t b);
/* ── Numeric parsing ─────────────────────────────────────────────────────── */
el_val_t parse_int(el_val_t s, el_val_t default_val);
/* ── Process ─────────────────────────────────────────────────────────────── */
el_val_t exit_program(el_val_t code);
el_val_t getpid_now(void);
/* Self-terminating memory guard. Reads ELC_MAX_MEM_MB (default 512) and
* exits with code 1 if resident memory exceeds the limit. Call periodically
* during long compilation loops (e.g. after each function is compiled).
* Returns 0 when memory is within bounds. */
el_val_t el_mem_check(void);
/* ── CGI identity ─────────────────────────────────────────────────────────────
* Called at the start of main() in CGI programs (those with a `cgi {}` block).
* Records the program's DHARMA identity before any other code executes. */
void el_cgi_init(el_val_t name, el_val_t dharma_id, el_val_t principal,
el_val_t network, el_val_t engram);
/* ── DHARMA network builtins ─────────────────────────────────────────────────
* Available to CGI programs (declared with a `cgi {}` block).
*
* Peers are addressed by `dharma_id` of the form
* "<registry-id>@<transport-url>" e.g. "ntn-genesis@http://localhost:7770"
* If the @<url> portion is omitted, transport defaults to
* "http://localhost:7770" (the local CGI daemon assumption).
*
* Wire protocol (all peers expose):
* POST <url>/dharma/recv { channel, from, content } → response body
* POST <url>/dharma/event { type, payload, source, timestamp }
* POST <url>/api/activate { query } → list of nodes
*
* Hosting application's responsibility: an El program with a `cgi {}` block
* runs http_serve() with its own request handler; that handler should route
* "/dharma/event" requests by calling el_runtime_dharma_event_arrive() so
* incoming events feed dharma_field() queues. The runtime itself does not
* intercept any /dharma path. */
el_val_t dharma_connect(el_val_t cgi_id);
el_val_t dharma_send(el_val_t channel, el_val_t content);
el_val_t dharma_activate(el_val_t query);
void dharma_emit(el_val_t event_type, el_val_t payload);
el_val_t dharma_field(el_val_t event_type);
void dharma_strengthen(el_val_t cgi_id, el_val_t weight);
el_val_t dharma_relationship(el_val_t cgi_id);
el_val_t dharma_peers(void);
/* Public C API: called by an El program's HTTP handler when a /dharma/event
* request arrives. Pushes onto the per-event-type queue and signals any
* pending dharma_field() blockers. All three arguments must be NUL-terminated
* C strings (or NULL — then treated as empty). */
void el_runtime_dharma_event_arrive(const char* event_type,
const char* payload,
const char* source);
/* ── Engram local graph primitives ───────────────────────────────────────────
* Operate on the CGI's local Engram knowledge graph.
* `engram_activate` queries the local graph only; `dharma_activate` is
* network-wide across all connected CGI graphs. */
el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience);
el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
el_val_t salience, el_val_t importance, el_val_t confidence,
el_val_t tier, el_val_t tags);
/* Layered consciousness — see el_runtime.c for the layered architecture
* design notes (search "Layered consciousness architecture"). The five
* canonical layers (safety / core-identity / domain-knowledge / imprint /
* suit) are seeded automatically; engram_add_layer extends the registry
* with imprint or suit overlays at runtime. Nodes default to layer 1
* (core-identity) when created via engram_node / engram_node_full. */
el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label,
el_val_t salience, el_val_t certainty, el_val_t confidence,
el_val_t status, el_val_t tags, el_val_t layer_id);
el_val_t engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible,
el_val_t transparent, el_val_t injectable);
el_val_t engram_remove_layer(el_val_t layer_id);
el_val_t engram_list_layers(void);
el_val_t engram_get_node(el_val_t id);
void engram_strengthen(el_val_t node_id);
void engram_forget(el_val_t node_id);
el_val_t engram_node_count(void);
el_val_t engram_search(el_val_t query, el_val_t limit);
el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset);
void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation);
el_val_t engram_edge_between(el_val_t from_id, el_val_t to_id);
el_val_t engram_neighbors(el_val_t node_id);
el_val_t engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction);
el_val_t engram_edge_count(void);
/* Three-pass activation: background fan-out → working-memory promotion →
* Layer 0 override. See "Three-pass activation" in el_runtime.c. */
el_val_t engram_activate(el_val_t query, el_val_t depth);
el_val_t engram_save(el_val_t path);
el_val_t engram_load(el_val_t path);
/* JSON-string accessors — return pre-serialized JSON so HTTP handlers
* can pass results straight through without round-tripping ElList/ElMap
* through json_stringify. */
el_val_t engram_get_node_json(el_val_t id);
el_val_t engram_search_json(el_val_t query, el_val_t limit);
el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset);
el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset);
el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction);
el_val_t engram_activate_json(el_val_t query, el_val_t depth);
el_val_t engram_stats_json(void);
el_val_t engram_list_layers_json(void);
/* engram_compile_layered_json — produce a prompt-ready text block split
* into "[LAYER 0 — STRUCTURAL]" (non-suppressible layers, sacred fire)
* and "[ENGRAM CONTEXT]" (standard suppressible layers). Returns "" if
* no nodes promoted to working memory. */
el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth);
/* ── LLM (Anthropic API client) ─────────────────────────────────────────────
* All functions call https://api.anthropic.com/v1/messages with the API key
* from env ANTHROPIC_API_KEY. Default model when empty: claude-sonnet-4-5. */
el_val_t llm_call(el_val_t model, el_val_t prompt);
el_val_t llm_call_system(el_val_t model, el_val_t system_prompt, el_val_t user_prompt);
el_val_t llm_call_agentic(el_val_t model, el_val_t system, el_val_t user, el_val_t tools);
el_val_t llm_vision(el_val_t model, el_val_t system, el_val_t prompt, el_val_t image_url_or_b64);
el_val_t llm_models(void);
/* Register a tool handler by name. The handler is looked up via dlsym
* (mirroring http_set_handler), so any El `fn <name>(input)` compiles to
* a global C symbol that this function can locate at runtime.
* Handler signature: `el_val_t handler(el_val_t input_json)` — receives
* the tool input as a JSON-string el_val_t and returns a JSON-string
* el_val_t result. Used by llm_call_agentic. */
void llm_register_tool(el_val_t name, el_val_t handler_fn_name);
/* ── args() ─────────────────────────────────────────────────────────────────
* Provides access to command-line arguments passed to the program.
* Populated by el_runtime_init_args() before main() runs. */
el_val_t args(void);
void el_runtime_init_args(int argc, char** argv);
/* ── Crypto primitives ─────────────────────────────────────────────────────
* SHA-256, HMAC-SHA-256, and base64 (standard + URL-safe).
* Self-contained — no OpenSSL/libcrypto dependency. The implementations are
* adapted from public-domain reference code (Brad Conte / RFC 4648).
*
* Bytes-returning variants (sha256_bytes, hmac_sha256_bytes) return a string
* value whose contents are raw binary; callers usually feed these into
* base64_encode. Note that el_val_t strings are NUL-terminated by convention,
* so the binary payload may contain embedded NULs — pass it directly into
* base64_encode (which uses an explicit length) rather than treating it as
* a printable C string.
*
* The "base64" variants emit/accept RFC 4648 standard alphabet with padding.
* The "base64url" variants use URL-safe alphabet (`-`/`_`) with no padding,
* as used in JWTs. */
el_val_t sha256_hex(el_val_t input);
el_val_t sha256_bytes(el_val_t input);
el_val_t hmac_sha256_hex(el_val_t key, el_val_t message);
el_val_t hmac_sha256_bytes(el_val_t key, el_val_t message);
el_val_t base64_encode(el_val_t input);
el_val_t base64_decode(el_val_t input);
el_val_t base64url_encode(el_val_t input);
el_val_t base64url_decode(el_val_t input);
/* Length-aware variants (internal — exposed for the rare caller that already
* has a known-length binary buffer and doesn't want to round-trip through
* a NUL-terminated el_val_t string). Sha256_bytes and hmac_sha256_bytes feed
* these implicitly. */
el_val_t el_sha256_bytes_n(const unsigned char* data, size_t len);
el_val_t el_base64_encode_n(const unsigned char* data, size_t len, int url_safe);
/* ── Post-quantum primitives (liboqs-backed) ────────────────────────────────
* All inputs/outputs hex-encoded. Algorithm choices:
* Signature: CRYSTALS-Dilithium-3 (NIST level 3, balanced)
* KEM: CRYSTALS-Kyber-768 (NIST level 3)
* Hash: SHA3-256 (Keccak) (PQ-aware protocols favour SHA3 over SHA2)
*
* If liboqs is not linked (detected via __has_include(<oqs/oqs.h>) at compile
* time), the pq_* entry points return a JSON-shaped error string so callers
* fail loudly rather than silently fall back to classical schemes:
* {"error":"liboqs not linked, post-quantum primitives unavailable"}
*
* The hybrid handshake pairs X25519 with Kyber-768 per NIST PQ guidance and
* CNSA 2.0. Combined shared secret is HKDF-SHA256(x25519_ss || kyber_ss).
* Even if Kyber falls, X25519 holds; if X25519 falls under quantum attack,
* Kyber holds. SHA3-256 also remains usable independent of liboqs (the
* Keccak permutation is PQ-OK as a primitive). */
el_val_t pq_keygen_signature(void);
el_val_t pq_sign(el_val_t secret_key_hex, el_val_t message);
el_val_t pq_verify(el_val_t public_key_hex, el_val_t message, el_val_t signature_hex);
el_val_t pq_kem_keygen(void);
el_val_t pq_kem_encaps(el_val_t public_key_hex);
el_val_t pq_kem_decaps(el_val_t secret_key_hex, el_val_t ciphertext_hex);
el_val_t pq_hybrid_keygen(void);
el_val_t pq_hybrid_handshake(el_val_t remote_pub_combined);
el_val_t sha3_256_hex(el_val_t input);
/* ── AEAD: AES-256-GCM (libcrypto-backed) ───────────────────────────────────
* Symmetric authenticated encryption used to wrap envelopes after a KEM
* handshake. Caller MUST supply a 32-byte key (64 hex chars) — typically the
* Kyber-768 / hybrid shared_secret, optionally normalized via SHA3-256.
*
* aead_encrypt returns a JSON map {"nonce":"...","ciphertext":"..."} where
* ciphertext is the AES-256-GCM output with the 16-byte auth tag appended.
* Nonce is a fresh 12-byte CSPRNG draw — callers never pick the nonce, which
* structurally rules out the GCM nonce-reuse footgun.
*
* aead_decrypt returns the plaintext String, or "" on any failure (including
* auth-tag mismatch). Callers MUST check for "" before trusting the result. */
el_val_t aead_encrypt(el_val_t key_hex, el_val_t plaintext);
el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_hex);
/* ── Native VM builtin aliases (for compiled El source) ─────────────────────
* These match the El VM's native_* builtins so that El source compiled
* to C can call the same names without modification. */
el_val_t native_list_get(el_val_t list, el_val_t index);
el_val_t native_list_len(el_val_t list);
el_val_t native_list_append(el_val_t list, el_val_t elem);
el_val_t native_list_empty(void);
el_val_t native_list_clone(el_val_t list);
el_val_t native_string_chars(el_val_t s);
el_val_t native_int_to_str(el_val_t n);
/* ── Method-call shorthand aliases ──────────────────────────────────────────
* The El method-call convention `obj.method(args)` compiles to
* `method(obj, args)`. These aliases expose the runtime functions under
* the short names that result from method calls in El source.
*
* Example: `myList.append(x)` → `append(myList, x)` (calls this alias)
* `myList.len()` → `len(myList)` (calls this alias) */
el_val_t append(el_val_t list, el_val_t elem); /* el_list_append */
el_val_t len(el_val_t list); /* el_list_len */
el_val_t get(el_val_t list, el_val_t index); /* el_list_get */
el_val_t map_get(el_val_t map, el_val_t key); /* el_map_get */
el_val_t map_set(el_val_t map, el_val_t key, el_val_t value); /* el_map_set */
/* ── OTLP/HTTP Observability ─────────────────────────────────────────────── */
/* See bottom of el_runtime.c for the implementation.
* Configured by env vars OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_SERVICE_VERSION.
* No-op when OTLP_ENDPOINT is unset. Drop-on-failure semantics. */
/* ── Subprocess execution ────────────────────────────────────────────────── */
el_val_t exec_command(el_val_t cmd); /* run shell command, return exit code */
el_val_t exec_capture(el_val_t cmd); /* run shell command, capture stdout */
el_val_t exec(el_val_t cmd); /* exec(cmd) → stdout String (30s timeout) */
el_val_t exec_bg(el_val_t cmd); /* exec_bg(cmd) → PID String (non-blocking) */
/* ── Stdout redirection (used by compiler JS pipeline) ───────────────────── */
el_val_t stdout_to_file(el_val_t path); /* redirect process stdout to a file */
el_val_t stdout_restore(void); /* restore process stdout to terminal */
el_val_t emit_log(el_val_t level, el_val_t msg, el_val_t fields_json);
el_val_t emit_metric(el_val_t name, el_val_t value, el_val_t tags_json);
el_val_t trace_span_start(el_val_t name);
el_val_t trace_span_end(el_val_t span_handle);
el_val_t emit_event(el_val_t name, el_val_t duration_ms);
el_val_t __thread_create(el_val_t fn_name_v, el_val_t arg_v);
el_val_t __thread_join(el_val_t tid_v);
/* ── __ prefixed aliases (self-hosting compiler ABI) ─────────────────────────
* The El self-hosting compiler emits calls to __-prefixed names. These are
* forwarding wrappers around the existing el_runtime functions above. */
/* I/O */
el_val_t __println(el_val_t s);
el_val_t __print(el_val_t s);
el_val_t __readline(void);
/* String */
el_val_t __int_to_str(el_val_t n);
el_val_t __str_to_int(el_val_t s);
el_val_t __float_to_str(el_val_t f);
el_val_t __str_to_float(el_val_t s);
el_val_t __str_len(el_val_t s);
el_val_t __str_char_at(el_val_t s, el_val_t i);
el_val_t __str_cmp(el_val_t a, el_val_t b);
el_val_t __str_ncmp(el_val_t a, el_val_t b, el_val_t n);
el_val_t __str_concat_raw(el_val_t a, el_val_t b);
el_val_t __str_slice_raw(el_val_t s, el_val_t start, el_val_t end);
el_val_t __str_alloc(el_val_t n);
el_val_t __str_set_char(el_val_t s, el_val_t i, el_val_t c);
/* URL encoding */
el_val_t __url_encode(el_val_t s);
el_val_t __url_decode(el_val_t s);
/* Environment */
el_val_t __env_get(el_val_t key);
/* Subprocess */
el_val_t __exec(el_val_t cmd);
el_val_t __exec_bg(el_val_t cmd);
/* Process */
el_val_t __exit_program(el_val_t code);
/* Filesystem */
el_val_t __fs_exists(el_val_t path);
el_val_t __fs_mkdir(el_val_t path);
el_val_t __fs_read(el_val_t path);
el_val_t __fs_write(el_val_t path, el_val_t content);
el_val_t __fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t n);
el_val_t __fs_list_raw(el_val_t path);
/* HTTP server */
el_val_t __http_response(el_val_t status, el_val_t headers_json, el_val_t body);
el_val_t __http_serve(el_val_t port, el_val_t handler);
el_val_t __http_serve_v2(el_val_t port, el_val_t handler);
/* HTTP conn fd / SSE (weak; overridden by el_seed.c when linked together) */
el_val_t __http_conn_fd(void);
el_val_t __http_sse_open(el_val_t conn_id);
el_val_t __http_sse_send(el_val_t conn_id, el_val_t data);
el_val_t __http_sse_close(el_val_t conn_id);
/* HTTP client (requires HAVE_CURL; stubs provided for no-curl builds) */
el_val_t __http_do(el_val_t method, el_val_t url, el_val_t body,
el_val_t headers_map, el_val_t timeout_ms);
el_val_t __http_do_map(el_val_t method, el_val_t url, el_val_t body,
el_val_t headers_json, el_val_t timeout_ms);
el_val_t __http_do_map_to_file(el_val_t method, el_val_t url, el_val_t body,
el_val_t headers_json, el_val_t output_path);
/* JSON */
el_val_t __json_array_get(el_val_t json, el_val_t index);
el_val_t __json_array_get_string(el_val_t json, el_val_t index);
el_val_t __json_array_len(el_val_t json);
el_val_t __json_get(el_val_t json, el_val_t key);
el_val_t __json_get_raw(el_val_t json, el_val_t key);
el_val_t __json_set(el_val_t json, el_val_t key, el_val_t value);
el_val_t __json_parse_map(el_val_t json_str);
el_val_t __json_stringify_val(el_val_t val);
/* Hashing */
el_val_t __sha256_hex(el_val_t s);
/* State K/V */
el_val_t __state_del(el_val_t key);
el_val_t __state_get(el_val_t key);
el_val_t __state_keys(void);
el_val_t __state_set(el_val_t key, el_val_t val);
/* UUID */
el_val_t __uuid_v4(void);
/* Args */
el_val_t __args_json(void);
#ifdef __cplusplus
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
/*
* main.m entry point for native-hello-ios.
*
* iOS UIKit lifecycle:
* UIApplicationMain never returns. The compiled el program's boot body
* (native_init window_from_manifest app_build window_show) must
* run inside applicationDidFinishLaunchingWithOptions on the main thread.
*
* How it works:
* 1. el_uikit_set_args() forwards argc/argv to UIApplicationMain.
* 2. el_main_entry_fn is set to el_app_boot (defined below) before
* UIApplicationMain is entered.
* 3. ElAppDelegate.didFinishLaunchingWithOptions calls el_main_entry_fn(),
* which runs the el program's widget setup at the correct lifecycle point.
* 4. __native_run_loop() inside the compiled el main is a no-op on iOS
* UIApplicationMain already owns the run loop.
*
* The generated native_hello.c exports el_app_main() (renamed from main by
* the gen step in build.sh). We call it from el_app_boot so it runs after
* UIApplicationMain sets up the application object.
*
* Compile with -fno-objc-arc (required by el_uikit.m).
*/
#import <UIKit/UIKit.h>
/* From el_uikit.m */
void el_uikit_set_args(int argc, char **argv);
extern void (*el_main_entry_fn)(void);
/* Forward declaration of the renamed compiled el main. */
int el_app_main(int argc, char **argv);
/* Boot function registered as el_main_entry_fn. Called by ElAppDelegate
* inside didFinishLaunchingWithOptions on the main thread. */
static void el_app_boot(void) {
char *argv[] = {"el-app", NULL};
el_app_main(1, argv);
}
int main(int argc, char *argv[]) {
el_uikit_set_args(argc, argv);
/* Register the boot function so ElAppDelegate can call it. */
el_main_entry_fn = el_app_boot;
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, @"ElAppDelegate");
}
}
@@ -0,0 +1,123 @@
#include <stdint.h>
#include <stdlib.h>
#include "el_runtime.h"
el_val_t on_greet_click(el_val_t widget, el_val_t data);
el_val_t on_counter_click(el_val_t widget, el_val_t data);
el_val_t on_name_change(el_val_t widget, el_val_t data);
el_val_t app_build(el_val_t window);
el_val_t g_window;
el_val_t g_label;
el_val_t g_input;
el_val_t g_button;
el_val_t g_counter;
el_val_t g_counter_lbl;
el_val_t manifest_env;
el_val_t manifest_path;
el_val_t win;
el_val_t g_window;
el_val_t on_greet_click(el_val_t widget, el_val_t data) {
el_val_t name = widget_get_text(g_input);
el_val_t greeting = ({ el_val_t _if_result_1 = 0; if ((str_len(name) > 0)) { _if_result_1 = (el_str_concat(el_str_concat(EL_STR("Hello, "), name), EL_STR("!"))); } else { _if_result_1 = (EL_STR("Hello, World!")); } _if_result_1; });
widget_set_text(g_label, greeting);
return 0;
}
el_val_t on_counter_click(el_val_t widget, el_val_t data) {
el_val_t g_counter = (g_counter + 1);
widget_set_text(g_counter_lbl, el_str_concat(EL_STR("Clicks: "), int_to_str(g_counter)));
return 0;
}
el_val_t on_name_change(el_val_t widget, el_val_t data) {
el_val_t has_text = (str_len(data) > 0);
widget_set_disabled(g_button, !has_text);
return 0;
}
el_val_t app_build(el_val_t window) {
el_val_t root = vstack(0);
widget_set_padding_all(root, 24);
widget_set_bg_color_hex(root, EL_STR("#0f172a"));
widget_set_flex(root, 1);
el_val_t title = label(EL_STR("el-native \xe2\x80\x94 native widget demo"));
style_label_heading(title);
widget_add_child(root, title);
el_val_t gap1 = label(EL_STR(""));
widget_set_height(gap1, 16);
widget_add_child(root, gap1);
el_val_t subtitle = label(EL_STR("AppKit controls from el code, no ObjC in the app layer."));
style_label_muted(subtitle);
widget_add_child(root, subtitle);
el_val_t gap2 = label(EL_STR(""));
widget_set_height(gap2, 24);
widget_add_child(root, gap2);
el_val_t input_row = hstack(8);
el_val_t name_label = label(EL_STR("Name:"));
style_label_body(name_label);
widget_set_width(name_label, 60);
el_val_t input = text_field(EL_STR("Enter your name\xe2\x80\xa6"));
style_label_body(input);
widget_set_flex(input, 1);
widget_on_change(input, EL_STR("on_name_change"));
el_val_t g_input = input;
el_val_t greet_btn = button(EL_STR("Greet"));
style_button_primary(greet_btn);
widget_set_disabled(greet_btn, 1);
widget_on_click(greet_btn, EL_STR("on_greet_click"));
el_val_t g_button = greet_btn;
widget_add_child(input_row, name_label);
widget_add_child(input_row, input);
widget_add_child(input_row, greet_btn);
widget_add_child(root, input_row);
el_val_t gap3 = label(EL_STR(""));
widget_set_height(gap3, 12);
widget_add_child(root, gap3);
el_val_t greeting = label(EL_STR("Waiting for name\xe2\x80\xa6"));
style_label_body(greeting);
widget_set_color_hex(greeting, EL_STR("#60a5fa"));
widget_set_font(greeting, EL_STR("system"), 16, 1);
el_val_t g_label = greeting;
widget_add_child(root, greeting);
el_val_t gap4 = label(EL_STR(""));
widget_set_height(gap4, 24);
widget_add_child(root, gap4);
el_val_t counter_row = hstack(12);
el_val_t counter_lbl = label(EL_STR("Clicks: 0"));
style_label_body(counter_lbl);
el_val_t g_counter_lbl = counter_lbl;
el_val_t counter_btn = button(EL_STR("Click me"));
style_button_primary(counter_btn);
widget_on_click(counter_btn, EL_STR("on_counter_click"));
widget_add_child(counter_row, counter_lbl);
widget_add_child(counter_row, counter_btn);
widget_add_child(root, counter_row);
widget_add_child(window, root);
return 0;
}
int el_app_main(int _argc, char** _argv) {
el_runtime_init_args(_argc, _argv);
g_window = (-1);
g_label = (-1);
g_input = (-1);
g_button = (-1);
g_counter = 0;
g_counter_lbl = (-1);
native_init();
manifest_env = env(EL_STR("EL_MANIFEST"));
manifest_path = ({ el_val_t _if_result_2 = 0; if ((str_len(manifest_env) > 0)) { _if_result_2 = (manifest_env); } else { _if_result_2 = (EL_STR("manifest.el")); } _if_result_2; });
win = window_from_manifest(manifest_path);
g_window = win;
if (win < 0) {
println(EL_STR("Error: failed to create window. Is EL_TARGET_MACOS defined?"));
exit_program(1);
}
app_build(win);
window_show(win);
native_run_loop();
return 0;
}
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env bash
# build.sh — Build and install native-hello-ios to the iOS Simulator.
#
# Usage:
# ./build.sh # gen C sources + xcodebuild for simulator
# ./build.sh gen # regenerate C sources only
# ./build.sh build # xcodebuild only (skip elc step)
# ./build.sh install # gen + build + boot simulator + install + launch
# ./build.sh clean # remove build/ directory
#
# Requirements:
# - Xcode with iOS Simulator SDK installed
# - elc at ../../../lang/dist/platform/elc (or in PATH)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang"
EL_UI_ROOT="${SCRIPT_DIR}/../.."
EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime"
EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el"
EL_APP_ENTRY="${EL_UI_ROOT}/examples/native-hello/src/main.el"
EL_MANIFEST="${EL_UI_ROOT}/examples/native-hello/manifest.el"
SOURCES_DIR="${SCRIPT_DIR}/NativeHello"
BUILD_DIR="${SCRIPT_DIR}/build"
# Locate elc.
if command -v elc &>/dev/null; then
ELC="elc"
elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then
ELC="${EL_LANG_ROOT}/dist/platform/elc"
else
echo "Error: elc not found. Add it to PATH or place it at:"
echo " ${EL_LANG_ROOT}/dist/platform/elc"
exit 1
fi
gen() {
echo "==> Generating native_hello.c..."
EL_MANIFEST="${EL_MANIFEST}" \
"${ELC}" "${EL_APP_ENTRY}" \
> "${SOURCES_DIR}/native_hello.c"
# Rename the compiled el main() to el_app_main so it doesn't conflict with
# main.m's main(). main.m's main() is the real entry point for UIApplicationMain.
# Use a simple sed substitution on the exact signature elc emits.
sed -i '' 's/^int main(int _argc, char\*\* _argv) {/int el_app_main(int _argc, char** _argv) {/' \
"${SOURCES_DIR}/native_hello.c"
echo "==> Generating el_native_vessel.c..."
"${ELC}" "${EL_NATIVE_VESSEL}" \
> "${SOURCES_DIR}/el_native_vessel.c"
# Rename vessel main() similarly.
sed -i '' 's/^int main(int _argc, char\*\* _argv) {/int el_vessel_main(int _argc, char** _argv) {/' \
"${SOURCES_DIR}/el_native_vessel.c"
echo "==> Copying runtime files..."
cp "${EL_RUNTIME}/el_uikit.m" "${SOURCES_DIR}/"
cp "${EL_RUNTIME}/el_seed.c" "${SOURCES_DIR}/"
cp "${EL_RUNTIME}/el_runtime.c" "${SOURCES_DIR}/"
cp "${EL_RUNTIME}/el_runtime.h" "${SOURCES_DIR}/"
cp "${EL_RUNTIME}/el_native_target.h" "${SOURCES_DIR}/"
echo "==> Source generation complete."
}
xcode_build() {
echo "==> Building with xcodebuild (iphonesimulator)..."
xcodebuild \
-project "${SCRIPT_DIR}/NativeHello.xcodeproj" \
-scheme NativeHello \
-sdk iphonesimulator \
-configuration Debug \
-derivedDataPath "${BUILD_DIR}" \
CLANG_ENABLE_OBJC_ARC=NO \
OTHER_CFLAGS="-DEL_TARGET_IOS" \
OTHER_LDFLAGS="-ldl" \
build
APP_PATH=$(find "${BUILD_DIR}" -name "NativeHello.app" -maxdepth 6 | head -1)
if [ -n "${APP_PATH}" ]; then
echo "==> Build complete."
echo " App bundle: ${APP_PATH}"
else
echo "==> Build complete (app bundle in ${BUILD_DIR})."
fi
}
install_and_launch() {
gen
xcode_build
APP_PATH=$(find "${BUILD_DIR}" -name "NativeHello.app" -maxdepth 6 | head -1)
if [ -z "${APP_PATH}" ]; then
echo "Error: could not find NativeHello.app in ${BUILD_DIR}"
exit 1
fi
# Boot the default simulator if not already booted.
SIM_UDID=$(xcrun simctl list devices available --json \
| python3 -c "
import json,sys
d=json.load(sys.stdin)['devices']
for runtime,devs in d.items():
for dev in devs:
if 'iPhone' in dev['name'] and dev['isAvailable']:
print(dev['udid'])
exit()
")
if [ -z "${SIM_UDID}" ]; then
echo "Error: no available iPhone simulator found."
exit 1
fi
echo "==> Using simulator: ${SIM_UDID}"
xcrun simctl boot "${SIM_UDID}" 2>/dev/null || true
open -a Simulator
echo "==> Installing app..."
xcrun simctl install "${SIM_UDID}" "${APP_PATH}"
echo "==> Launching app..."
xcrun simctl launch "${SIM_UDID}" "ai.neurontechnologies.el.nativehello"
}
clean() {
echo "==> Cleaning..."
rm -rf "${BUILD_DIR}"
echo "==> Clean complete."
}
case "${1:-all}" in
gen) gen ;;
build) xcode_build ;;
install) install_and_launch ;;
clean) clean ;;
all|*) gen && xcode_build ;;
esac