Compiles a single user-defined callable.
(
ctx: &mut Context,
symtable: &mut GlobalSymtable,
callable: CallableSpan,
)
| 1115 | |
| 1116 | /// Compiles a single user-defined callable. |
| 1117 | fn compile_user_callable( |
| 1118 | ctx: &mut Context, |
| 1119 | symtable: &mut GlobalSymtable, |
| 1120 | callable: CallableSpan, |
| 1121 | ) -> Result<()> { |
| 1122 | if ctx.current_callable.is_some() { |
| 1123 | return Err(Error::CannotNestUserCallables(callable.name_pos)); |
| 1124 | } |
| 1125 | |
| 1126 | let skip_pc = ctx.codegen.emit(bytecode::make_nop(), callable.name_pos); |
| 1127 | |
| 1128 | let start_pc = ctx.codegen.next_pc(); |
| 1129 | |
| 1130 | let key_pos = callable.name_pos; |
| 1131 | let key = SymbolKey::from(callable.name.name); |
| 1132 | ctx.current_callable = Some(if callable.name.ref_type.is_some() { |
| 1133 | CallableKind::Function |
| 1134 | } else { |
| 1135 | CallableKind::Sub |
| 1136 | }); |
| 1137 | debug_assert!(ctx.callable_exit_jumps.is_empty()); |
| 1138 | |
| 1139 | let mut symtable = symtable.enter_scope(); |
| 1140 | |
| 1141 | // The call protocol expects the return value to be in the first local variable |
| 1142 | // so allocate it early, and then all arguments follow in order from left to right. |
| 1143 | if let Some(vtype) = callable.name.ref_type { |
| 1144 | let ret_reg = symtable |
| 1145 | .put_local(key.clone(), SymbolPrototype::Scalar(vtype)) |
| 1146 | .map_err(|e| Error::from_syms(e, key_pos))?; |
| 1147 | |
| 1148 | // Set the default value of the function result. We could instead try to do this |
| 1149 | // at runtime by clearning the return register... but the problem is that we need |
| 1150 | // to handle non-primitive types like strings and the runtime doesn't know the type |
| 1151 | // of the result to properly allocate it. |
| 1152 | let value = match vtype { |
| 1153 | ExprType::Boolean | ExprType::Integer => 0, |
| 1154 | ExprType::Double => ctx.codegen.get_constant(ConstantDatum::Double(0.0), key_pos)?, |
| 1155 | ExprType::Text => { |
| 1156 | ctx.codegen.get_constant(ConstantDatum::Text(String::new()), key_pos)? |
| 1157 | } |
| 1158 | }; |
| 1159 | ctx.codegen.emit(bytecode::make_load_integer(ret_reg, value), key_pos); |
| 1160 | } |
| 1161 | for param in callable.params { |
| 1162 | let key = SymbolKey::from(param.name); |
| 1163 | symtable |
| 1164 | .put_local( |
| 1165 | key.clone(), |
| 1166 | SymbolPrototype::Scalar(param.ref_type.unwrap_or(ExprType::Integer)), |
| 1167 | ) |
| 1168 | .map_err(|e| Error::from_syms(e, key_pos))?; |
| 1169 | } |
| 1170 | |
| 1171 | for stmt in callable.body { |
| 1172 | compile_stmt(ctx, &mut symtable, stmt)?; |
| 1173 | } |
| 1174 |
no test coverage detected