restore(lang/compiler): bring back lang/src/compiler from e917d400; add Hako index canaries and docs; implement Rust-side index operator (Array/Map get/set) with Fail‑Fast diagnostics

- restore: lang/src/compiler/** (parser/emit/builder/pipeline_v2) from e917d400
- docs: docs/development/selfhosting/index-operator-hako.md
- smokes(hako): tools/smokes/v2/profiles/quick/core/index_operator_hako.sh (opt-in)
- smokes(vm): adjust index_operator_vm.sh for semicolon gate + stable error text
- rust/parser: allow IndexExpr and assignment LHS=Index; postfix parse LBRACK chain
- rust/builder: lower arr/map index to BoxCall get/set; annotate array/map literals; Fail‑Fast for unsupported types
- CURRENT_TASK: mark Rust side done; add Hako tasks checklist

Note: files disappeared likely due to branch FF to a lineage without lang/src/compiler; no explicit delete commit found. Added anchor checks and suggested CI guard in follow-up.
This commit is contained in:
nyash-codex
2025-10-31 20:18:39 +09:00
parent 86fd03afe8
commit 5e3d9e7ae4
86 changed files with 6214 additions and 20 deletions

View File

@ -66,6 +66,8 @@ impl super::MirBuilder {
let stmt = AssignStmt::try_from(node).expect("ASTNode::Assignment must convert");
if let ASTNode::FieldAccess { object, field, .. } = stmt.target.as_ref() {
self.build_field_assignment(*object.clone(), field.clone(), *stmt.value.clone())
} else if let ASTNode::Index { target, index, .. } = stmt.target.as_ref() {
self.build_index_assignment(*target.clone(), *index.clone(), *stmt.value.clone())
} else if let ASTNode::Variable { name, .. } = stmt.target.as_ref() {
self.build_assignment(name.clone(), *stmt.value.clone())
} else {
@ -73,6 +75,10 @@ impl super::MirBuilder {
}
}
ASTNode::Index { target, index, .. } => {
self.build_index_expression(*target.clone(), *index.clone())
}
node @ ASTNode::FunctionCall { .. } => {
let c = CallExpr::try_from(node).expect("ASTNode::FunctionCall must convert");
self.build_function_call(c.name, c.arguments)
@ -213,6 +219,11 @@ impl super::MirBuilder {
box_type: "ArrayBox".to_string(),
args: vec![],
})?;
self.value_origin_newbox
.insert(arr_id, "ArrayBox".to_string());
self
.value_types
.insert(arr_id, super::MirType::Box("ArrayBox".to_string()));
for e in elements {
let v = self.build_expression_impl(e)?;
self.emit_instruction(MirInstruction::BoxCall {
@ -233,6 +244,12 @@ impl super::MirBuilder {
box_type: "MapBox".to_string(),
args: vec![],
})?;
self
.value_origin_newbox
.insert(map_id, "MapBox".to_string());
self
.value_types
.insert(map_id, super::MirType::Box("MapBox".to_string()));
for (k, expr) in entries {
// const string key
let k_id = crate::mir::builder::emission::constant::emit_string(self, k);
@ -310,4 +327,110 @@ impl super::MirBuilder {
_ => Err(format!("Unsupported AST node type: {:?}", ast)),
}
}
fn infer_index_target_class(&self, target_val: ValueId) -> Option<String> {
if let Some(cls) = self.value_origin_newbox.get(&target_val) {
return Some(cls.clone());
}
self.value_types.get(&target_val).and_then(|ty| match ty {
super::MirType::Box(name) => Some(name.clone()),
super::MirType::String => Some("String".to_string()),
super::MirType::Integer => Some("Integer".to_string()),
super::MirType::Float => Some("Float".to_string()),
_ => None,
})
}
fn format_index_target_kind(class_hint: Option<&String>) -> String {
class_hint
.map(|s| s.as_str())
.filter(|s| !s.is_empty())
.unwrap_or("unknown")
.to_string()
}
pub(super) fn build_index_expression(
&mut self,
target: ASTNode,
index: ASTNode,
) -> Result<ValueId, String> {
let target_val = self.build_expression(target)?;
let class_hint = self.infer_index_target_class(target_val);
match class_hint.as_deref() {
Some("ArrayBox") => {
let index_val = self.build_expression(index)?;
let dst = self.value_gen.next();
self.emit_box_or_plugin_call(
Some(dst),
target_val,
"get".to_string(),
None,
vec![index_val],
super::EffectMask::READ,
)?;
Ok(dst)
}
Some("MapBox") => {
let index_val = self.build_expression(index)?;
let dst = self.value_gen.next();
self.emit_box_or_plugin_call(
Some(dst),
target_val,
"get".to_string(),
None,
vec![index_val],
super::EffectMask::READ,
)?;
Ok(dst)
}
_ => Err(format!(
"index operator is only supported for Array/Map (found {})",
Self::format_index_target_kind(class_hint.as_ref())
)),
}
}
pub(super) fn build_index_assignment(
&mut self,
target: ASTNode,
index: ASTNode,
value: ASTNode,
) -> Result<ValueId, String> {
let target_val = self.build_expression(target)?;
let class_hint = self.infer_index_target_class(target_val);
match class_hint.as_deref() {
Some("ArrayBox") => {
let index_val = self.build_expression(index)?;
let value_val = self.build_expression(value)?;
self.emit_box_or_plugin_call(
None,
target_val,
"set".to_string(),
None,
vec![index_val, value_val],
super::EffectMask::MUT,
)?;
Ok(value_val)
}
Some("MapBox") => {
let index_val = self.build_expression(index)?;
let value_val = self.build_expression(value)?;
self.emit_box_or_plugin_call(
None,
target_val,
"set".to_string(),
None,
vec![index_val, value_val],
super::EffectMask::MUT,
)?;
Ok(value_val)
}
_ => Err(format!(
"index assignment is only supported for Array/Map (found {})",
Self::format_index_target_kind(class_hint.as_ref())
)),
}
}
}