Skip to content

Commit c9d22b3

Browse files
committed
Introduce import system to support KernelScript and Python modules. Update AST, parser, and type checker to handle new import syntax and module calls. Enhance evaluation and IR generation for module function calls. Update symbol table to track imported modules and functions.
1 parent 8ec0865 commit c9d22b3

21 files changed

+2322
-158
lines changed

SPEC.md

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,49 @@ fn main(args: Args) -> i32 {
8989
}
9090
```
9191

92+
### 1.4 Unified Import System
93+
94+
KernelScript supports importing both KernelScript modules and external language modules using a unified syntax. Import behavior is automatically determined by file extension:
95+
96+
```kernelscript
97+
// Import KernelScript modules (.ks files)
98+
import utils from "./common/utils.ks" // Functions, types, maps, configs
99+
import packet_helpers from "../net/helpers.ks" // Shared across eBPF and userspace
100+
101+
// Import Python modules (.py files) - userspace only
102+
import ml_analysis from "./ml/threat_analysis.py"
103+
import data_processor from "./analytics/stats.py"
104+
105+
// Usage is identical regardless of source language
106+
@xdp
107+
fn intelligent_filter(ctx: *xdp_md) -> xdp_action {
108+
// Use KernelScript imported functions
109+
var protocol = utils.extract_protocol(ctx)
110+
111+
// Use Python imported functions (FFI bridge in userspace)
112+
var packet_data = ctx->data
113+
var packet_len = ctx->data_end - ctx->data
114+
var threat_score = ml_analysis.compute_threat_score(packet_data, packet_len)
115+
116+
if (threat_score > 0.8) {
117+
return XDP_DROP
118+
}
119+
return XDP_PASS
120+
}
121+
122+
fn main() -> i32 {
123+
// Both KernelScript and Python functions work seamlessly in userspace
124+
var is_valid = utils.validate_config()
125+
var model_stats = ml_analysis.get_model_statistics()
126+
127+
print("Config valid: %d, Model accuracy: %f", is_valid, model_stats.accuracy)
128+
129+
var prog = load(intelligent_filter)
130+
attach(prog, "eth0", 0)
131+
return 0
132+
}
133+
```
134+
92135
## 2. Lexical Structure
93136

94137
### 2.1 Keywords
@@ -4449,9 +4492,17 @@ boolean_literal = "true" | "false"
44494492
array_literal = "[" [ expression { "," expression } ] "]"
44504493
null_literal = "null"
44514494
4452-
(* Import declarations *)
4453-
import_declaration = "import" import_target
4454-
import_target = identifier | string_literal
4495+
(* Import declarations - unified syntax for KernelScript and external languages *)
4496+
import_declaration = "import" identifier "from" string_literal
4497+
4498+
(* Examples:
4499+
import utils from "./common/utils.ks" // KernelScript import
4500+
import ml_analysis from "./ml/threat.py" // Python import (userspace only)
4501+
4502+
Import behavior is determined by file extension:
4503+
- .ks files: Import KernelScript symbols (functions, types, maps, configs)
4504+
- .py files: Import Python functions with automatic FFI bridging (userspace only)
4505+
*)
44554506
44564507
(* Identifiers and basic tokens *)
44574508
identifier = letter { letter | digit | "_" }

examples/import/network_utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
Simple network utilities for KernelScript import testing
3+
"""
4+
5+
def calculate_bandwidth(packets_per_second, avg_packet_size=1500):
6+
"""Calculate bandwidth in bytes per second"""
7+
return packets_per_second * avg_packet_size
8+
9+
def is_rate_limited(current_rate, max_rate=1000000):
10+
"""Check if current rate exceeds maximum allowed rate"""
11+
return current_rate > max_rate
12+
13+
def get_default_mtu():
14+
"""Get default MTU size"""
15+
return 1500
16+
17+
def format_packet_count(count):
18+
"""Format packet count for display"""
19+
if count > 1000000:
20+
return f"{count / 1000000:.1f}M packets"
21+
elif count > 1000:
22+
return f"{count / 1000:.1f}K packets"
23+
else:
24+
return f"{count} packets"
25+
26+
# Configuration constants
27+
MAX_PACKET_SIZE = 9000
28+
DEFAULT_TIMEOUT = 30

examples/import/simple_utils.ks

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Simple utilities for import testing
2+
3+
fn validate_config() -> bool {
4+
return true
5+
}
6+
7+
fn get_status() -> u32 {
8+
return 42
9+
}

examples/import_demo.ks

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Working demo of unified import syntax
2+
3+
// Import KernelScript module (compiled to .so)
4+
import utils from "./import/simple_utils.ks"
5+
6+
// Import Python module (uses Python bridge)
7+
import network_utils from "./import/network_utils.py"
8+
9+
config network {
10+
enable_filtering: bool = false,
11+
status_code: u32 = 0,
12+
packet_count: u32 = 5000,
13+
}
14+
15+
@xdp
16+
fn intelligent_filter(ctx: *xdp_md) -> xdp_action {
17+
if (network.enable_filtering) {
18+
return XDP_DROP
19+
}
20+
return XDP_PASS
21+
}
22+
23+
fn main() -> i32 {
24+
// Use KernelScript imported functions (compiled C binding)
25+
var is_valid = utils.validate_config()
26+
var status = utils.get_status()
27+
28+
// Use Python imported functions (Python bridge) - simplified calls
29+
var mtu = network_utils.get_default_mtu()
30+
31+
print("=== Import Demo Results ===")
32+
print("KernelScript utils - Config valid: %d, Status: %d", is_valid, status)
33+
print("Python network_utils - MTU: %d", mtu)
34+
35+
var prog = load(intelligent_filter)
36+
attach(prog, "eth0", 0)
37+
return 0
38+
}

src/ast.ml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ and expr_desc =
162162
| ConfigAccess of string * string (* config_name, field_name *)
163163
| Call of expr * expr list (* Unified call: callee_expression * arguments *)
164164
| TailCall of string * expr list (* function_name, arguments - for explicit tail calls *)
165+
| ModuleCall of module_call (* module.function(args) calls *)
165166
| ArrayAccess of expr * expr
166167
| FieldAccess of expr * string
167168
| ArrowAccess of expr * string (* pointer->field *)
@@ -170,6 +171,14 @@ and expr_desc =
170171
| StructLiteral of string * (string * expr) list
171172
| Match of expr * match_arm list (* match (expr) { arms } *)
172173

174+
(** Module function call *)
175+
and module_call = {
176+
module_name: string;
177+
function_name: string;
178+
args: expr list;
179+
call_pos: position;
180+
}
181+
173182
(** Match pattern for basic match expressions *)
174183
and match_pattern =
175184
| ConstantPattern of literal (* 42, "string", true, etc. *)
@@ -309,6 +318,17 @@ type impl_block = {
309318
impl_pos: position;
310319
}
311320

321+
(** Import source type - determined by file extension *)
322+
type import_source_type = KernelScript | Python
323+
324+
(** Import declaration *)
325+
type import_declaration = {
326+
module_name: string; (* Local name for the imported module *)
327+
source_path: string; (* File path *)
328+
source_type: import_source_type; (* Determined from file extension *)
329+
import_pos: position;
330+
}
331+
312332
(** Top-level declarations *)
313333
type declaration =
314334
| AttributedFunction of attributed_function
@@ -319,6 +339,7 @@ type declaration =
319339
| StructDecl of struct_def
320340
| GlobalVarDecl of global_variable_declaration
321341
| ImplBlock of impl_block
342+
| ImportDecl of import_declaration
322343

323344
(** Complete AST *)
324345
type ast = declaration list
@@ -488,6 +509,31 @@ let make_default_pattern () = DefaultPattern
488509
let make_match_expr matched_expr arms pos =
489510
make_expr (Match (matched_expr, arms)) pos
490511

512+
(** Import-related helper functions *)
513+
let detect_import_source_type file_path =
514+
let extension = Filename.extension file_path in
515+
match String.lowercase_ascii extension with
516+
| ".ks" -> KernelScript
517+
| ".py" -> Python
518+
| _ -> failwith ("Unsupported import file type: " ^ extension)
519+
520+
let make_import_declaration module_name source_path pos = {
521+
module_name;
522+
source_path;
523+
source_type = detect_import_source_type source_path;
524+
import_pos = pos;
525+
}
526+
527+
let make_module_call module_name function_name args pos = {
528+
module_name;
529+
function_name;
530+
args;
531+
call_pos = pos;
532+
}
533+
534+
let make_module_call_expr module_name function_name args pos =
535+
make_expr (ModuleCall (make_module_call module_name function_name args pos)) pos
536+
491537
(** Pretty-printing functions for debugging *)
492538

493539
let string_of_position pos =
@@ -607,6 +653,11 @@ let rec string_of_expr expr =
607653
| TailCall (name, args) ->
608654
Printf.sprintf "%s(%s)" name
609655
(String.concat ", " (List.map string_of_expr args))
656+
| ModuleCall module_call ->
657+
Printf.sprintf "%s.%s(%s)"
658+
module_call.module_name
659+
module_call.function_name
660+
(String.concat ", " (List.map string_of_expr module_call.args))
610661
| ArrayAccess (arr, idx) ->
611662
Printf.sprintf "%s[%s]" (string_of_expr arr) (string_of_expr idx)
612663
| FieldAccess (obj, field) ->
@@ -802,6 +853,15 @@ let string_of_declaration = function
802853
| ImplStaticField (name, expr) -> Printf.sprintf "%s: %s," name (string_of_expr expr)
803854
) impl_block.impl_items) in
804855
Printf.sprintf "%s impl %s {\n %s\n}" attrs_str impl_block.impl_name items_str
856+
| ImportDecl import_decl ->
857+
let source_type_str = match import_decl.source_type with
858+
| KernelScript -> "KernelScript"
859+
| Python -> "Python"
860+
in
861+
Printf.sprintf "import %s from \"%s\" // %s"
862+
import_decl.module_name
863+
import_decl.source_path
864+
source_type_str
805865

806866
let string_of_ast ast =
807867
String.concat "\n\n" (List.map string_of_declaration ast)

src/dune

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
multi_program_analyzer multi_program_ir_optimizer ebpf_c_codegen
66
userspace_codegen evaluator safety_checker stdlib test_codegen
77
tail_call_analyzer kernel_module_codegen dynptr_bridge
8-
btf_parser btf_binary_parser kernel_types struct_ops_registry)
8+
btf_parser btf_binary_parser kernel_types struct_ops_registry
9+
import_resolver python_bridge kernelscript_bridge)
910
(libraries unix str kernelscript_context)
1011
(foreign_stubs
1112
(language c)

src/evaluator.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,10 @@ and eval_expression ctx expr =
668668
(* Tail calls are not supported in evaluation context - they only exist in eBPF *)
669669
eval_error ("Tail call to " ^ name ^ " cannot be evaluated in userspace context") expr.expr_pos
670670

671+
| ModuleCall module_call ->
672+
(* Module calls are not supported in evaluation context - they need FFI setup *)
673+
eval_error ("Module call to " ^ module_call.module_name ^ "." ^ module_call.function_name ^ " cannot be evaluated in userspace context") expr.expr_pos
674+
671675
| ArrayAccess (arr, idx) -> eval_array_access ctx arr idx expr.expr_pos
672676

673677
| FieldAccess (obj, field) -> eval_field_access ctx obj field expr.expr_pos

0 commit comments

Comments
 (0)