Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions examples/robot_arm.eigs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Robot Arm Control Simulation
# Demonstrates the EigenScript Standard Library for robotics

import control
import geometry
import robotics

# Simulation parameters
position is 0
velocity is 0
target is 100
time is 0

print of "=== Robot Arm Position Control ==="
print of "Initial position:"
print of position
print of "Target position:"
print of target

# Control loop (simplified - 10 steps)
steps is 0
loop while steps < 10:
# Update velocity using servo control
control_signal is robotics.servo_control of [position, target]
velocity is velocity + control_signal

# Apply stability damping
velocity is control.apply_damping of velocity

# Update position
position is position + velocity

# Clamp position to reasonable range
position is geometry.clamp of [position, -200, 200]

# Print status every 2 steps
remainder is steps % 2
if remainder = 0:
print of "Step:"
print of steps
print of "Position:"
print of position
print of "Velocity:"
print of velocity

# Check convergence
error is target - position
error_abs is geometry.norm of error

if error_abs < 1:
print of "Target reached!"
break

steps is steps + 1

print of "=== Final State ==="
print of "Final position:"
print of position
print of "Final velocity:"
print of velocity
print of "Error from target:"
print of (target - position)
33 changes: 33 additions & 0 deletions examples/robot_simple.eigs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Simple Robot Control Test
# Tests stdlib modules: control, geometry, robotics

import control
import geometry
import robotics

print of "=== Testing Control Module ==="

vel is 10
damped is control.apply_damping of vel
print of "Original velocity:"
print of vel
print of "Damped velocity:"
print of damped

print of "=== Testing Geometry Module ==="

x is 5
y is 3
dist is geometry.distance_1d of [x, y]
print of "Distance between 5 and 3:"
print of dist

print of "=== Testing Robotics Module ==="

current_pos is 0
target_pos is 10
control_force is robotics.compute_control_force of [current_pos, target_pos]
print of "Control force:"
print of control_force

print of "=== Success! ==="
17 changes: 17 additions & 0 deletions examples/stdlib_test.eigs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Simple test of stdlib functions - no cross-module calls

print of "=== Testing Standalone Functions ==="

# Test basic arithmetic
x is 10
y is 5
result is x + y
print of result

# Test control flow
if x > y:
print of 1
else:
print of 0

print of "=== Test Complete ==="
14 changes: 11 additions & 3 deletions src/eigenscript/compiler/analysis/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,19 @@ def __init__(self, root_dir: str = None):
else:
self.search_paths.append(os.getcwd())

# 2. Add Standard Library Path (Future Phase 4.3)
# We will eventually set EIGEN_PATH env var or use a fixed install location
# 2. Add Standard Library Path
# Check environment variable first, then fall back to package stdlib
stdlib_path = os.environ.get("EIGEN_PATH")
if stdlib_path:
if stdlib_path and os.path.exists(stdlib_path):
self.search_paths.append(stdlib_path)
else:
# Auto-detect stdlib path relative to this file
# resolver.py is in src/eigenscript/compiler/analysis/
# stdlib is in src/eigenscript/stdlib/
this_file = Path(__file__).resolve()
stdlib_dir = this_file.parent.parent.parent / "stdlib"
if stdlib_dir.exists():
self.search_paths.append(str(stdlib_dir))

def resolve(self, module_name: str) -> str:
"""
Expand Down
89 changes: 89 additions & 0 deletions src/eigenscript/compiler/codegen/llvm_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
Index,
Slice,
Program,
Import,
MemberAccess,
)


Expand Down Expand Up @@ -732,6 +734,12 @@ def _generate(self, node: ASTNode) -> ir.Value:
return self._generate_list_literal(node)
elif isinstance(node, Index):
return self._generate_index(node)
elif isinstance(node, Import):
# Import statements are no-ops in code generation
# Module linking is handled by compile.py during recursive compilation
return None
elif isinstance(node, MemberAccess):
return self._generate_member_access(node)
else:
raise CompilerError(
f"Code generation for '{type(node).__name__}' not implemented",
Expand Down Expand Up @@ -786,6 +794,17 @@ def _generate_identifier(self, node: Identifier) -> ir.Value:
if self.local_vars:
last_var_name = list(self.local_vars.keys())[-1]
var_ptr = self.local_vars[last_var_name]

# Check if this variable is a raw double (Scalar Fast Path) or EigenValue
if isinstance(var_ptr.type.pointee, ir.DoubleType):
# FAST PATH variable: No geometric tracking available
# Predicates always return False for unobserved variables
# This is semantically correct: unobserved = not tracked = assume stable
return GeneratedValue(
value=ir.Constant(self.bool_type, 0), kind=ValueKind.SCALAR
)

# EigenValue variable: Load and check predicate
eigen_ptr = self.builder.load(var_ptr)

# Call the appropriate predicate function
Expand Down Expand Up @@ -1114,6 +1133,51 @@ def _generate_relation(self, node: Relation) -> ir.Value:
result = self.builder.call(func, [call_arg])
return result

elif isinstance(node.left, MemberAccess):
# Handle module.function calls (cross-module function calls)
# Extract module and member names
module_name = node.left.object.name
member_name = node.left.member
mangled_name = f"{module_name}_{member_name}"

if mangled_name not in self.functions:
raise CompilerError(
f"Function '{mangled_name}' not found",
hint=f"Make sure module '{module_name}' is imported and compiled first",
node=node
)

# Generate the argument (same logic as user-defined functions)
gen_arg = self._generate(node.right)

# Determine call argument based on type
call_arg = None

if isinstance(gen_arg, GeneratedValue):
if gen_arg.kind == ValueKind.SCALAR:
# JIT Promotion: Scalar -> Stack EigenValue
call_arg = self._create_eigen_on_stack(gen_arg.value)
elif gen_arg.kind == ValueKind.EIGEN_PTR:
call_arg = gen_arg.value
else:
raise TypeError(
"Cannot pass List to function expecting EigenValue"
)
elif isinstance(gen_arg, ir.Value):
if gen_arg.type == self.double_type:
# JIT Promotion: Scalar -> Stack EigenValue
call_arg = self._create_eigen_on_stack(gen_arg)
elif isinstance(gen_arg.type, ir.PointerType):
# Assume it's an EigenValue pointer
call_arg = gen_arg
else:
raise TypeError(f"Unexpected argument type: {gen_arg.type}")

# Call the function
func = self.functions[mangled_name]
result = self.builder.call(func, [call_arg])
return result

raise NotImplementedError(
f"Relation {node.left} of {node.right} not implemented"
)
Expand Down Expand Up @@ -1286,6 +1350,31 @@ def _generate_interrogative(self, node: Interrogative) -> GeneratedValue:
f"Interrogative {node.interrogative} not implemented"
)

def _generate_member_access(self, node: MemberAccess) -> GeneratedValue:
"""Generate code for member access (module.function).

Converts module.function_name to mangled name module_function_name
for cross-module function calls.
"""
# For now, member access is only supported for module.function pattern
if not isinstance(node.object, Identifier):
raise CompilerError(
"Member access only supported for module.function pattern",
hint="Example: control.apply_damping",
node=node
)

module_name = node.object.name
member_name = node.member

# Create mangled name: module_function
mangled_name = f"{module_name}_{member_name}"

# Return as an identifier that can be used in function calls
# Create a synthetic Identifier node
synthetic_id = Identifier(name=mangled_name)
return self._generate_identifier(synthetic_id)

def _generate_function_def(self, node: FunctionDef) -> None:
"""Generate code for function definitions."""
# Apply name mangling for library modules to prevent symbol collisions
Expand Down
58 changes: 58 additions & 0 deletions src/eigenscript/stdlib/control.eigs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# EigenScript Standard Library: Control Systems
# Provides stability mechanisms for dynamic systems

# apply_damping
# Input: velocity (scalar)
# Output: damped velocity
# Usage: new_v is apply_damping of v
define apply_damping as:
vel is n

# Paradox Mediation:
# If the system is oscillating (period-2 loops), energy is trapped.
# We must dissipate it to resolve the paradox.
if oscillating:
return vel * 0.5

# Divergence Check:
# If the system is exploring too fast (spacelike expansion),
# apply emergency braking.
if diverging:
return vel * 0.1

# If stable (timelike), preserve momentum.
return vel


# pid_step
# Input: error (scalar)
# Output: control signal
# Simple proportional control with adaptive gain
define pid_step as:
error is n

# Base proportional gain
kp is 0.1

# Auto-tuning:
# If the trajectory is stable, we can afford to move faster.
if stable:
kp is kp * 1.5

# If diverging, reduce gain to prevent overshoot
if diverging:
kp is kp * 0.5

return error * kp


# convergence_check
# Input: value (scalar)
# Output: 1 if converged, 0 otherwise
define convergence_check as:
x is n

if converged:
return 1

return 0
Loading
Loading