AScript: Lightweight Dynamic Scripting Engine for C# Applications
Introduction
AScript represents a dynamic script parsing, compilation, and execution engine built on C#. It supports C# syntax, enabling applications to dynamically execute user-defined script logic without requiring recompilation. This capability proves invaluable for scenarios requiring runtime flexibility and user customization.
Open Source Repository: https://gitee.com/rockey627/ascript
Core Features
Syntax Support
AScript provides comprehensive C# syntax support:
- C# Basic Syntax: Variables, operators, expressions
- Control Flow: if/else, for, while, foreach, continue, break, return statements
- Functions: Custom function definitions, function overloading, recursive calls
- String Interpolation:
$'hello {name}'syntax support - Hexadecimal Integers: 0x0A format support
- Comments: Both line comments and block comments
Execution Modes
The engine supports two distinct execution modes:
Interpretive Execution: Immediate parsing and calculation without compilation overhead. Ideal for quick evaluations and simple expressions.
Compiled Execution: Compiles scripts into delegates before execution, with the ability to cache compilation results for repeated execution. This mode offers superior performance for frequently executed scripts.
Multi-Level Context System
AScript implements a sophisticated context hierarchy:
- Sub-context Creation: Support for creating child contexts that inherit from parent contexts
- Parent Context Inheritance: Sub-contexts can access definitions from parent contexts, enabling logical reuse
- Application Scenarios: Multi-branch script execution, function call isolation, and similar scenarios
- Root Context:
ScriptContext.Rootserves as the top-level context where global variables, functions, and other definitions can be set
Host Integration
Seamless integration with host applications through:
- Variable, Function, and Type Injection: Expose host application functionality to scripts
- Custom Syntax Parsing: Keyword extension capabilities for domain-specific languages
- Stream Script Reading: Efficient script loading from various sources
Quick Start Guide
Getting started with AScript requires minimal setup:
// NuGet Installation: Install-Package AScript
using AScript;
var script = new Script();
var result = script.Eval("5+8*6"); // Returns 53
// Function Definition
string code = @"
int sum(int a, int b)=>a+b;
int n=10;
sum(n,5)";
var result = script.Eval(code); // Returns 15The simplicity of this API demonstrates AScript's design philosophy: powerful capabilities with minimal complexity.
Application Scenarios
1. Rule Engine
Store business rules as scripts, dynamically loading and executing them at runtime. This enables rule adjustments without releasing new versions.
string filePath = @"./rule.txt";
// Cache time: -1 means permanent cache, 0 means no cache,
// positive values indicate cache time in milliseconds
int cacheTime = -1;
// Use file path as cache key
string cacheKey = filePath;
// Use file modification time as version number
// (or calculate file MD5 as version number)
string cacheVersion = File.GetLastWriteTime(filePath).ToFileTimeUtc().ToString();
// If file hasn't changed, execute from cache;
// otherwise re-read → parse → compile → cache → execute
var script = new Script();
script.Eval(() => File.OpenRead(filePath), cacheTime, cacheKey, cacheVersion);This approach proves particularly valuable in financial services, insurance underwriting, and e-commerce pricing scenarios where business rules change frequently.
2. Formula Calculator
Enable users to define custom calculation formulas while the system handles parsing and execution. Common applications include financial templates, performance calculations, and similar scenarios.
Users can create complex formulas without programming knowledge, while the system ensures safe execution and accurate results.
3. Game Scripting System
Provide lightweight scripting support for games, enabling players to write scripts for automated tasks, macro commands, and similar functionality.
The sandboxed execution environment ensures player scripts cannot compromise game integrity while providing meaningful customization capabilities.
4. Plugin Extension Mechanism
Allow third parties to write scripts extending application functionality, with sandbox execution ensuring security.
This approach enables extensible applications without exposing internal APIs or requiring complex plugin architectures.
5. Multi-Branch Execution
Based on the multi-level context system, branches maintain data isolation while sharing parent-level public logic.
var rootContext = new ScriptContext();
rootContext.SetVar("n", 8);
rootContext.AddFunc<int, int, int>("sum", (a, b) => a + b);
// Branch 1
var context1 = new ScriptContext(rootContext);
context1.SetVar("x", 100);
var script1 = new Script(context1);
var result1 = script1.Eval("sum(x, n)");
Console.WriteLine(result1); // Outputs 108
// Branch 2
var context2 = new ScriptContext(rootContext);
context2.SetVar("y", 50);
var script2 = new Script(context2);
var result2 = script2.Eval("sum(y, n)");
Console.WriteLine(result2); // Outputs 58This capability proves invaluable for scenarios requiring parallel execution paths with shared base functionality but branch-specific variables.
Technical Architecture
Parsing Layer
The parsing layer converts script text into an abstract syntax tree (AST), handling:
- Lexical analysis: Token identification and classification
- Syntax analysis: Building hierarchical structure from tokens
- Semantic analysis: Type checking and scope resolution
Compilation Layer
The compilation layer transforms AST into executable delegates:
- Expression compilation: Converting expressions to lambda functions
- Statement compilation: Handling control flow and variable assignments
- Optimization: Constant folding and dead code elimination
Execution Layer
The execution layer manages runtime behavior:
- Context management: Maintaining variable scopes and inheritance chains
- Function invocation: Handling parameter passing and return values
- Error handling: Providing meaningful error messages for debugging
Security Considerations
When embedding scripting engines in applications, security requires careful attention:
Sandbox Execution: AScript operates within a controlled environment, preventing access to unauthorized system resources.
Resource Limits: Implement execution time limits and memory constraints to prevent denial-of-service scenarios.
API Exposure: Carefully curate which host functions and types are exposed to scripts, following the principle of least privilege.
Input Validation: Validate script content before execution, rejecting potentially malicious patterns.
Performance Optimization
Several strategies optimize AScript performance:
Compilation Caching: Cache compiled delegates for repeated execution, eliminating parsing and compilation overhead.
Context Reuse: Reuse contexts when possible, avoiding recreation overhead.
Batch Execution: Group multiple script executions together when feasible, amortizing initialization costs.
Best Practices
1. Version Management
Implement script versioning to track changes and enable rollback:
string cacheVersion = File.GetLastWriteTime(filePath).ToFileTimeUtc().ToString();2. Error Handling
Provide comprehensive error handling with meaningful messages:
try {
var result = script.Eval(userScript);
} catch (ScriptException ex) {
// Log error and provide user-friendly message
Console.WriteLine($"Script error at line {ex.LineNumber}: {ex.Message}");
}3. Documentation
Document available functions and variables for script authors:
// Provide IntelliSense or documentation for script developers
// List available functions, their parameters, and return typesConclusion
AScript offers a compact, clean interface suitable for .NET applications requiring embedded scripting capabilities. Its multi-level context design enables graceful handling of multi-branch parallel execution scenarios.
For developers seeking a lightweight, efficient, easily integrated dynamic scripting engine, AScript deserves consideration. The combination of C# syntax familiarity, execution flexibility, and security features makes it a compelling choice for various application extension scenarios.
Whether building rule engines, formula calculators, game scripting systems, or plugin architectures, AScript provides the foundational capabilities needed for dynamic script execution without the complexity of full-featured scripting languages.