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
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"crates/qsys_ir",
"crates/qsys_model",
"crates/qsys_parser",
"crates/qsys_runtime",
"crates/qsys_sema",
"crates/qsys_wasm",
"crates/qsyspyc",
Expand All @@ -24,4 +25,5 @@ qsys_diagnostics = { path = "crates/qsys_diagnostics" }
qsys_ir = { path = "crates/qsys_ir" }
qsys_model = { path = "crates/qsys_model" }
qsys_parser = { path = "crates/qsys_parser" }
qsys_runtime = { path = "crates/qsys_runtime" }
qsys_sema = { path = "crates/qsys_sema" }
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ crates/
├── qsys_model/ # Authoritative Q-SYS domain model (Controls, Properties, Pin styles, Layout)
├── qsys_diagnostics/ # Diagnostic catalog (E0001–E0205), span tracking, Monaco/LSP markers
├── qsys_parser/ # AST lowering from ruff_python_parser; strict syntax validation
├── qsys_runtime/ # Runtime plugins (PythonCore, LuaStdlib, QSysRuntime): globals, native types, removed builtins
├── qsys_sema/ # Scope management, symbol resolution, stable IDs, type inference
├── qsys_ir/ # Target-agnostic Intermediate Representation (Typed IR)
├── qsys_codegen_lua/ # Q-SYS Lua 5.3 code generator (PluginInfo, GetControls, runtime guard)
Expand Down
21 changes: 2 additions & 19 deletions crates/qsys_codegen_lua/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
use qsys_ir::*;
use qsys_model::{ControlKind, PropertyKind};
use qsys_model::{ControlKind, PropertyKind, is_design_time_method};

use crate::context::CodegenContext;
use crate::emitter::{LuaEmitter, escape_lua_string, format_lua_float};

// Helper to identify lifecycle methods
fn is_lifecycle_method(name: &str) -> bool {
matches!(
name,
"get_properties"
| "get_controls"
| "get_control_layout"
| "get_pages"
| "get_color"
| "get_pretty_name"
| "rectify_properties"
| "get_components"
| "get_pins"
| "get_wiring"
)
}

pub fn emit_plugin(plugin: &IRPlugin, ctx: &mut CodegenContext) -> String {
// Register controls and properties
for ctl in &plugin.controls {
Expand Down Expand Up @@ -114,7 +97,7 @@ pub fn emit_plugin(plugin: &IRPlugin, ctx: &mut CodegenContext) -> String {

// Emit runtime methods (excluding design-time lifecycle methods)
for method in &plugin.methods {
if is_lifecycle_method(&method.name) {
if is_design_time_method(&method.name) {
continue;
}
emit_method(&mut emitter, &plugin.class_name, method);
Expand Down
88 changes: 88 additions & 0 deletions crates/qsys_codegen_lua/tests/codegen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,91 @@ class MatrixMixer(QSysPlugin):
"Subclass super().show() must lower to Foo.show(self):\n{lua}"
);
}

#[test]
fn test_runtime_env_call_styles() {
let src = r#"
t = Timer.New()
t.Start(0.5)
sock = UdpSocket.New()
sock.Send("255.255.255.255", 9, "hi")
n = math.floor(2.5)
qsys.lua("print('raw')")
"#;
let lua = compile_to_lua(src);

assert!(
lua.contains("Timer.New()"),
"Factories use dot calls:\n{lua}"
);
assert!(
lua.contains("t:Start(0.5)"),
"Handle methods use colon calls:\n{lua}"
);
assert!(
lua.contains("UdpSocket.New()"),
"Factories use dot calls:\n{lua}"
);
assert!(
lua.contains("sock:Send(\"255.255.255.255\", 9, \"hi\")"),
"Handle methods use colon calls:\n{lua}"
);
assert!(
lua.contains("math.floor(2.5)"),
"Library functions use dot calls:\n{lua}"
);
assert!(
lua.contains("print('raw')"),
"Escape hatch emits raw Lua:\n{lua}"
);
}

#[test]
fn test_runtime_env_builtin_lowering() {
let src = r#"
items = [1, 2, 3]
n = len(items)
s = str(n)
m = abs(-3)
c = chr(65)
"#;
let lua = compile_to_lua(src);

assert!(
lua.contains("#items"),
"len() lowers to the # operator:\n{lua}"
);
assert!(
lua.contains("tostring(n)"),
"str() lowers to tostring():\n{lua}"
);
assert!(
lua.contains("math.abs("),
"abs() lowers to math.abs():\n{lua}"
);
assert!(
lua.contains("string.char(65)"),
"chr() lowers to string.char():\n{lua}"
);
}

#[test]
fn test_user_functions_shadow_builtin_lowering() {
let src = r#"
def len(x):
return 42

items = [1, 2, 3]
n = len(items)
"#;
let lua = compile_to_lua(src);

assert!(
!lua.contains("#items"),
"A user-defined len() must not be lowered to the # operator:\n{lua}"
);
assert!(
lua.contains("len(items)"),
"User len() is called as written:\n{lua}"
);
}
1 change: 1 addition & 0 deletions crates/qsys_compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ qsys_diagnostics.workspace = true
qsys_ir.workspace = true
qsys_model.workspace = true
qsys_parser.workspace = true
qsys_runtime.workspace = true
qsys_sema.workspace = true
qsys_codegen_lua = { path = "../qsys_codegen_lua" }

Expand Down
Loading
Loading