2021-12-01 02:52:00 +08:00
|
|
|
use super::*;
|
2022-01-09 19:55:17 +08:00
|
|
|
use crate::{
|
2022-02-12 21:09:23 +08:00
|
|
|
codegen::{expr::destructure_range, irrt::calculate_len_for_slice_range, stmt::exn_constructor},
|
2022-01-09 19:55:17 +08:00
|
|
|
symbol_resolver::SymbolValue,
|
|
|
|
};
|
|
|
|
use inkwell::{FloatPredicate, IntPredicate};
|
2021-12-01 02:52:00 +08:00
|
|
|
|
2021-12-01 03:23:58 +08:00
|
|
|
type BuiltinInfo = (
|
|
|
|
Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>,
|
|
|
|
&'static [&'static str]
|
|
|
|
);
|
|
|
|
|
2021-12-02 10:45:46 +08:00
|
|
|
pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
2021-12-01 02:52:00 +08:00
|
|
|
let int32 = primitives.0.int32;
|
|
|
|
let int64 = primitives.0.int64;
|
|
|
|
let float = primitives.0.float;
|
|
|
|
let boolean = primitives.0.bool;
|
|
|
|
let range = primitives.0.range;
|
|
|
|
let string = primitives.0.str;
|
2022-02-21 17:52:34 +08:00
|
|
|
let num_ty = primitives.1.get_fresh_var_with_range(&[int32, int64, float, boolean], Some("N".into()), None);
|
2021-12-01 02:52:00 +08:00
|
|
|
let var_map: HashMap<_, _> = vec![(num_ty.1, num_ty.0)].into_iter().collect();
|
|
|
|
|
2022-02-12 21:09:23 +08:00
|
|
|
let exception_fields = vec![
|
|
|
|
("__name__".into(), int32, true),
|
|
|
|
("__file__".into(), string, true),
|
|
|
|
("__line__".into(), int32, true),
|
|
|
|
("__col__".into(), int32, true),
|
|
|
|
("__func__".into(), string, true),
|
|
|
|
("__message__".into(), string, true),
|
|
|
|
("__param0__".into(), int64, true),
|
|
|
|
("__param1__".into(), int64, true),
|
|
|
|
("__param2__".into(), int64, true),
|
|
|
|
];
|
|
|
|
let div_by_zero = primitives.1.add_ty(TypeEnum::TObj {
|
|
|
|
obj_id: DefinitionId(10),
|
2022-02-21 17:52:34 +08:00
|
|
|
fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(),
|
2022-02-12 21:09:23 +08:00
|
|
|
params: Default::default()
|
|
|
|
});
|
|
|
|
let index_error = primitives.1.add_ty(TypeEnum::TObj {
|
|
|
|
obj_id: DefinitionId(11),
|
2022-02-21 17:52:34 +08:00
|
|
|
fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(),
|
2022-02-12 21:09:23 +08:00
|
|
|
params: Default::default()
|
|
|
|
});
|
|
|
|
let exn_cons_args = vec![
|
|
|
|
FuncArg { name: "msg".into(), ty: string,
|
2022-02-21 17:52:34 +08:00
|
|
|
default_value: Some(SymbolValue::Str("".into()))},
|
2022-02-12 21:09:23 +08:00
|
|
|
FuncArg { name: "param0".into(), ty: int64,
|
2022-02-21 17:52:34 +08:00
|
|
|
default_value: Some(SymbolValue::I64(0))},
|
2022-02-12 21:09:23 +08:00
|
|
|
FuncArg { name: "param1".into(), ty: int64,
|
2022-02-21 17:52:34 +08:00
|
|
|
default_value: Some(SymbolValue::I64(0))},
|
2022-02-12 21:09:23 +08:00
|
|
|
FuncArg { name: "param2".into(), ty: int64,
|
2022-02-21 17:52:34 +08:00
|
|
|
default_value: Some(SymbolValue::I64(0))},
|
2022-02-12 21:09:23 +08:00
|
|
|
];
|
2022-02-21 17:52:34 +08:00
|
|
|
let div_by_zero_signature = primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
2022-02-12 21:09:23 +08:00
|
|
|
args: exn_cons_args.clone(),
|
|
|
|
ret: div_by_zero,
|
|
|
|
vars: Default::default()
|
2022-02-21 17:52:34 +08:00
|
|
|
}));
|
|
|
|
let index_error_signature = primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
2022-02-12 21:09:23 +08:00
|
|
|
args: exn_cons_args,
|
|
|
|
ret: index_error,
|
|
|
|
vars: Default::default()
|
2022-02-21 17:52:34 +08:00
|
|
|
}));
|
2021-12-01 02:52:00 +08:00
|
|
|
let top_level_def_list = vec![
|
|
|
|
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
|
2022-02-21 17:52:34 +08:00
|
|
|
0,
|
|
|
|
None,
|
|
|
|
"int32".into(),
|
|
|
|
None,
|
|
|
|
None,
|
2021-12-01 02:52:00 +08:00
|
|
|
))),
|
|
|
|
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
|
2022-02-21 17:52:34 +08:00
|
|
|
1,
|
|
|
|
None,
|
|
|
|
"int64".into(),
|
|
|
|
None,
|
|
|
|
None,
|
2021-12-01 02:52:00 +08:00
|
|
|
))),
|
|
|
|
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
|
2022-02-21 17:52:34 +08:00
|
|
|
2,
|
|
|
|
None,
|
|
|
|
"float".into(),
|
|
|
|
None,
|
|
|
|
None,
|
2021-12-01 02:52:00 +08:00
|
|
|
))),
|
2022-02-21 17:52:34 +08:00
|
|
|
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(3, None, "bool".into(), None, None))),
|
|
|
|
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(4, None, "none".into(), None, None))),
|
2021-12-01 02:52:00 +08:00
|
|
|
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
|
2022-02-21 17:52:34 +08:00
|
|
|
5,
|
|
|
|
None,
|
|
|
|
"range".into(),
|
|
|
|
None,
|
|
|
|
None,
|
2021-12-01 02:52:00 +08:00
|
|
|
))),
|
2022-02-21 17:52:34 +08:00
|
|
|
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(6, None, "str".into(), None, None))),
|
2022-02-12 21:09:23 +08:00
|
|
|
Arc::new(RwLock::new(TopLevelDef::Class {
|
|
|
|
name: "Exception".into(),
|
|
|
|
object_id: DefinitionId(7),
|
|
|
|
type_vars: Default::default(),
|
|
|
|
fields: exception_fields.clone(),
|
|
|
|
methods: Default::default(),
|
|
|
|
ancestors: vec![],
|
|
|
|
constructor: None,
|
|
|
|
resolver: None,
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2022-02-12 21:09:23 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "ZeroDivisionError.__init__".into(),
|
|
|
|
simple_name: "__init__".into(),
|
|
|
|
signature: div_by_zero_signature,
|
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-21 17:52:34 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor)))),
|
|
|
|
loc: None,
|
2022-02-12 21:09:23 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "IndexError.__init__".into(),
|
|
|
|
simple_name: "__init__".into(),
|
|
|
|
signature: index_error_signature,
|
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-21 17:52:34 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(exn_constructor)))),
|
|
|
|
loc: None,
|
2022-02-12 21:09:23 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Class {
|
|
|
|
name: "ZeroDivisionError".into(),
|
|
|
|
object_id: DefinitionId(10),
|
|
|
|
type_vars: Default::default(),
|
|
|
|
fields: exception_fields.clone(),
|
|
|
|
methods: vec![("__init__".into(), div_by_zero_signature, DefinitionId(8))],
|
|
|
|
ancestors: vec![
|
|
|
|
TypeAnnotation::CustomClass { id: DefinitionId(10), params: Default::default() },
|
|
|
|
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }
|
|
|
|
],
|
|
|
|
constructor: Some(div_by_zero_signature),
|
|
|
|
resolver: None,
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2022-02-12 21:09:23 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Class {
|
|
|
|
name: "IndexError".into(),
|
|
|
|
object_id: DefinitionId(11),
|
|
|
|
type_vars: Default::default(),
|
|
|
|
fields: exception_fields,
|
|
|
|
methods: vec![("__init__".into(), index_error_signature, DefinitionId(9))],
|
|
|
|
ancestors: vec![
|
|
|
|
TypeAnnotation::CustomClass { id: DefinitionId(11), params: Default::default() },
|
|
|
|
TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() }
|
|
|
|
],
|
|
|
|
constructor: Some(index_error_signature),
|
|
|
|
resolver: None,
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2022-02-12 21:09:23 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "int32".into(),
|
|
|
|
simple_name: "int32".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
|
2021-12-01 02:52:00 +08:00
|
|
|
ret: int32,
|
|
|
|
vars: var_map.clone(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
2022-02-21 17:52:34 +08:00
|
|
|
|ctx, _, fun, args, generator| {
|
|
|
|
let int32 = ctx.primitives.int32;
|
|
|
|
let int64 = ctx.primitives.int64;
|
|
|
|
let float = ctx.primitives.float;
|
|
|
|
let boolean = ctx.primitives.bool;
|
|
|
|
let arg_ty = fun.0.args[0].ty;
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
|
|
|
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
|
|
|
|
Some(
|
|
|
|
ctx.builder
|
|
|
|
.build_int_z_extend(
|
|
|
|
arg.into_int_value(),
|
|
|
|
ctx.ctx.i32_type(),
|
|
|
|
"zext",
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
)
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, int32) {
|
|
|
|
Some(arg)
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, int64) {
|
|
|
|
Some(
|
|
|
|
ctx.builder
|
|
|
|
.build_int_truncate(
|
|
|
|
arg.into_int_value(),
|
|
|
|
ctx.ctx.i32_type(),
|
|
|
|
"trunc",
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
)
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, float) {
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
|
|
|
.build_float_to_signed_int(
|
|
|
|
arg.into_float_value(),
|
|
|
|
ctx.ctx.i32_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into();
|
|
|
|
Some(val)
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
})
|
|
|
|
},
|
2021-12-01 02:52:00 +08:00
|
|
|
)))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "int64".into(),
|
|
|
|
simple_name: "int64".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
|
2021-12-01 02:52:00 +08:00
|
|
|
ret: int64,
|
|
|
|
vars: var_map.clone(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
2022-02-21 17:52:34 +08:00
|
|
|
|ctx, _, fun, args, generator| {
|
|
|
|
let int32 = ctx.primitives.int32;
|
|
|
|
let int64 = ctx.primitives.int64;
|
|
|
|
let float = ctx.primitives.float;
|
|
|
|
let boolean = ctx.primitives.bool;
|
|
|
|
let arg_ty = fun.0.args[0].ty;
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
|
|
|
Ok(if ctx.unifier.unioned(arg_ty, boolean)
|
|
|
|
|| ctx.unifier.unioned(arg_ty, int32)
|
|
|
|
{
|
|
|
|
Some(
|
|
|
|
ctx.builder
|
|
|
|
.build_int_z_extend(
|
|
|
|
arg.into_int_value(),
|
|
|
|
ctx.ctx.i64_type(),
|
|
|
|
"zext",
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
)
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, int64) {
|
|
|
|
Some(arg)
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, float) {
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
|
|
|
.build_float_to_signed_int(
|
|
|
|
arg.into_float_value(),
|
|
|
|
ctx.ctx.i64_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into();
|
|
|
|
Some(val)
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
})
|
|
|
|
},
|
2021-12-01 02:52:00 +08:00
|
|
|
)))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "float".into(),
|
|
|
|
simple_name: "float".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
|
2021-12-01 02:52:00 +08:00
|
|
|
ret: float,
|
|
|
|
vars: var_map.clone(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
2022-02-21 17:52:34 +08:00
|
|
|
|ctx, _, fun, args, generator| {
|
|
|
|
let int32 = ctx.primitives.int32;
|
|
|
|
let int64 = ctx.primitives.int64;
|
|
|
|
let boolean = ctx.primitives.bool;
|
|
|
|
let float = ctx.primitives.float;
|
|
|
|
let arg_ty = fun.0.args[0].ty;
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
|
|
|
Ok(if ctx.unifier.unioned(arg_ty, boolean)
|
|
|
|
|| ctx.unifier.unioned(arg_ty, int32)
|
|
|
|
|| ctx.unifier.unioned(arg_ty, int64)
|
|
|
|
{
|
|
|
|
let arg = arg.into_int_value();
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
|
|
|
.build_signed_int_to_float(arg, ctx.ctx.f64_type(), "sitofp")
|
|
|
|
.into();
|
|
|
|
Some(val)
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, float) {
|
|
|
|
Some(arg)
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
})
|
|
|
|
},
|
2021-12-01 02:52:00 +08:00
|
|
|
)))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "round".into(),
|
|
|
|
simple_name: "round".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: float, default_value: None }],
|
2021-12-01 02:52:00 +08:00
|
|
|
ret: int32,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2021-12-01 02:52:00 +08:00
|
|
|
let round_intrinsic =
|
|
|
|
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
|
|
|
|
let float = ctx.ctx.f64_type();
|
|
|
|
let fn_type = float.fn_type(&[float.into()], false);
|
|
|
|
ctx.module.add_function("llvm.round.f64", fn_type, None)
|
|
|
|
});
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
2021-12-25 22:17:06 +08:00
|
|
|
.build_call(round_intrinsic, &[arg.into()], "round")
|
2021-12-01 02:52:00 +08:00
|
|
|
.try_as_basic_value()
|
|
|
|
.left()
|
|
|
|
.unwrap();
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(
|
2021-12-01 02:52:00 +08:00
|
|
|
ctx.builder
|
2022-02-21 17:52:34 +08:00
|
|
|
.build_float_to_signed_int(
|
|
|
|
val.into_float_value(),
|
|
|
|
ctx.ctx.i32_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
))
|
2021-12-01 02:52:00 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "round64".into(),
|
|
|
|
simple_name: "round64".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: float, default_value: None }],
|
2021-12-01 02:52:00 +08:00
|
|
|
ret: int64,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2021-12-01 02:52:00 +08:00
|
|
|
let round_intrinsic =
|
|
|
|
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
|
|
|
|
let float = ctx.ctx.f64_type();
|
|
|
|
let fn_type = float.fn_type(&[float.into()], false);
|
|
|
|
ctx.module.add_function("llvm.round.f64", fn_type, None)
|
|
|
|
});
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
2021-12-25 22:17:06 +08:00
|
|
|
.build_call(round_intrinsic, &[arg.into()], "round")
|
2021-12-01 02:52:00 +08:00
|
|
|
.try_as_basic_value()
|
|
|
|
.left()
|
|
|
|
.unwrap();
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(
|
2021-12-01 02:52:00 +08:00
|
|
|
ctx.builder
|
2022-02-21 17:52:34 +08:00
|
|
|
.build_float_to_signed_int(
|
|
|
|
val.into_float_value(),
|
|
|
|
ctx.ctx.i64_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
))
|
2021-12-01 02:52:00 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "range".into(),
|
|
|
|
simple_name: "range".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
2021-12-01 02:52:00 +08:00
|
|
|
args: vec![
|
|
|
|
FuncArg { name: "start".into(), ty: int32, default_value: None },
|
|
|
|
FuncArg {
|
|
|
|
name: "stop".into(),
|
|
|
|
ty: int32,
|
|
|
|
// placeholder
|
|
|
|
default_value: Some(SymbolValue::I32(0)),
|
|
|
|
},
|
|
|
|
FuncArg {
|
|
|
|
name: "step".into(),
|
|
|
|
ty: int32,
|
|
|
|
default_value: Some(SymbolValue::I32(1)),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
ret: range,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
2021-12-01 02:52:00 +08:00
|
|
|
let mut start = None;
|
|
|
|
let mut stop = None;
|
|
|
|
let mut step = None;
|
|
|
|
let int32 = ctx.ctx.i32_type();
|
|
|
|
let zero = int32.const_zero();
|
|
|
|
for (i, arg) in args.iter().enumerate() {
|
|
|
|
if arg.0 == Some("start".into()) {
|
2022-02-12 21:00:12 +08:00
|
|
|
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
2021-12-01 02:52:00 +08:00
|
|
|
} else if arg.0 == Some("stop".into()) {
|
2022-02-12 21:00:12 +08:00
|
|
|
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
2021-12-01 02:52:00 +08:00
|
|
|
} else if arg.0 == Some("step".into()) {
|
2022-02-12 21:00:12 +08:00
|
|
|
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
2021-12-01 02:52:00 +08:00
|
|
|
} else if i == 0 {
|
2022-02-12 21:00:12 +08:00
|
|
|
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
2021-12-01 02:52:00 +08:00
|
|
|
} else if i == 1 {
|
2022-02-12 21:00:12 +08:00
|
|
|
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
2021-12-01 02:52:00 +08:00
|
|
|
} else if i == 2 {
|
2022-02-12 21:00:12 +08:00
|
|
|
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator));
|
2021-12-01 02:52:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: error when step == 0
|
|
|
|
let step = step.unwrap_or_else(|| int32.const_int(1, false).into());
|
|
|
|
let stop = stop.unwrap_or_else(|| {
|
|
|
|
let v = start.unwrap();
|
|
|
|
start = None;
|
|
|
|
v
|
|
|
|
});
|
|
|
|
let start = start.unwrap_or_else(|| int32.const_zero().into());
|
|
|
|
let ty = int32.array_type(3);
|
|
|
|
let ptr = ctx.builder.build_alloca(ty, "range");
|
|
|
|
unsafe {
|
|
|
|
let a = ctx.builder.build_in_bounds_gep(ptr, &[zero, zero], "start");
|
|
|
|
let b = ctx.builder.build_in_bounds_gep(
|
|
|
|
ptr,
|
|
|
|
&[zero, int32.const_int(1, false)],
|
|
|
|
"end",
|
|
|
|
);
|
|
|
|
let c = ctx.builder.build_in_bounds_gep(
|
|
|
|
ptr,
|
|
|
|
&[zero, int32.const_int(2, false)],
|
|
|
|
"step",
|
|
|
|
);
|
|
|
|
ctx.builder.build_store(a, start);
|
|
|
|
ctx.builder.build_store(b, stop);
|
|
|
|
ctx.builder.build_store(c, step);
|
|
|
|
}
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(ptr.into()))
|
2021-12-01 02:52:00 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "str".into(),
|
|
|
|
simple_name: "str".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "s".into(), ty: string, default_value: None }],
|
2021-12-01 02:52:00 +08:00
|
|
|
ret: string,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator)))
|
2021-12-01 02:52:00 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "bool".into(),
|
|
|
|
simple_name: "bool".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
|
2021-12-01 02:52:00 +08:00
|
|
|
ret: primitives.0.bool,
|
|
|
|
vars: var_map,
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 02:52:00 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
2022-02-12 21:00:12 +08:00
|
|
|
|ctx, _, fun, args, generator| {
|
2021-12-01 02:52:00 +08:00
|
|
|
let int32 = ctx.primitives.int32;
|
|
|
|
let int64 = ctx.primitives.int64;
|
|
|
|
let float = ctx.primitives.float;
|
|
|
|
let boolean = ctx.primitives.bool;
|
|
|
|
let arg_ty = fun.0.args[0].ty;
|
2022-02-12 21:00:12 +08:00
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
|
2021-12-01 02:52:00 +08:00
|
|
|
Some(arg)
|
2021-12-02 01:08:55 +08:00
|
|
|
} else if ctx.unifier.unioned(arg_ty, int32) {
|
|
|
|
Some(ctx.builder.build_int_compare(
|
|
|
|
IntPredicate::NE,
|
|
|
|
ctx.ctx.i32_type().const_zero(),
|
|
|
|
arg.into_int_value(),
|
|
|
|
"bool",
|
|
|
|
).into())
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, int64) {
|
2021-12-01 02:52:00 +08:00
|
|
|
Some(ctx.builder.build_int_compare(
|
|
|
|
IntPredicate::NE,
|
|
|
|
ctx.ctx.i64_type().const_zero(),
|
|
|
|
arg.into_int_value(),
|
|
|
|
"bool",
|
|
|
|
).into())
|
|
|
|
} else if ctx.unifier.unioned(arg_ty, float) {
|
|
|
|
let val = ctx.builder.
|
|
|
|
build_float_compare(
|
|
|
|
// UEQ as bool(nan) is True
|
|
|
|
FloatPredicate::UEQ,
|
|
|
|
arg.into_float_value(),
|
|
|
|
ctx.ctx.f64_type().const_zero(),
|
|
|
|
"bool"
|
|
|
|
).into();
|
|
|
|
Some(val)
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
2022-02-21 17:52:34 +08:00
|
|
|
})
|
2021-12-01 02:52:00 +08:00
|
|
|
},
|
|
|
|
)))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 02:52:00 +08:00
|
|
|
})),
|
2021-12-01 03:23:58 +08:00
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "floor".into(),
|
|
|
|
simple_name: "floor".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: float, default_value: None }],
|
2021-12-01 03:23:58 +08:00
|
|
|
ret: int32,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 03:23:58 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2021-12-01 03:23:58 +08:00
|
|
|
let floor_intrinsic =
|
|
|
|
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
|
|
|
|
let float = ctx.ctx.f64_type();
|
|
|
|
let fn_type = float.fn_type(&[float.into()], false);
|
|
|
|
ctx.module.add_function("llvm.floor.f64", fn_type, None)
|
|
|
|
});
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
2021-12-25 22:17:06 +08:00
|
|
|
.build_call(floor_intrinsic, &[arg.into()], "floor")
|
2021-12-01 03:23:58 +08:00
|
|
|
.try_as_basic_value()
|
|
|
|
.left()
|
|
|
|
.unwrap();
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(
|
2021-12-01 03:23:58 +08:00
|
|
|
ctx.builder
|
|
|
|
.build_float_to_signed_int(
|
|
|
|
val.into_float_value(),
|
|
|
|
ctx.ctx.i32_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
))
|
2021-12-01 03:23:58 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 03:23:58 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "floor64".into(),
|
|
|
|
simple_name: "floor64".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: float, default_value: None }],
|
2021-12-01 03:23:58 +08:00
|
|
|
ret: int64,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 03:23:58 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2021-12-01 03:23:58 +08:00
|
|
|
let floor_intrinsic =
|
|
|
|
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
|
|
|
|
let float = ctx.ctx.f64_type();
|
|
|
|
let fn_type = float.fn_type(&[float.into()], false);
|
|
|
|
ctx.module.add_function("llvm.floor.f64", fn_type, None)
|
|
|
|
});
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
2021-12-25 22:17:06 +08:00
|
|
|
.build_call(floor_intrinsic, &[arg.into()], "floor")
|
2021-12-01 03:23:58 +08:00
|
|
|
.try_as_basic_value()
|
|
|
|
.left()
|
|
|
|
.unwrap();
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(
|
2021-12-01 03:23:58 +08:00
|
|
|
ctx.builder
|
|
|
|
.build_float_to_signed_int(
|
|
|
|
val.into_float_value(),
|
|
|
|
ctx.ctx.i64_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
))
|
2021-12-01 03:23:58 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 03:23:58 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "ceil".into(),
|
|
|
|
simple_name: "ceil".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: float, default_value: None }],
|
2021-12-01 03:23:58 +08:00
|
|
|
ret: int32,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 03:23:58 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2021-12-01 03:23:58 +08:00
|
|
|
let ceil_intrinsic =
|
|
|
|
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
|
|
|
|
let float = ctx.ctx.f64_type();
|
|
|
|
let fn_type = float.fn_type(&[float.into()], false);
|
|
|
|
ctx.module.add_function("llvm.ceil.f64", fn_type, None)
|
|
|
|
});
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
2021-12-25 22:17:06 +08:00
|
|
|
.build_call(ceil_intrinsic, &[arg.into()], "ceil")
|
2021-12-01 03:23:58 +08:00
|
|
|
.try_as_basic_value()
|
|
|
|
.left()
|
|
|
|
.unwrap();
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(
|
2021-12-01 03:23:58 +08:00
|
|
|
ctx.builder
|
|
|
|
.build_float_to_signed_int(
|
|
|
|
val.into_float_value(),
|
|
|
|
ctx.ctx.i32_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
))
|
2021-12-01 03:23:58 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 03:23:58 +08:00
|
|
|
})),
|
|
|
|
Arc::new(RwLock::new(TopLevelDef::Function {
|
|
|
|
name: "ceil64".into(),
|
|
|
|
simple_name: "ceil64".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
|
|
|
args: vec![FuncArg { name: "n".into(), ty: float, default_value: None }],
|
2021-12-01 03:23:58 +08:00
|
|
|
ret: int64,
|
|
|
|
vars: Default::default(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-01 03:23:58 +08:00
|
|
|
var_id: Default::default(),
|
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
2022-02-12 21:00:12 +08:00
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(|ctx, _, _, args, generator| {
|
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2021-12-01 03:23:58 +08:00
|
|
|
let ceil_intrinsic =
|
|
|
|
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
|
|
|
|
let float = ctx.ctx.f64_type();
|
|
|
|
let fn_type = float.fn_type(&[float.into()], false);
|
|
|
|
ctx.module.add_function("llvm.ceil.f64", fn_type, None)
|
|
|
|
});
|
|
|
|
let val = ctx
|
|
|
|
.builder
|
2021-12-25 22:17:06 +08:00
|
|
|
.build_call(ceil_intrinsic, &[arg.into()], "ceil")
|
2021-12-01 03:23:58 +08:00
|
|
|
.try_as_basic_value()
|
|
|
|
.left()
|
|
|
|
.unwrap();
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(Some(
|
2021-12-01 03:23:58 +08:00
|
|
|
ctx.builder
|
|
|
|
.build_float_to_signed_int(
|
|
|
|
val.into_float_value(),
|
|
|
|
ctx.ctx.i64_type(),
|
|
|
|
"fptosi",
|
|
|
|
)
|
|
|
|
.into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
))
|
2021-12-01 03:23:58 +08:00
|
|
|
})))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-01 03:23:58 +08:00
|
|
|
})),
|
2021-12-09 01:14:28 +08:00
|
|
|
Arc::new(RwLock::new({
|
2022-02-21 17:52:34 +08:00
|
|
|
let list_var = primitives.1.get_fresh_var(Some("L".into()), None);
|
2021-12-09 01:14:28 +08:00
|
|
|
let list = primitives.1.add_ty(TypeEnum::TList { ty: list_var.0 });
|
2022-02-21 17:52:34 +08:00
|
|
|
let arg_ty = primitives.1.get_fresh_var_with_range(&[list, primitives.0.range], Some("I".into()), None);
|
2021-12-09 01:14:28 +08:00
|
|
|
TopLevelDef::Function {
|
|
|
|
name: "len".into(),
|
|
|
|
simple_name: "len".into(),
|
2022-02-21 17:52:34 +08:00
|
|
|
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
|
2021-12-09 01:14:28 +08:00
|
|
|
args: vec![FuncArg {
|
2022-02-21 17:52:34 +08:00
|
|
|
name: "ls".into(),
|
2021-12-09 01:14:28 +08:00
|
|
|
ty: arg_ty.0,
|
|
|
|
default_value: None
|
|
|
|
}],
|
2021-12-20 17:44:42 +08:00
|
|
|
ret: int32,
|
2021-12-09 01:14:28 +08:00
|
|
|
vars: vec![(list_var.1, list_var.0), (arg_ty.1, arg_ty.0)].into_iter().collect(),
|
2022-02-21 17:52:34 +08:00
|
|
|
})),
|
2021-12-12 05:52:22 +08:00
|
|
|
var_id: vec![arg_ty.1],
|
2021-12-09 01:14:28 +08:00
|
|
|
instance_to_symbol: Default::default(),
|
|
|
|
instance_to_stmt: Default::default(),
|
|
|
|
resolver: None,
|
|
|
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
2022-02-12 21:00:12 +08:00
|
|
|
|ctx, _, fun, args, generator| {
|
2021-12-09 01:14:28 +08:00
|
|
|
let range_ty = ctx.primitives.range;
|
|
|
|
let arg_ty = fun.0.args[0].ty;
|
2022-02-12 21:00:12 +08:00
|
|
|
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator);
|
2022-02-21 17:52:34 +08:00
|
|
|
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
|
2021-12-09 01:14:28 +08:00
|
|
|
let arg = arg.into_pointer_value();
|
|
|
|
let (start, end, step) = destructure_range(ctx, arg);
|
2021-12-12 05:52:22 +08:00
|
|
|
Some(calculate_len_for_slice_range(ctx, start, end, step).into())
|
2021-12-09 01:14:28 +08:00
|
|
|
} else {
|
2021-12-13 04:02:30 +08:00
|
|
|
let int32 = ctx.ctx.i32_type();
|
|
|
|
let zero = int32.const_zero();
|
2021-12-27 22:50:36 +08:00
|
|
|
let len = ctx.build_gep_and_load(arg.into_pointer_value(), &[zero, int32.const_int(1, false)]).into_int_value();
|
|
|
|
if len.get_type().get_bit_width() != 32 {
|
|
|
|
Some(ctx.builder.build_int_truncate(len, int32, "len2i32").into())
|
|
|
|
} else {
|
|
|
|
Some(len.into())
|
|
|
|
}
|
2022-02-21 17:52:34 +08:00
|
|
|
})
|
2021-12-09 01:14:28 +08:00
|
|
|
},
|
|
|
|
)))),
|
2022-02-21 17:52:34 +08:00
|
|
|
loc: None,
|
2021-12-09 01:14:28 +08:00
|
|
|
}
|
|
|
|
}))
|
2021-12-01 02:52:00 +08:00
|
|
|
];
|
|
|
|
let ast_list: Vec<Option<ast::Stmt<()>>> =
|
|
|
|
(0..top_level_def_list.len()).map(|_| None).collect();
|
2021-12-01 03:23:58 +08:00
|
|
|
(
|
|
|
|
izip!(top_level_def_list, ast_list).collect_vec(),
|
|
|
|
&[
|
2022-02-12 21:09:23 +08:00
|
|
|
"ZeroDivisionError",
|
|
|
|
"IndexError",
|
2021-12-01 03:23:58 +08:00
|
|
|
"int32",
|
|
|
|
"int64",
|
|
|
|
"float",
|
|
|
|
"round",
|
|
|
|
"round64",
|
|
|
|
"range",
|
|
|
|
"str",
|
|
|
|
"bool",
|
|
|
|
"floor",
|
|
|
|
"floor64",
|
|
|
|
"ceil",
|
2021-12-09 01:14:28 +08:00
|
|
|
"ceil64",
|
|
|
|
"len",
|
2021-12-01 03:23:58 +08:00
|
|
|
]
|
|
|
|
)
|
2021-12-12 05:52:22 +08:00
|
|
|
}
|