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
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ jobs:
cmake -S runtime -B build/runtime -DCMAKE_BUILD_TYPE=Release -DKVSPACE_LIB_DIR="$KVSPACE_LIB_DIR"
cmake --build build/runtime --target kvlang_runtime -j
cargo build --release --manifest-path layout/Cargo.toml
cargo build --release --manifest-path runtime-rs/Cargo.toml
# kvspace CLI 是本步 clone 到 /tmp/kvspace 的 cli/(独立 Cargo 项目)
cargo build --release --manifest-path /tmp/kvspace/cli/Cargo.toml
# runtime-rs 的 build.rs 在 ../bin 找 libkvlang_runtime/libkvlanglayout,必须先就位
mkdir -p bin
cp layout/target/release/libkvlanglayout.dylib bin/
cargo build --release --manifest-path runtime-rs/Cargo.toml
cargo build --release --manifest-path /tmp/kvspace/cli/Cargo.toml
cp layout/target/release/kvlanglayout bin/
cp runtime-rs/target/release/kvlang bin/
- name: 后端装载冒烟(frontend 的 dlopen 真能加载后端)
Expand Down
39 changes: 38 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
# make install 一次性安装产物到最终态目录(.so→/usr/lib,可执行→/usr/bin,头→/usr/include/kvlang/)
# make all 全部(runtime + runtime-rs + layout + json)
# make clean 清理 bin/ 与各构建目录
# make clear install 的逆操作:删系统目录里 kvspace/kvlang 的 .so 与可执行
# make status 列出系统目录里已安装的库 / 可执行 / 头
# kvspace 后端由 libkvspace dispatch 前端按 DSN 运行时选择,不再编译期切换。

BIN := bin

.PHONY: all runtime runtime-rs layout json oldhero test install clean
.PHONY: all runtime runtime-rs layout json oldhero test install clean \
status clear clear-headers clear-all

all: runtime runtime-rs layout json

Expand Down Expand Up @@ -49,3 +52,37 @@ clean:
rm -rf $(BIN) build
cargo clean --manifest-path layout/Cargo.toml
cargo clean --manifest-path runtime-rs/Cargo.toml

# ── 安装态清理(install 的逆操作,kvspace / kvlang 生态) ─────────────────
# make clear 删 .so(/usr/lib、/usr/lib/kvspace、/lib、/usr/local/lib …)+ 可执行
# make clear-headers 再删头(/usr/include/kvspace、/usr/include/kvlang …)
# make clear-all clear + clear-headers
# 注:/lib、/bin 多为指向 /usr 的符号链接,重复删同一文件无副作用。
SUDO ?= sudo
LIBDIRS := /usr/lib/kvspace /usr/lib/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib /usr/local/lib
BINDIRS := /usr/bin /bin /usr/local/bin
BINS := kvlang kvlanglayout kvspace
HDRDIRS := /usr/include/kvspace /usr/include/kvlang /usr/local/include/kvspace /usr/local/include/kvlang
PCDIRS := /usr/lib/x86_64-linux-gnu/pkgconfig /usr/lib/pkgconfig /usr/local/lib/pkgconfig

status:
@echo "── 库 ──"
@for d in $(LIBDIRS); do ls -1 $$d/libkvspace*.so* $$d/libkvlang*.so* 2>/dev/null | sed 's|^| |'; done; true
@for d in $(PCDIRS); do [ -e $$d/kvspace.pc ] && echo " $$d/kvspace.pc"; done; true
@echo "── 可执行 ──"
@for d in $(BINDIRS); do for b in $(BINS); do [ -e $$d/$$b ] && echo " $$d/$$b"; done; done; true
@echo "── 头 ──"
@for d in $(HDRDIRS); do [ -e $$d ] && echo " $$d"; done; true

clear:
@for d in $(LIBDIRS); do $(SUDO) rm -f $$d/libkvspace*.so* $$d/libkvlang*.so*; done
@for d in $(PCDIRS); do $(SUDO) rm -f $$d/kvspace.pc; done
@for d in $(BINDIRS); do for b in $(BINS); do $(SUDO) rm -f $$d/$$b; done; done
@$(SUDO) ldconfig 2>/dev/null || true
@echo "✅ clear:已删 kvspace/kvlang 的 .so 与可执行"

clear-headers:
@$(SUDO) rm -rf $(HDRDIRS)
@echo "✅ clear-headers:已删头文件"

clear-all: clear clear-headers
8 changes: 8 additions & 0 deletions error_cases/type_error/array_member.kv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// expected:
// error: TypeError: kvspace·get: member access requires a container (struct/map), got [3]int64; compact arrays use [] indexing

rwfunc test() -> () {
list:[]int64 = [11, 22, 33]
list·[0] -> q
println("q=", q)
}
9 changes: 9 additions & 0 deletions error_cases/type_error/ptr_index.kv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// expected:
// error: 0:0: `[]` 下标不能用于指针 `p`(类型 *[]int64);先解引用 `*p` 再下标

rwfunc test() -> () {
list:[]int64 = [11, 22, 33]
&list -> p
p[0] -> q
println("q=", q)
}
9 changes: 9 additions & 0 deletions error_cases/type_error/ptr_member.kv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// expected:
// error: TypeError: kvspace·get: member access requires a container (struct/map), got [3]int64; compact arrays use [] indexing

rwfunc test() -> () {
list:[]int64 = [11, 22, 33]
&list -> p
p·[0] -> q
println("q=", q)
}
13 changes: 13 additions & 0 deletions layout/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ pub struct Expr {
pub lit: LitKind, // 字面量类型(仅叶节点有意义)
}

/// 一元前缀 `*` 解引用在 AST 里的算子名。落成读参/写槽名时前缀到名字上(`*p`),
/// runtime 按该槽里的 Ptr 解引用到目标——与形参槽 `*[0,±k]` 同一约定(见 spec [[ptr]])。
pub const DEREF_OP: &str = "*";

impl Expr {
pub fn is_leaf(&self) -> bool {
self.op.is_empty()
Expand Down Expand Up @@ -394,6 +398,15 @@ impl Instruction {
}
return ("=".to_string(), vec![v.clone()]);
}
// 解引用:`*<叶>` → 读参/写槽名 `*name`(与形参槽 `*[0,±k]` 同一约定,runtime 按该槽里的
// Ptr 解引用到目标)。非叶操作数由 lower 先展开成临时槽,故此处必为叶。
if e.op == DEREF_OP {
let a = match e.args.first() {
Some(a) if a.is_leaf() => a,
_ => panic!("flat: `*` 解引用要求叶操作数(lower 未展开?)"),
};
return ("=".to_string(), vec![format!("*{}", a.val)]);
}
let opcode = e.op.clone();
let reads: Vec<String> = e
.args
Expand Down
82 changes: 60 additions & 22 deletions layout/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet};

use super::ast::{self, Expr, Func, Instruction, LitKind, Stmt};
use super::scanner::{Diagnostic, Pos};
use super::{builtin, keytree, symbol};
use super::{builtin, keytree, langtype, symbol};

/// 容器类型(stringkeymap / mapexpr)不可用 `[]` 下标访问成员——
/// `[]` 仅限 compact array(shaped langtype,含字符串 `[]char/*`)。
Expand Down Expand Up @@ -195,30 +195,45 @@ fn check_map_inst(s: &Instruction, defined: &HashSet<String>, diags: &mut Vec<Di

pub fn check_container_subscript(fn_: &Func) -> Vec<Diagnostic> {
let tm = infer_types(fn_);
// 地址传参/返回的 `*` 是**传递方式**而非指针值:layout 体内已把它解引用(名字指称实参本体),
// 故 `p[i]`/`p·x` 是对实操本体的下标/成员,不是「指针当数组」。
let addr: Vec<&str> = fn_
.sig
.params
.iter()
.chain(fn_.sig.returns.iter())
.filter(|p| langtype::is_addr_param(&p.ty))
.map(|p| p.name.as_str())
.collect();
let mut diags = Vec::new();
check_subscript_body(&fn_.body, &tm, &mut diags);
check_subscript_body(&fn_.body, &tm, &addr, &mut diags);
diags
}

fn check_subscript_body(body: &[Stmt], tm: &HashMap<String, String>, diags: &mut Vec<Diagnostic>) {
fn check_subscript_body(
body: &[Stmt],
tm: &HashMap<String, String>,
addr: &[&str],
diags: &mut Vec<Diagnostic>,
) {
for st in body {
match st {
Stmt::Instruction(s) => check_subscript_inst(s, tm, diags),
Stmt::Scope(s) => check_subscript_body(&s.body, tm, diags),
Stmt::Instruction(s) => check_subscript_inst(s, tm, addr, diags),
Stmt::Scope(s) => check_subscript_body(&s.body, tm, addr, diags),
Stmt::If(s) => {
if let Some(c) = &s.cond {
check_subscript_inst(c, tm, diags);
check_subscript_inst(c, tm, addr, diags);
}
check_subscript_body(&s.then_, tm, diags);
check_subscript_body(&s.else_, tm, diags);
check_subscript_body(&s.then_, tm, addr, diags);
check_subscript_body(&s.else_, tm, addr, diags);
}
Stmt::While(s) => {
if let Some(c) = &s.cond {
check_subscript_inst(c, tm, diags);
check_subscript_inst(c, tm, addr, diags);
}
check_subscript_body(&s.body, tm, diags);
check_subscript_body(&s.body, tm, addr, diags);
}
Stmt::For(s) => check_subscript_body(&s.body, tm, diags),
Stmt::For(s) => check_subscript_body(&s.body, tm, addr, diags),
_ => {}
}
}
Expand All @@ -227,25 +242,42 @@ fn check_subscript_body(body: &[Stmt], tm: &HashMap<String, String>, diags: &mut
fn check_subscript_inst(
inst: &Instruction,
tm: &HashMap<String, String>,
addr: &[&str],
diags: &mut Vec<Diagnostic>,
) {
if let Some(e) = &inst.expr {
check_subscript_expr(e, tm, diags);
check_subscript_expr(e, tm, addr, diags);
}
}

fn check_subscript_expr(e: &Expr, tm: &HashMap<String, String>, diags: &mut Vec<Diagnostic>) {
fn check_subscript_expr(
e: &Expr,
tm: &HashMap<String, String>,
addr: &[&str],
diags: &mut Vec<Diagnostic>,
) {
if (e.op == "xv·at" || e.op == "xv·set") && !e.args.is_empty() {
let base = &e.args[0];
if base.is_leaf() {
if base.is_leaf() && !addr.contains(&base.val.as_str()) {
if let Some(t) = tm.get(&base.val) {
if is_container_type(t) {
let msg = if is_container_type(t) {
Some(format!(
"`[]` 下标不能用于容器 `{}`(类型 {});容器成员访问用 kvspace·get/kvspace·set 或 `{}·key`,`[]` 仅限 compact array",
base.val, t, base.val
))
} else if t.starts_with('*') {
// 指针不是数组:`[]` 不对指针隐式解引用(见 spec [[ptr]])。
Some(format!(
"`[]` 下标不能用于指针 `{}`(类型 {});先解引用 `*{}` 再下标",
base.val, t, base.val
))
} else {
None
};
if let Some(message) = msg {
diags.push(Diagnostic {
pos: Pos { line: 0, col: 0 },
message: format!(
"`[]` 下标不能用于容器 `{}`(类型 {});容器成员访问用 kvspace·get/kvspace·set 或 `{}·key`,`[]` 仅限 compact array",
base.val, t, base.val
),
message,
warn: false,
info: false,
source: String::new(),
Expand All @@ -257,7 +289,7 @@ fn check_subscript_expr(e: &Expr, tm: &HashMap<String, String>, diags: &mut Vec<
}
}
for a in &e.args {
check_subscript_expr(a, tm, diags);
check_subscript_expr(a, tm, addr, diags);
}
}

Expand Down Expand Up @@ -1034,6 +1066,12 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap<String, String
if symbol::lookup(opcode).cmp {
return "bool".to_string();
}
if opcode == "kvlang·abs" {
// `&x` 取址产 Ptr:类型记 `*<目标类型>`(目标类型未知则记裸 `*`)——`*` 前缀即 ref 轴标记,
// 供 `[]` 下标校验拦下「指针当数组」(见 [[ptr]])。
let t = reads.first().map(|r| slot_type(r, tm)).unwrap_or_default();
return format!("*{t}");
}
if is_cast_op(opcode) {
return opcode.to_string();
}
Expand All @@ -1049,8 +1087,8 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap<String, String
return String::new();
}
match opcode {
"kvlen" | "ndarray·numel" | "ndarray·dim" | "kvspace·listlen" | "string·len" | "string·ord"
| "string·cmp" | "string·find" | "string·parseint" | "xv·bodylen" => {
"kvlen" | "ndarray·numel" | "ndarray·dim" | "kvspace·listlen" | "string·len"
| "string·ord" | "string·cmp" | "string·find" | "string·parseint" | "xv·bodylen" => {
return "int64".to_string();
}
"xv·langtype" => return "[]char/utf8".to_string(),
Expand Down
Loading
Loading