Skip to content

Commit

Permalink
feat: add support for static arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
jac3km4 committed May 30, 2024
1 parent d2caf17 commit a3c3401
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 114 deletions.
140 changes: 91 additions & 49 deletions compiler/src/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use itertools::Itertools;
use redscript::ast::{Constant, Expr, Ident, Intrinsic, Literal, Seq, Span, TypeName};
use redscript::bundle::{ConstantPool, PoolIndex};
use redscript::bytecode::{Code, Instr, Label, Location, Offset};
use redscript::definition::{Definition, Function, Local};
use redscript::definition::{Definition, Function, Local, Type};

use crate::error::{Cause, Error, ResultSpan};
use crate::scope::{Reference, Scope, TypeId, Value};
Expand Down Expand Up @@ -308,55 +308,72 @@ impl<'a> Assembler<'a> {
scope: &mut Scope,
pool: &mut ConstantPool,
) -> Result<(), Cause> {
let mut emit_assignment = |instr: Instr<Label>| {
self.emit(Instr::Assign);
self.emit(Instr::Local(local));
self.emit(instr);
};

match typ {
TypeId::Prim(typ_idx) => match Ident::from_heap(pool.def_name(typ_idx)?) {
tp if tp == TypeName::BOOL.name() => emit_assignment(Instr::FalseConst),
tp if tp == TypeName::INT8.name() => emit_assignment(Instr::I8Const(0)),
tp if tp == TypeName::INT16.name() => emit_assignment(Instr::I16Const(0)),
tp if tp == TypeName::INT32.name() => emit_assignment(Instr::I32Zero),
tp if tp == TypeName::INT64.name() => emit_assignment(Instr::I64Const(0)),
tp if tp == TypeName::UINT8.name() => emit_assignment(Instr::U8Const(0)),
tp if tp == TypeName::UINT16.name() => emit_assignment(Instr::U16Const(0)),
tp if tp == TypeName::UINT32.name() => emit_assignment(Instr::U32Const(0)),
tp if tp == TypeName::UINT64.name() => emit_assignment(Instr::U64Const(0)),
tp if tp == TypeName::FLOAT.name() => emit_assignment(Instr::F32Const(0.0)),
tp if tp == TypeName::DOUBLE.name() => emit_assignment(Instr::F64Const(0.0)),
tp if tp == TypeName::STRING.name() => {
let empty = pool.strings.add("".into());
emit_assignment(Instr::StringConst(empty));
}
tp if tp == TypeName::CNAME.name() => emit_assignment(Instr::NameConst(PoolIndex::UNDEFINED)),
tp if tp == TypeName::TWEAKDB_ID.name() => emit_assignment(Instr::TweakDbIdConst(PoolIndex::UNDEFINED)),
tp if tp == TypeName::RESOURCE.name() => emit_assignment(Instr::ResourceConst(PoolIndex::UNDEFINED)),
_ => {}
},
TypeId::Struct(struct_) => {
emit_assignment(Instr::Construct(0, struct_));
}
TypeId::Enum(enum_idx) => {
let enum_ = pool.enum_(enum_idx)?;
if let Some(member) = enum_.members.first() {
emit_assignment(Instr::EnumConst(enum_idx, *member));
fn get_initializer(typ: &TypeId, pool: &mut ConstantPool) -> Result<Option<Instr<Label>>, Cause> {
let res = match typ {
&TypeId::Prim(typ_idx) => match Ident::from_heap(pool.def_name(typ_idx)?) {
tp if tp == TypeName::BOOL.name() => Some(Instr::FalseConst),
tp if tp == TypeName::INT8.name() => Some(Instr::I8Const(0)),
tp if tp == TypeName::INT16.name() => Some(Instr::I16Const(0)),
tp if tp == TypeName::INT32.name() => Some(Instr::I32Zero),
tp if tp == TypeName::INT64.name() => Some(Instr::I64Const(0)),
tp if tp == TypeName::UINT8.name() => Some(Instr::U8Const(0)),
tp if tp == TypeName::UINT16.name() => Some(Instr::U16Const(0)),
tp if tp == TypeName::UINT32.name() => Some(Instr::U32Const(0)),
tp if tp == TypeName::UINT64.name() => Some(Instr::U64Const(0)),
tp if tp == TypeName::FLOAT.name() => Some(Instr::F32Const(0.0)),
tp if tp == TypeName::DOUBLE.name() => Some(Instr::F64Const(0.0)),
tp if tp == TypeName::STRING.name() => {
let empty = pool.strings.add("".into());
Some(Instr::StringConst(empty))
}
tp if tp == TypeName::CNAME.name() => Some(Instr::NameConst(PoolIndex::UNDEFINED)),
tp if tp == TypeName::TWEAKDB_ID.name() => Some(Instr::TweakDbIdConst(PoolIndex::UNDEFINED)),
tp if tp == TypeName::RESOURCE.name() => Some(Instr::ResourceConst(PoolIndex::UNDEFINED)),
_ => None,
},
&TypeId::Struct(struct_) => Some(Instr::Construct(0, struct_)),
&TypeId::Enum(enum_idx) => {
let enum_ = pool.enum_(enum_idx)?;
enum_.members.first().map(|member| Instr::EnumConst(enum_idx, *member))
}
}
TypeId::Ref(_) => {
emit_assignment(Instr::Null);
}
TypeId::WeakRef(_) => {
emit_assignment(Instr::WeakRefNull);
}
TypeId::Ref(_) => Some(Instr::Null),
TypeId::WeakRef(_) => Some(Instr::WeakRefNull),
TypeId::Array(_) | TypeId::StaticArray(_, _) => {
return Err(Cause::UnsupportedFeature(
"initializing a static array with another array",
));
}
_ => None,
};
Ok(res)
}

match &typ {
TypeId::Array(_) => {
self.emit(Instr::ArrayClear(scope.get_type_index(&typ, pool)?));
self.emit(Instr::Local(local));
}
_ => {}
TypeId::StaticArray(elem, size) => {
if let Some(instr) = get_initializer(elem, pool)? {
let type_idx = scope.get_type_index(&typ, pool)?;
for i in 0..*size {
self.emit(Instr::Assign);
self.emit(Instr::StaticArrayElement(type_idx));
self.emit(Instr::Local(local));
self.emit(Instr::U32Const(i));
self.emit(instr.clone());
}
}
}
_ => {
if let Some(instr) = get_initializer(&typ, pool)? {
self.emit(Instr::Assign);
self.emit(Instr::Local(local));
self.emit(instr);
}
}
}

Ok(())
}

Expand Down Expand Up @@ -460,22 +477,47 @@ impl<'a> Assembler<'a> {
self.emit(Instr::ArrayClear(get_arg_type(0)?));
}
Intrinsic::ArraySize => {
self.emit(Instr::ArraySize(get_arg_type(0)?));
let idx = get_arg_type(0)?;
if matches!(pool.type_(idx)?, Type::StaticArray(_, _)) {
self.emit(Instr::StaticArraySize(idx));
} else {
self.emit(Instr::ArraySize(idx));
}
}
Intrinsic::ArrayResize => {
self.emit(Instr::ArrayResize(get_arg_type(0)?));
}
Intrinsic::ArrayFindFirst => {
self.emit(Instr::ArrayFindFirst(get_arg_type(0)?));
let idx = get_arg_type(0)?;
if matches!(pool.type_(idx)?, Type::StaticArray(_, _)) {
self.emit(Instr::StaticArrayFindFirst(idx));
} else {
self.emit(Instr::ArrayFindFirst(idx));
}
}
Intrinsic::ArrayFindLast => {
self.emit(Instr::ArrayFindLast(get_arg_type(0)?));
let idx = get_arg_type(0)?;
if matches!(pool.type_(idx)?, Type::StaticArray(_, _)) {
self.emit(Instr::StaticArrayFindLast(idx));
} else {
self.emit(Instr::ArrayFindLast(idx));
}
}
Intrinsic::ArrayContains => {
self.emit(Instr::ArrayContains(get_arg_type(0)?));
let idx = get_arg_type(0)?;
if matches!(pool.type_(idx)?, Type::StaticArray(_, _)) {
self.emit(Instr::StaticArrayContains(idx));
} else {
self.emit(Instr::ArrayContains(idx));
}
}
Intrinsic::ArrayCount => {
self.emit(Instr::ArrayCount(get_arg_type(0)?));
let idx = get_arg_type(0)?;
if matches!(pool.type_(idx)?, Type::StaticArray(_, _)) {
self.emit(Instr::StaticArrayCount(idx));
} else {
self.emit(Instr::ArrayCount(idx));
}
}
Intrinsic::ArrayPush => {
self.emit(Instr::ArrayPush(get_arg_type(0)?));
Expand Down
17 changes: 12 additions & 5 deletions compiler/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,11 @@ peg::parser! {
rule keyword(id: &'static str) -> () =
##parse_string_literal(id) !['0'..='9' | 'a'..='z' | 'A'..='Z' | '_']

rule number_str() -> &'input str
= str:$(['-']? ['0'..='9' | '.']+)

rule number() -> Constant
= str:$(['-']? ['0'..='9' | '.']+) unsigned: $(['u'])? postfix:$(['l' | 'd'])?
= str:number_str() unsigned: $(['u'])? postfix:$(['l' | 'd'])?
{? if postfix == Some("d") { str.parse::<f64>().or(Err("valid double")).map(Constant::F64) }
else if str.contains('.') { str.parse::<f32>().or(Err("valid float")).map(Constant::F32) }
else if postfix == Some("l") && unsigned.is_some() { str.parse::<u64>().or(Err("valid unsigned 64-bit int")).map(Constant::U64) }
Expand Down Expand Up @@ -265,6 +268,10 @@ peg::parser! {

rule type_() -> TypeName
= name:ident() args:type_args()? { TypeName::new(name, args.unwrap_or_default()) }
/ "[" _ type_:type_() _ "]" { TypeName::Array(type_.into()) }
/ "[" _ type_:type_() _ ";" _ size:number_str() "]" {?
Ok(TypeName::StaticArray(type_.into(), size.parse().or(Err("valid 32-bit integer"))?))
}
rule type_args() -> Vec<TypeName> = "<" _ args:commasep(<type_()>) _ ">" { args }

rule let_type() -> TypeName = ":" _ type_:type_() { type_ }
Expand Down Expand Up @@ -520,7 +527,7 @@ mod tests {
.unwrap();
assert_eq!(
format!("{:?}", module.entries),
r#"[Class(ClassSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Public]), name: "A", span: Span { low: Pos(0), high: Pos(14) } }, base: Some("IScriptable"), members: [Field(FieldSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Private, Const]), name: "m_field", span: Span { low: Pos(53), high: Pos(78) } }, type_: TypeName { name: "Int32", arguments: None }, default: None }), Function(FunctionSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Public]), name: "GetField", span: Span { low: Pos(104), high: Pos(124) } }, type_: Some(TypeName { name: "Int32", arguments: None }), parameters: [], body: Some(Seq { exprs: [Return(Some(Member(This(Span { low: Pos(165), high: Pos(169) }), "m_field", Span { low: Pos(165), high: Pos(177) })), Span { low: Pos(158), high: Pos(178) })] }), span: Span { low: Pos(104), high: Pos(196) } })], span: Span { low: Pos(0), high: Pos(211) } })]"#
r#"[Class(ClassSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Public]), name: "A", span: Span { low: Pos(0), high: Pos(14) } }, base: Some("IScriptable"), members: [Field(FieldSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Private, Const]), name: "m_field", span: Span { low: Pos(53), high: Pos(78) } }, type_: Named { name: "Int32", args: None }, default: None }), Function(FunctionSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Public]), name: "GetField", span: Span { low: Pos(104), high: Pos(124) } }, type_: Some(Named { name: "Int32", args: None }), parameters: [], body: Some(Seq { exprs: [Return(Some(Member(This(Span { low: Pos(165), high: Pos(169) }), "m_field", Span { low: Pos(165), high: Pos(177) })), Span { low: Pos(158), high: Pos(178) })] }), span: Span { low: Pos(104), high: Pos(196) } })], span: Span { low: Pos(0), high: Pos(211) } })]"#
);
}

Expand All @@ -535,7 +542,7 @@ mod tests {
.unwrap();
assert_eq!(
format!("{:?}", module.entries),
r#"[Function(FunctionSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Public, Static]), name: "GetField", span: Span { low: Pos(0), high: Pos(27) } }, type_: Some(TypeName { name: "Uint64", arguments: None }), parameters: [ParameterSource { qualifiers: Qualifiers([]), name: "optimum", type_: TypeName { name: "Uint64", arguments: None } }], body: Some(Seq { exprs: [Return(Some(Conditional(BinOp(Member(This(Span { low: Pos(80), high: Pos(84) }), "m_field", Span { low: Pos(80), high: Pos(92) }), Ident("optimum", Span { low: Pos(95), high: Pos(102) }), Greater, Span { low: Pos(80), high: Pos(102) }), Member(This(Span { low: Pos(105), high: Pos(109) }), "m_field", Span { low: Pos(105), high: Pos(117) }), Ident("optimum", Span { low: Pos(120), high: Pos(127) }), Span { low: Pos(80), high: Pos(127) })), Span { low: Pos(73), high: Pos(128) })] }), span: Span { low: Pos(0), high: Pos(143) } })]"#
r#"[Function(FunctionSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Public, Static]), name: "GetField", span: Span { low: Pos(0), high: Pos(27) } }, type_: Some(Named { name: "Uint64", args: None }), parameters: [ParameterSource { qualifiers: Qualifiers([]), name: "optimum", type_: Named { name: "Uint64", args: None } }], body: Some(Seq { exprs: [Return(Some(Conditional(BinOp(Member(This(Span { low: Pos(80), high: Pos(84) }), "m_field", Span { low: Pos(80), high: Pos(92) }), Ident("optimum", Span { low: Pos(95), high: Pos(102) }), Greater, Span { low: Pos(80), high: Pos(102) }), Member(This(Span { low: Pos(105), high: Pos(109) }), "m_field", Span { low: Pos(105), high: Pos(117) }), Ident("optimum", Span { low: Pos(120), high: Pos(127) }), Span { low: Pos(80), high: Pos(127) })), Span { low: Pos(73), high: Pos(128) })] }), span: Span { low: Pos(0), high: Pos(143) } })]"#
);
}

Expand Down Expand Up @@ -630,7 +637,7 @@ mod tests {
.unwrap();
assert_eq!(
format!("{:?}", module.entries),
r#"[Class(ClassSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([]), name: "Test", span: Span { low: Pos(101), high: Pos(111) } }, base: None, members: [Field(FieldSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Private]), name: "m_field", span: Span { low: Pos(130), high: Pos(149) } }, type_: TypeName { name: "String", arguments: None }, default: None })], span: Span { low: Pos(101), high: Pos(189) } })]"#
r#"[Class(ClassSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([]), name: "Test", span: Span { low: Pos(101), high: Pos(111) } }, base: None, members: [Field(FieldSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Private]), name: "m_field", span: Span { low: Pos(130), high: Pos(149) } }, type_: Named { name: "String", args: None }, default: None })], span: Span { low: Pos(101), high: Pos(189) } })]"#
);
}

Expand All @@ -648,7 +655,7 @@ mod tests {
.unwrap();
assert_eq!(
format!("{:?}", module.entries),
r#"[Class(ClassSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([]), name: "Test", span: Span { low: Pos(13), high: Pos(23) } }, base: None, members: [Field(FieldSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Private]), name: "m_field", span: Span { low: Pos(114), high: Pos(133) } }, type_: TypeName { name: "String", arguments: None }, default: None })], span: Span { low: Pos(13), high: Pos(156) } })]"#
r#"[Class(ClassSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([]), name: "Test", span: Span { low: Pos(13), high: Pos(23) } }, base: None, members: [Field(FieldSource { declaration: Declaration { annotations: [], qualifiers: Qualifiers([Private]), name: "m_field", span: Span { low: Pos(114), high: Pos(133) } }, type_: Named { name: "String", args: None }, default: None })], span: Span { low: Pos(13), high: Pos(156) } })]"#
);
}

Expand Down
7 changes: 5 additions & 2 deletions compiler/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ impl Scope {
(Kind::WRef, [nested]) => TypeId::WeakRef(Box::new(self.resolve_type(nested, pool)?)),
(Kind::ScriptRef, [nested]) => TypeId::ScriptRef(Box::new(self.resolve_type(nested, pool)?)),
(Kind::Array, [nested]) => TypeId::Array(Box::new(self.resolve_type(nested, pool)?)),
(Kind::StaticArray(size), [nested]) => {
TypeId::StaticArray(Box::new(self.resolve_type(nested, pool)?), size)
}
_ => match self.symbols.find(&name.repr()) {
Some(Symbol::Class(idx, _)) => TypeId::Class(*idx),
Some(Symbol::Struct(idx, _)) => TypeId::Struct(*idx),
Expand Down Expand Up @@ -363,8 +366,8 @@ impl TypeId {
Self::Enum(idx) => Ok(Ident::from_heap(pool.def_name(*idx)?)),
Self::Ref(idx) => Ok(str_fmt!("ref<{}>", idx.pretty(pool)?)),
Self::WeakRef(idx) => Ok(str_fmt!("wref<{}>", idx.pretty(pool)?)),
Self::Array(idx) => Ok(str_fmt!("array<{}>", idx.pretty(pool)?)),
Self::StaticArray(idx, size) => Ok(str_fmt!("array<{}, {}>", idx.pretty(pool)?, size)),
Self::Array(idx) => Ok(str_fmt!("[{}]", idx.pretty(pool)?)),
Self::StaticArray(idx, size) => Ok(str_fmt!("[{}; {}]", idx.pretty(pool)?, size)),
Self::ScriptRef(idx) => Ok(str_fmt!("script_ref<{}>", idx.pretty(pool)?)),
Self::Variant => Ok(Ident::from_static("Variant")),
Self::Null => Ok(Ident::from_static("Null")),
Expand Down
Loading

0 comments on commit a3c3401

Please sign in to comment.