1
0
forked from M-Labs/nac3

basic unsigned integer support

This commit is contained in:
ychenfo 2022-03-05 03:45:09 +08:00
parent 8602852241
commit 79c469301a
14 changed files with 306 additions and 60 deletions

View File

@ -59,6 +59,8 @@ pub struct PrimitivePythonId {
int: u64, int: u64,
int32: u64, int32: u64,
int64: u64, int64: u64,
uint32: u64,
uint64: u64,
float: u64, float: u64,
bool: u64, bool: u64,
list: u64, list: u64,
@ -362,6 +364,8 @@ impl Nac3 {
int: id_fn.call1((builtins_mod.getattr("int").unwrap(),)).unwrap().extract().unwrap(), int: id_fn.call1((builtins_mod.getattr("int").unwrap(),)).unwrap().extract().unwrap(),
int32: id_fn.call1((numpy_mod.getattr("int32").unwrap(),)).unwrap().extract().unwrap(), int32: id_fn.call1((numpy_mod.getattr("int32").unwrap(),)).unwrap().extract().unwrap(),
int64: id_fn.call1((numpy_mod.getattr("int64").unwrap(),)).unwrap().extract().unwrap(), int64: id_fn.call1((numpy_mod.getattr("int64").unwrap(),)).unwrap().extract().unwrap(),
uint32: id_fn.call1((numpy_mod.getattr("uint32").unwrap(),)).unwrap().extract().unwrap(),
uint64: id_fn.call1((numpy_mod.getattr("uint64").unwrap(),)).unwrap().extract().unwrap(),
bool: id_fn.call1((builtins_mod.getattr("bool").unwrap(),)).unwrap().extract().unwrap(), bool: id_fn.call1((builtins_mod.getattr("bool").unwrap(),)).unwrap().extract().unwrap(),
float: id_fn float: id_fn
.call1((builtins_mod.getattr("float").unwrap(),)) .call1((builtins_mod.getattr("float").unwrap(),))

View File

@ -24,6 +24,8 @@ use crate::PrimitivePythonId;
pub enum PrimitiveValue { pub enum PrimitiveValue {
I32(i32), I32(i32),
I64(i64), I64(i64),
U32(u32),
U64(u64),
F64(f64), F64(f64),
Bool(bool), Bool(bool),
} }
@ -115,6 +117,8 @@ impl StaticValue for PythonValue {
return Ok(match val { return Ok(match val {
PrimitiveValue::I32(val) => ctx.ctx.i32_type().const_int(*val as u64, false).into(), PrimitiveValue::I32(val) => ctx.ctx.i32_type().const_int(*val as u64, false).into(),
PrimitiveValue::I64(val) => ctx.ctx.i64_type().const_int(*val as u64, false).into(), PrimitiveValue::I64(val) => ctx.ctx.i64_type().const_int(*val as u64, false).into(),
PrimitiveValue::U32(val) => ctx.ctx.i32_type().const_int(*val as u64, false).into(),
PrimitiveValue::U64(val) => ctx.ctx.i64_type().const_int(*val as u64, false).into(),
PrimitiveValue::F64(val) => ctx.ctx.f64_type().const_float(*val).into(), PrimitiveValue::F64(val) => ctx.ctx.f64_type().const_float(*val).into(),
PrimitiveValue::Bool(val) => { PrimitiveValue::Bool(val) => {
ctx.ctx.bool_type().const_int(*val as u64, false).into() ctx.ctx.bool_type().const_int(*val as u64, false).into()
@ -238,6 +242,10 @@ impl InnerResolver {
Ok(Ok((primitives.int32, true))) Ok(Ok((primitives.int32, true)))
} else if ty_id == self.primitive_ids.int64 { } else if ty_id == self.primitive_ids.int64 {
Ok(Ok((primitives.int64, true))) Ok(Ok((primitives.int64, true)))
} else if ty_id == self.primitive_ids.uint32 {
Ok(Ok((primitives.uint32, true)))
} else if ty_id == self.primitive_ids.uint64 {
Ok(Ok((primitives.uint64, true)))
} else if ty_id == self.primitive_ids.bool { } else if ty_id == self.primitive_ids.bool {
Ok(Ok((primitives.bool, true))) Ok(Ok((primitives.bool, true)))
} else if ty_id == self.primitive_ids.float { } else if ty_id == self.primitive_ids.float {
@ -615,6 +623,16 @@ impl InnerResolver {
format!("{} is not in the range of int64", obj)))?; format!("{} is not in the range of int64", obj)))?;
self.id_to_primitive.write().insert(id, PrimitiveValue::I64(val)); self.id_to_primitive.write().insert(id, PrimitiveValue::I64(val));
Ok(Some(ctx.ctx.i64_type().const_int(val as u64, false).into())) Ok(Some(ctx.ctx.i64_type().const_int(val as u64, false).into()))
} else if ty_id == self.primitive_ids.uint32 {
let val: u32 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of uint32", obj)))?;
self.id_to_primitive.write().insert(id, PrimitiveValue::U32(val));
Ok(Some(ctx.ctx.i32_type().const_int(val as u64, false).into()))
} else if ty_id == self.primitive_ids.uint64 {
let val: u64 = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of uint64", obj)))?;
self.id_to_primitive.write().insert(id, PrimitiveValue::U64(val));
Ok(Some(ctx.ctx.i64_type().const_int(val, false).into()))
} else if ty_id == self.primitive_ids.bool { } else if ty_id == self.primitive_ids.bool {
let val: bool = obj.extract().map_err(|_| super::CompileError::new_err( let val: bool = obj.extract().map_err(|_| super::CompileError::new_err(
format!("{} is not in the range of bool", obj)))?; format!("{} is not in the range of bool", obj)))?;

View File

@ -28,6 +28,8 @@ pub struct ConcreteFuncArg {
pub enum Primitive { pub enum Primitive {
Int32, Int32,
Int64, Int64,
UInt32,
UInt64,
Float, Float,
Bool, Bool,
None, None,
@ -72,6 +74,8 @@ impl ConcreteTypeStore {
ConcreteTypeEnum::TPrimitive(Primitive::Range), ConcreteTypeEnum::TPrimitive(Primitive::Range),
ConcreteTypeEnum::TPrimitive(Primitive::Str), ConcreteTypeEnum::TPrimitive(Primitive::Str),
ConcreteTypeEnum::TPrimitive(Primitive::Exception), ConcreteTypeEnum::TPrimitive(Primitive::Exception),
ConcreteTypeEnum::TPrimitive(Primitive::UInt32),
ConcreteTypeEnum::TPrimitive(Primitive::UInt64),
], ],
} }
} }
@ -130,6 +134,10 @@ impl ConcreteTypeStore {
ConcreteType(6) ConcreteType(6)
} else if unifier.unioned(ty, primitives.exception) { } else if unifier.unioned(ty, primitives.exception) {
ConcreteType(7) ConcreteType(7)
} else if unifier.unioned(ty, primitives.uint32) {
ConcreteType(8)
} else if unifier.unioned(ty, primitives.uint64) {
ConcreteType(9)
} else if let Some(cty) = cache.get(&ty) { } else if let Some(cty) = cache.get(&ty) {
if let Some(cty) = cty { if let Some(cty) = cty {
*cty *cty
@ -223,6 +231,8 @@ impl ConcreteTypeStore {
let ty = match primitive { let ty = match primitive {
Primitive::Int32 => primitives.int32, Primitive::Int32 => primitives.int32,
Primitive::Int64 => primitives.int64, Primitive::Int64 => primitives.int64,
Primitive::UInt32 => primitives.uint32,
Primitive::UInt64 => primitives.uint64,
Primitive::Float => primitives.float, Primitive::Float => primitives.float,
Primitive::Bool => primitives.bool, Primitive::Bool => primitives.bool,
Primitive::None => primitives.none, Primitive::None => primitives.none,

View File

@ -94,6 +94,8 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
match val { match val {
SymbolValue::I32(v) => self.ctx.i32_type().const_int(*v as u64, true).into(), SymbolValue::I32(v) => self.ctx.i32_type().const_int(*v as u64, true).into(),
SymbolValue::I64(v) => self.ctx.i64_type().const_int(*v as u64, true).into(), SymbolValue::I64(v) => self.ctx.i64_type().const_int(*v as u64, true).into(),
SymbolValue::U32(v) => self.ctx.i32_type().const_int(*v as u64, false).into(),
SymbolValue::U64(v) => self.ctx.i64_type().const_int(*v as u64, false).into(),
SymbolValue::Bool(v) => self.ctx.bool_type().const_int(*v as u64, true).into(), SymbolValue::Bool(v) => self.ctx.bool_type().const_int(*v as u64, true).into(),
SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(), SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(),
SymbolValue::Str(v) => { SymbolValue::Str(v) => {
@ -152,9 +154,13 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
ty.const_int(if *v { 1 } else { 0 }, false).into() ty.const_int(if *v { 1 } else { 0 }, false).into()
} }
Constant::Int(Some(val)) => { Constant::Int(Some(val)) => {
let ty = if self.unifier.unioned(ty, self.primitives.int32) { let ty = if self.unifier.unioned(ty, self.primitives.int32)
|| self.unifier.unioned(ty, self.primitives.uint32)
{
self.ctx.i32_type() self.ctx.i32_type()
} else if self.unifier.unioned(ty, self.primitives.int64) { } else if self.unifier.unioned(ty, self.primitives.int64)
|| self.unifier.unioned(ty, self.primitives.uint64)
{
self.ctx.i64_type() self.ctx.i64_type()
} else { } else {
unreachable!(); unreachable!();
@ -201,6 +207,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
op: &Operator, op: &Operator,
lhs: BasicValueEnum<'ctx>, lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>, rhs: BasicValueEnum<'ctx>,
signed: bool
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let (lhs, rhs) = let (lhs, rhs) =
if let (BasicValueEnum::IntValue(lhs), BasicValueEnum::IntValue(rhs)) = (lhs, rhs) { if let (BasicValueEnum::IntValue(lhs), BasicValueEnum::IntValue(rhs)) = (lhs, rhs) {
@ -208,26 +215,33 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
} else { } else {
unreachable!() unreachable!()
}; };
match op { let float = self.ctx.f64_type();
Operator::Add => self.builder.build_int_add(lhs, rhs, "add").into(), match (op, signed) {
Operator::Sub => self.builder.build_int_sub(lhs, rhs, "sub").into(), (Operator::Add, _) => self.builder.build_int_add(lhs, rhs, "add").into(),
Operator::Mult => self.builder.build_int_mul(lhs, rhs, "mul").into(), (Operator::Sub, _) => self.builder.build_int_sub(lhs, rhs, "sub").into(),
Operator::Div => { (Operator::Mult, _) => self.builder.build_int_mul(lhs, rhs, "mul").into(),
let float = self.ctx.f64_type(); (Operator::Div, true) => {
let left = self.builder.build_signed_int_to_float(lhs, float, "i2f"); let left = self.builder.build_signed_int_to_float(lhs, float, "i2f");
let right = self.builder.build_signed_int_to_float(rhs, float, "i2f"); let right = self.builder.build_signed_int_to_float(rhs, float, "i2f");
self.builder.build_float_div(left, right, "fdiv").into() self.builder.build_float_div(left, right, "fdiv").into()
} }
Operator::Mod => self.builder.build_int_signed_rem(lhs, rhs, "mod").into(), (Operator::Div, false) => {
Operator::BitOr => self.builder.build_or(lhs, rhs, "or").into(), let left = self.builder.build_unsigned_int_to_float(lhs, float, "i2f");
Operator::BitXor => self.builder.build_xor(lhs, rhs, "xor").into(), let right = self.builder.build_unsigned_int_to_float(rhs, float, "i2f");
Operator::BitAnd => self.builder.build_and(lhs, rhs, "and").into(), self.builder.build_float_div(left, right, "fdiv").into()
Operator::LShift => self.builder.build_left_shift(lhs, rhs, "lshift").into(), }
Operator::RShift => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(), (Operator::Mod, true) => self.builder.build_int_signed_rem(lhs, rhs, "mod").into(),
Operator::FloorDiv => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(), (Operator::Mod, false) => self.builder.build_int_unsigned_rem(lhs, rhs, "mod").into(),
Operator::Pow => integer_power(self, lhs, rhs).into(), (Operator::BitOr, _) => self.builder.build_or(lhs, rhs, "or").into(),
(Operator::BitXor, _) => self.builder.build_xor(lhs, rhs, "xor").into(),
(Operator::BitAnd, _) => self.builder.build_and(lhs, rhs, "and").into(),
(Operator::LShift, _) => self.builder.build_left_shift(lhs, rhs, "lshift").into(),
(Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(),
(Operator::FloorDiv, true) => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(),
(Operator::FloorDiv, false) => self.builder.build_int_unsigned_div(lhs, rhs, "floordiv").into(),
(Operator::Pow, s) => integer_power(self, lhs, rhs, s).into(),
// special implementation? // special implementation?
Operator::MatMult => unreachable!(), (Operator::MatMult, _) => unreachable!(),
} }
} }
@ -807,7 +821,9 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
// which would be unchanged until further unification, which we would never do // which would be unchanged until further unification, which we would never do
// when doing code generation for function instances // when doing code generation for function instances
Ok(if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) { Ok(if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
ctx.gen_int_ops(op, left, right) ctx.gen_int_ops(op, left, right, true)
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
ctx.gen_int_ops(op, left, right, false)
} else if ty1 == ty2 && ctx.primitives.float == ty1 { } else if ty1 == ty2 && ctx.primitives.float == ty1 {
ctx.gen_float_ops(op, left, right) ctx.gen_float_ops(op, left, right)
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 { } else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {

View File

@ -26,6 +26,8 @@ typedef unsigned _ExtInt(64) uint64_t;
DEF_INT_EXP(int32_t) DEF_INT_EXP(int32_t)
DEF_INT_EXP(int64_t) DEF_INT_EXP(int64_t)
DEF_INT_EXP(uint32_t)
DEF_INT_EXP(uint64_t)
int32_t __nac3_slice_index_bound(int32_t i, const int32_t len) { int32_t __nac3_slice_index_bound(int32_t i, const int32_t len) {

View File

@ -37,10 +37,13 @@ pub fn integer_power<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
base: IntValue<'ctx>, base: IntValue<'ctx>,
exp: IntValue<'ctx>, exp: IntValue<'ctx>,
signed: bool,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width()) { let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width(), signed) {
(32, 32) => "__nac3_int_exp_int32_t", (32, 32, true) => "__nac3_int_exp_int32_t",
(64, 64) => "__nac3_int_exp_int64_t", (64, 64, true) => "__nac3_int_exp_int64_t",
(32, 32, false) => "__nac3_int_exp_uint32_t",
(64, 64, false) => "__nac3_int_exp_uint64_t",
_ => unreachable!(), _ => unreachable!(),
}; };
let base_type = base.get_type(); let base_type = base.get_type();

View File

@ -360,6 +360,8 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
let primitives = PrimitiveStore { let primitives = PrimitiveStore {
int32: unifier.get_representative(primitives.int32), int32: unifier.get_representative(primitives.int32),
int64: unifier.get_representative(primitives.int64), int64: unifier.get_representative(primitives.int64),
uint32: unifier.get_representative(primitives.uint32),
uint64: unifier.get_representative(primitives.uint64),
float: unifier.get_representative(primitives.float), float: unifier.get_representative(primitives.float),
bool: unifier.get_representative(primitives.bool), bool: unifier.get_representative(primitives.bool),
none: unifier.get_representative(primitives.none), none: unifier.get_representative(primitives.none),
@ -371,6 +373,8 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
let mut type_cache: HashMap<_, _> = [ let mut type_cache: HashMap<_, _> = [
(primitives.int32, context.i32_type().into()), (primitives.int32, context.i32_type().into()),
(primitives.int64, context.i64_type().into()), (primitives.int64, context.i64_type().into()),
(primitives.uint32, context.i32_type().into()),
(primitives.uint64, context.i64_type().into()),
(primitives.float, context.f64_type().into()), (primitives.float, context.f64_type().into()),
(primitives.bool, context.bool_type().into()), (primitives.bool, context.bool_type().into()),
(primitives.str, { (primitives.str, {

View File

@ -23,6 +23,8 @@ use parking_lot::RwLock;
pub enum SymbolValue { pub enum SymbolValue {
I32(i32), I32(i32),
I64(i64), I64(i64),
U32(u32),
U64(u64),
Str(String), Str(String),
Double(f64), Double(f64),
Bool(bool), Bool(bool),
@ -34,6 +36,8 @@ impl Display for SymbolValue {
match self { match self {
SymbolValue::I32(i) => write!(f, "{}", i), SymbolValue::I32(i) => write!(f, "{}", i),
SymbolValue::I64(i) => write!(f, "int64({})", i), SymbolValue::I64(i) => write!(f, "int64({})", i),
SymbolValue::U32(i) => write!(f, "uint32({})", i),
SymbolValue::U64(i) => write!(f, "uint64({})", i),
SymbolValue::Str(s) => write!(f, "\"{}\"", s), SymbolValue::Str(s) => write!(f, "\"{}\"", s),
SymbolValue::Double(d) => write!(f, "{}", d), SymbolValue::Double(d) => write!(f, "{}", d),
SymbolValue::Bool(b) => { SymbolValue::Bool(b) => {
@ -141,7 +145,7 @@ pub trait SymbolResolver {
} }
thread_local! { thread_local! {
static IDENTIFIER_ID: [StrRef; 10] = [ static IDENTIFIER_ID: [StrRef; 12] = [
"int32".into(), "int32".into(),
"int64".into(), "int64".into(),
"float".into(), "float".into(),
@ -152,6 +156,8 @@ thread_local! {
"tuple".into(), "tuple".into(),
"str".into(), "str".into(),
"Exception".into(), "Exception".into(),
"uint32".into(),
"uint64".into(),
]; ];
} }
@ -175,12 +181,18 @@ pub fn parse_type_annotation<T>(
let tuple_id = ids[7]; let tuple_id = ids[7];
let str_id = ids[8]; let str_id = ids[8];
let exn_id = ids[9]; let exn_id = ids[9];
let uint32_id = ids[10];
let uint64_id = ids[11];
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| { let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
if *id == int32_id { if *id == int32_id {
Ok(primitives.int32) Ok(primitives.int32)
} else if *id == int64_id { } else if *id == int64_id {
Ok(primitives.int64) Ok(primitives.int64)
} else if *id == uint32_id {
Ok(primitives.uint32)
} else if *id == uint64_id {
Ok(primitives.uint64)
} else if *id == float_id { } else if *id == float_id {
Ok(primitives.float) Ok(primitives.float)
} else if *id == bool_id { } else if *id == bool_id {

View File

@ -12,12 +12,14 @@ type BuiltinInfo = (Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>, &'static [&'s
pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let int32 = primitives.0.int32; let int32 = primitives.0.int32;
let int64 = primitives.0.int64; let int64 = primitives.0.int64;
let uint32 = primitives.0.uint32;
let uint64 = primitives.0.uint64;
let float = primitives.0.float; let float = primitives.0.float;
let boolean = primitives.0.bool; let boolean = primitives.0.bool;
let range = primitives.0.range; let range = primitives.0.range;
let string = primitives.0.str; let string = primitives.0.str;
let num_ty = primitives.1.get_fresh_var_with_range( let num_ty = primitives.1.get_fresh_var_with_range(
&[int32, int64, float, boolean], &[int32, int64, float, boolean, uint32, uint64],
Some("N".into()), Some("N".into()),
None, None,
); );
@ -35,12 +37,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
("__param2__".into(), int64, true), ("__param2__".into(), int64, true),
]; ];
let div_by_zero = primitives.1.add_ty(TypeEnum::TObj { let div_by_zero = primitives.1.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10), obj_id: DefinitionId(12),
fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(), fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(),
params: Default::default(), params: Default::default(),
}); });
let index_error = primitives.1.add_ty(TypeEnum::TObj { let index_error = primitives.1.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(11), obj_id: DefinitionId(13),
fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(), fields: exception_fields.iter().map(|(a, b, c)| (*a, (*b, *c))).collect(),
params: Default::default(), params: Default::default(),
}); });
@ -125,6 +127,20 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
resolver: None, resolver: None,
loc: None, loc: None,
})), })),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
8,
None,
"uint32".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
9,
None,
"uint64".into(),
None,
None,
))),
Arc::new(RwLock::new(TopLevelDef::Function { Arc::new(RwLock::new(TopLevelDef::Function {
name: "ZeroDivisionError.__init__".into(), name: "ZeroDivisionError.__init__".into(),
simple_name: "__init__".into(), simple_name: "__init__".into(),
@ -149,7 +165,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
})), })),
Arc::new(RwLock::new(TopLevelDef::Class { Arc::new(RwLock::new(TopLevelDef::Class {
name: "ZeroDivisionError".into(), name: "ZeroDivisionError".into(),
object_id: DefinitionId(10), object_id: DefinitionId(12),
type_vars: Default::default(), type_vars: Default::default(),
fields: exception_fields.clone(), fields: exception_fields.clone(),
methods: vec![("__init__".into(), div_by_zero_signature, DefinitionId(8))], methods: vec![("__init__".into(), div_by_zero_signature, DefinitionId(8))],
@ -163,7 +179,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
})), })),
Arc::new(RwLock::new(TopLevelDef::Class { Arc::new(RwLock::new(TopLevelDef::Class {
name: "IndexError".into(), name: "IndexError".into(),
object_id: DefinitionId(11), object_id: DefinitionId(13),
type_vars: Default::default(), type_vars: Default::default(),
fields: exception_fields, fields: exception_fields,
methods: vec![("__init__".into(), index_error_signature, DefinitionId(9))], methods: vec![("__init__".into(), index_error_signature, DefinitionId(9))],
@ -295,6 +311,46 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
)))), )))),
loc: None, loc: None,
})), })),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "uint32".into(),
simple_name: "uint32".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: uint32,
vars: var_map.clone(),
})),
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(
|ctx, _, fun, args, generator| {
// TODO:
unimplemented!()
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "uint64".into(),
simple_name: "uint64".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: num_ty.0, default_value: None }],
ret: uint64,
vars: var_map.clone(),
})),
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(
|ctx, _, fun, args, generator| {
// TODO:
unimplemented!()
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function { Arc::new(RwLock::new(TopLevelDef::Function {
name: "float".into(), name: "float".into(),
simple_name: "float".into(), simple_name: "float".into(),
@ -797,6 +853,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
"IndexError", "IndexError",
"int32", "int32",
"int64", "int64",
"uint32",
"uint64",
"float", "float",
"round", "round",
"round64", "round64",

View File

@ -62,6 +62,8 @@ impl TopLevelComposer {
"tuple".into(), "tuple".into(),
"int32".into(), "int32".into(),
"int64".into(), "int64".into(),
"uint32".into(),
"uint64".into(),
"float".into(), "float".into(),
"bool".into(), "bool".into(),
"none".into(), "none".into(),

View File

@ -97,7 +97,17 @@ impl TopLevelComposer {
.collect::<HashMap<_, _>>(), .collect::<HashMap<_, _>>(),
params: HashMap::new(), params: HashMap::new(),
}); });
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception }; let uint32 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(8),
fields: HashMap::new(),
params: HashMap::new(),
});
let uint64 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(9),
fields: HashMap::new(),
params: HashMap::new(),
});
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 };
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier); crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
(primitives, unifier) (primitives, unifier)
} }
@ -399,6 +409,20 @@ impl TopLevelComposer {
Some("int64".to_string()) Some("int64".to_string())
} }
} }
SymbolValue::U32(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.uint32) {
None
} else {
Some("uint32".to_string())
}
}
SymbolValue::U64(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.uint64) {
None
} else {
Some("uint64".to_string())
}
}
SymbolValue::Str(..) => { SymbolValue::Str(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.str) { if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.str) {
None None
@ -475,6 +499,48 @@ pub fn parse_parameter_default_value(
Err(format!("only allow constant integer here at {}", default.location)) Err(format!("only allow constant integer here at {}", default.location))
} }
} }
ast::ExprKind::Call { func, args, .. } if {
match &func.node {
ast::ExprKind::Name { id, .. } => *id == "uint32".into(),
_ => false,
}
} => {
if args.len() == 1 {
match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(Some(v)), .. } => {
let v: Result<u32, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U32(v)),
_ => Err(format!("default param value out of range at {}", default.location))
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
}
} else {
Err(format!("only allow constant integer here at {}", default.location))
}
}
ast::ExprKind::Call { func, args, .. } if {
match &func.node {
ast::ExprKind::Name { id, .. } => *id == "uint64".into(),
_ => false,
}
} => {
if args.len() == 1 {
match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(Some(v)), .. } => {
let v: Result<u64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U64(v)),
_ => Err(format!("default param value out of range at {}", default.location))
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
}
} else {
Err(format!("only allow constant integer here at {}", default.location))
}
}
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts
.iter() .iter()
.map(|x| parse_parameter_default_value(x, resolver)) .map(|x| parse_parameter_default_value(x, resolver))

View File

@ -64,6 +64,10 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
Ok(TypeAnnotation::Primitive(primitives.int32)) Ok(TypeAnnotation::Primitive(primitives.int32))
} else if id == &"int64".into() { } else if id == &"int64".into() {
Ok(TypeAnnotation::Primitive(primitives.int64)) Ok(TypeAnnotation::Primitive(primitives.int64))
} else if id == &"uint32".into() {
Ok(TypeAnnotation::Primitive(primitives.uint32))
} else if id == &"uint64".into() {
Ok(TypeAnnotation::Primitive(primitives.uint64))
} else if id == &"float".into() { } else if id == &"float".into() {
Ok(TypeAnnotation::Primitive(primitives.float)) Ok(TypeAnnotation::Primitive(primitives.float))
} else if id == &"bool".into() { } else if id == &"bool".into() {

View File

@ -286,35 +286,32 @@ pub fn impl_eq(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
} }
pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifier) { pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifier) {
let PrimitiveStore { int32: int32_t, int64: int64_t, float: float_t, bool: bool_t, .. } = let PrimitiveStore {
*store; int32: int32_t,
/* int32 ======== */ int64: int64_t,
impl_basic_arithmetic(unifier, store, int32_t, &[int32_t], int32_t); float: float_t,
impl_pow(unifier, store, int32_t, &[int32_t], int32_t); bool: bool_t,
impl_bitwise_arithmetic(unifier, store, int32_t); uint32: uint32_t,
impl_bitwise_shift(unifier, store, int32_t); uint64: uint64_t,
impl_div(unifier, store, int32_t, &[int32_t]); ..
impl_floordiv(unifier, store, int32_t, &[int32_t], int32_t); } = *store;
impl_mod(unifier, store, int32_t, &[int32_t], int32_t); /* int ======== */
impl_sign(unifier, store, int32_t); for t in [int32_t, int64_t, uint32_t, uint64_t] {
impl_invert(unifier, store, int32_t); impl_basic_arithmetic(unifier, store, t, &[t], t);
impl_not(unifier, store, int32_t); impl_pow(unifier, store, t, &[t], t);
impl_comparison(unifier, store, int32_t, int32_t); impl_bitwise_arithmetic(unifier, store, t);
impl_eq(unifier, store, int32_t); impl_bitwise_shift(unifier, store, t);
impl_div(unifier, store, t, &[t]);
/* int64 ======== */ impl_floordiv(unifier, store, t, &[t], t);
impl_basic_arithmetic(unifier, store, int64_t, &[int64_t], int64_t); impl_mod(unifier, store, t, &[t], t);
impl_pow(unifier, store, int64_t, &[int64_t], int64_t); impl_invert(unifier, store, t);
impl_bitwise_arithmetic(unifier, store, int64_t); impl_not(unifier, store, t);
impl_bitwise_shift(unifier, store, int64_t); impl_comparison(unifier, store, t, t);
impl_div(unifier, store, int64_t, &[int64_t]); impl_eq(unifier, store, t);
impl_floordiv(unifier, store, int64_t, &[int64_t], int64_t); }
impl_mod(unifier, store, int64_t, &[int64_t], int64_t); for t in [int32_t, int64_t] {
impl_sign(unifier, store, int64_t); impl_sign(unifier, store, t);
impl_invert(unifier, store, int64_t); }
impl_not(unifier, store, int64_t);
impl_comparison(unifier, store, int64_t, int64_t);
impl_eq(unifier, store, int64_t);
/* float ======== */ /* float ======== */
impl_basic_arithmetic(unifier, store, float_t, &[float_t], float_t); impl_basic_arithmetic(unifier, store, float_t, &[float_t], float_t);

View File

@ -32,6 +32,8 @@ impl From<Location> for CodeLocation {
pub struct PrimitiveStore { pub struct PrimitiveStore {
pub int32: Type, pub int32: Type,
pub int64: Type, pub int64: Type,
pub uint32: Type,
pub uint64: Type,
pub float: Type, pub float: Type,
pub bool: Type, pub bool: Type,
pub none: Type, pub none: Type,
@ -779,8 +781,56 @@ impl<'a> Inferencer<'a> {
&args[0].node &args[0].node
{ {
let custom = Some(self.primitives.int64); let custom = Some(self.primitives.int64);
if val.is_none() { match val {
return report_error("Integer out of bound", args[0].location); Some(val) if {
let v: Result<i64, _> = (*val).try_into();
v.is_ok()
} => {},
_ => return report_error("Integer out of bound", args[0].location)
}
return Ok(Located {
location: args[0].location,
custom,
node: ExprKind::Constant {
value: ast::Constant::Int(*val),
kind: kind.clone(),
},
});
}
}
if id == "uint32".into() && args.len() == 1 {
if let ExprKind::Constant { value: ast::Constant::Int(val), kind } =
&args[0].node
{
let custom = Some(self.primitives.uint32);
match val {
Some(val) if {
let v: Result<u32, _> = (*val).try_into();
v.is_ok()
} => {},
_ => return report_error("Integer out of bound", args[0].location)
}
return Ok(Located {
location: args[0].location,
custom,
node: ExprKind::Constant {
value: ast::Constant::Int(*val),
kind: kind.clone(),
},
});
}
}
if id == "uint64".into() && args.len() == 1 {
if let ExprKind::Constant { value: ast::Constant::Int(val), kind } =
&args[0].node
{
let custom = Some(self.primitives.uint64);
match val {
Some(val) if {
let v: Result<u64, _> = (*val).try_into();
v.is_ok()
} => {},
_ => return report_error("Integer out of bound", args[0].location)
} }
return Ok(Located { return Ok(Located {
location: args[0].location, location: args[0].location,
@ -876,7 +926,7 @@ impl<'a> Inferencer<'a> {
match val { match val {
Some(val) => { Some(val) => {
let int32: Result<i32, _> = (*val).try_into(); let int32: Result<i32, _> = (*val).try_into();
// int64 is handled separately in functions // int64 and unsigned integers are handled separately in functions
if int32.is_ok() { if int32.is_ok() {
Ok(self.primitives.int32) Ok(self.primitives.int32)
} else { } else {