Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions capc/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub enum Item {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Function {
pub name: Ident,
pub type_params: Vec<Ident>,
pub params: Vec<Param>,
pub ret: Type,
pub body: Block,
Expand All @@ -70,6 +71,7 @@ pub struct Function {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExternFunction {
pub name: Ident,
pub type_params: Vec<Ident>,
pub params: Vec<Param>,
pub ret: Type,
pub is_pub: bool,
Expand All @@ -79,6 +81,7 @@ pub struct ExternFunction {

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImplBlock {
pub type_params: Vec<Ident>,
pub target: Type,
pub methods: Vec<Function>,
pub doc: Option<String>,
Expand All @@ -94,6 +97,7 @@ pub struct Param {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructDecl {
pub name: Ident,
pub type_params: Vec<Ident>,
pub fields: Vec<Field>,
pub is_pub: bool,
pub is_opaque: bool,
Expand All @@ -107,6 +111,7 @@ pub struct StructDecl {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumDecl {
pub name: Ident,
pub type_params: Vec<Ident>,
pub variants: Vec<EnumVariant>,
pub is_pub: bool,
pub doc: Option<String>,
Expand Down Expand Up @@ -215,6 +220,7 @@ pub enum Expr {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructLiteralExpr {
pub path: Path,
pub type_args: Vec<Type>,
pub fields: Vec<StructLiteralField>,
pub span: Span,
}
Expand Down Expand Up @@ -270,6 +276,7 @@ impl fmt::Display for Path {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallExpr {
pub callee: Box<Expr>,
pub type_args: Vec<Type>,
pub args: Vec<Expr>,
pub span: Span,
}
Expand All @@ -285,6 +292,7 @@ pub struct FieldAccessExpr {
pub struct MethodCallExpr {
pub receiver: Box<Expr>,
pub method: Ident,
pub type_args: Vec<Type>,
pub args: Vec<Expr>,
pub span: Span,
}
Expand Down
9 changes: 9 additions & 0 deletions capc/src/codegen/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,9 @@ fn store_value_by_ty(
module,
)
}
Ty::Param(_) => Err(CodegenError::Unsupported(
"generic type parameters must be monomorphized before codegen".to_string(),
)),
Ty::Path(name, args) => {
if name == "Result" && args.len() == 2 {
let ValueRepr::Result { tag, ok, err } = value else {
Expand Down Expand Up @@ -1368,6 +1371,9 @@ fn load_value_by_ty(
module,
)
}
Ty::Param(_) => Err(CodegenError::Unsupported(
"generic type parameters must be monomorphized before codegen".to_string(),
)),
Ty::Path(name, args) => {
if name == "Result" && args.len() == 2 {
let AbiType::Result(ok_abi, err_abi) = &ty.abi else {
Expand Down Expand Up @@ -2212,6 +2218,9 @@ fn zero_value_for_ty(
};
zero_value_for_ty(builder, &inner_ty, ptr_ty, _struct_layouts)
}
Ty::Param(_) => Err(CodegenError::Unsupported(
"generic type parameters must be monomorphized before codegen".to_string(),
)),
Ty::Path(name, args) => {
if name == "Result" && args.len() == 2 {
let AbiType::Result(ok_abi, err_abi) = &ty.abi else {
Expand Down
5 changes: 5 additions & 0 deletions capc/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct HirProgram {
#[derive(Debug, Clone)]
pub struct HirFunction {
pub name: String,
pub type_params: Vec<String>,
pub params: Vec<HirParam>,
pub ret_ty: HirType,
pub body: HirBlock,
Expand All @@ -54,6 +55,7 @@ pub struct HirFunction {
#[derive(Debug, Clone)]
pub struct HirExternFunction {
pub name: String,
pub type_params: Vec<String>,
pub params: Vec<HirParam>,
pub ret_ty: HirType,
}
Expand All @@ -68,6 +70,7 @@ pub struct HirParam {
#[derive(Debug, Clone)]
pub struct HirStruct {
pub name: String,
pub type_params: Vec<String>,
pub fields: Vec<HirField>,
pub is_opaque: bool,
}
Expand All @@ -82,6 +85,7 @@ pub struct HirField {
#[derive(Debug, Clone)]
pub struct HirEnum {
pub name: String,
pub type_params: Vec<String>,
pub variants: Vec<HirEnumVariant>,
}

Expand Down Expand Up @@ -238,6 +242,7 @@ pub struct HirEnumVariantExpr {
#[derive(Debug, Clone)]
pub struct HirCall {
pub callee: ResolvedCallee,
pub type_args: Vec<Ty>,
pub args: Vec<HirExpr>,
pub ret_ty: HirType,
pub span: Span,
Expand Down
96 changes: 90 additions & 6 deletions capc/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ impl Parser {

fn parse_impl_block(&mut self, impl_doc: Option<String>) -> Result<ImplBlock, ParseError> {
let start = self.expect(TokenKind::Impl)?.span.start;
let type_params = self.parse_type_params()?;
let target = self.parse_type()?;
self.expect(TokenKind::LBrace)?;
let mut methods = Vec::new();
Expand All @@ -248,6 +249,7 @@ impl Parser {
Ok(ImplBlock {
target,
methods,
type_params,
doc: impl_doc,
span: Span::new(start, end),
})
Expand All @@ -257,6 +259,7 @@ impl Parser {
let start = self.expect(TokenKind::Extern)?.span.start;
self.expect(TokenKind::Fn)?;
let name = self.expect_ident()?;
let type_params = self.parse_type_params()?;
self.expect(TokenKind::LParen)?;
let mut params = Vec::new();
if self.peek_kind() != Some(TokenKind::RParen) {
Expand Down Expand Up @@ -285,6 +288,7 @@ impl Parser {
.map_or(ret.span().end, |t| t.span.end);
Ok(ExternFunction {
name,
type_params,
params,
ret,
is_pub,
Expand All @@ -296,6 +300,7 @@ impl Parser {
fn parse_function(&mut self, is_pub: bool, doc: Option<String>) -> Result<Function, ParseError> {
let start = self.expect(TokenKind::Fn)?.span.start;
let name = self.expect_ident()?;
let type_params = self.parse_type_params()?;
self.expect(TokenKind::LParen)?;
let mut params = Vec::new();
if self.peek_kind() != Some(TokenKind::RParen) {
Expand Down Expand Up @@ -329,6 +334,7 @@ impl Parser {
let span = Span::new(start, body.span.end);
Ok(Function {
name,
type_params,
params,
ret,
body,
Expand All @@ -349,6 +355,7 @@ impl Parser {
) -> Result<StructDecl, ParseError> {
let start = self.expect(TokenKind::Struct)?.span.start;
let name = self.expect_ident()?;
let type_params = self.parse_type_params()?;
let mut fields = Vec::new();
let end = if self.peek_kind() == Some(TokenKind::LBrace) {
self.bump();
Expand Down Expand Up @@ -382,6 +389,7 @@ impl Parser {
};
Ok(StructDecl {
name,
type_params,
fields,
is_pub,
is_opaque,
Expand All @@ -396,6 +404,7 @@ impl Parser {
fn parse_enum(&mut self, is_pub: bool, doc: Option<String>) -> Result<EnumDecl, ParseError> {
let start = self.expect(TokenKind::Enum)?.span.start;
let name = self.expect_ident()?;
let type_params = self.parse_type_params()?;
self.expect(TokenKind::LBrace)?;
let mut variants = Vec::new();
if self.peek_kind() != Some(TokenKind::RBrace) {
Expand Down Expand Up @@ -429,6 +438,7 @@ impl Parser {
let end = self.expect(TokenKind::RBrace)?.span.end;
Ok(EnumDecl {
name,
type_params,
variants,
is_pub,
doc,
Expand Down Expand Up @@ -592,6 +602,11 @@ impl Parser {
let start = lhs.span().start;
self.bump(); // consume '.'
let field = self.expect_ident()?;
let type_args = if self.peek_kind() == Some(TokenKind::LBracket) {
self.parse_type_args()?
} else {
Vec::new()
};

// Check if this is a struct literal (followed by '{')
if self.peek_kind() == Some(TokenKind::LBrace) {
Expand All @@ -606,7 +621,7 @@ impl Parser {
};
path.segments.push(field);
path.span = Span::new(path.span.start, path.segments.last().unwrap().span.end);
lhs = self.parse_struct_literal(path)?;
lhs = self.parse_struct_literal(path, type_args)?;
continue;
}

Expand All @@ -626,12 +641,19 @@ impl Parser {
lhs = Expr::MethodCall(MethodCallExpr {
receiver: Box::new(lhs),
method: field,
type_args,
args,
span: Span::new(start, end),
});
continue;
}

if !type_args.is_empty() {
return Err(self.error_current(
"type arguments require a method call or struct literal"
.to_string(),
));
}
// Otherwise, it's a field access
let span = Span::new(start, field.span.end);
lhs = Expr::FieldAccess(FieldAccessExpr {
Expand All @@ -642,7 +664,30 @@ impl Parser {
continue;
}
TokenKind::LParen => {
lhs = self.finish_call(lhs)?;
lhs = self.finish_call(lhs, Vec::new())?;
continue;
}
TokenKind::LBracket => {
let type_args = self.parse_type_args()?;
if self.peek_kind() == Some(TokenKind::LBrace) {
let path = match lhs {
Expr::Path(p) => p,
Expr::FieldAccess(ref fa) => self.field_access_to_path(fa)?,
_ => {
return Err(self.error_current(
"expected path before struct literal".to_string(),
))
}
};
lhs = self.parse_struct_literal(path, type_args)?;
continue;
}
if self.peek_kind() != Some(TokenKind::LParen) {
return Err(self.error_current(
"type arguments require a call or struct literal".to_string(),
));
}
lhs = self.finish_call(lhs, type_args)?;
continue;
}
TokenKind::Question => {
Expand Down Expand Up @@ -811,7 +856,7 @@ impl Parser {
};

if self.peek_kind() == Some(TokenKind::LBrace) {
self.parse_struct_literal(path)
self.parse_struct_literal(path, Vec::new())
} else {
Ok(Expr::Path(path))
}
Expand Down Expand Up @@ -999,7 +1044,7 @@ impl Parser {
Ok(Type::Path { path, args, span })
}

fn parse_struct_literal(&mut self, path: Path) -> Result<Expr, ParseError> {
fn parse_struct_literal(&mut self, path: Path, type_args: Vec<Type>) -> Result<Expr, ParseError> {
let start = path.span.start;
self.expect(TokenKind::LBrace)?;
let mut fields = Vec::new();
Expand All @@ -1026,12 +1071,13 @@ impl Parser {
let end = self.expect(TokenKind::RBrace)?.span.end;
Ok(Expr::StructLiteral(StructLiteralExpr {
path,
type_args,
fields,
span: Span::new(start, end),
}))
}

fn finish_call(&mut self, callee: Expr) -> Result<Expr, ParseError> {
fn finish_call(&mut self, callee: Expr, type_args: Vec<Type>) -> Result<Expr, ParseError> {
let start = callee.span().start;
self.expect(TokenKind::LParen)?;
let mut args = Vec::new();
Expand All @@ -1046,11 +1092,49 @@ impl Parser {
let end = self.expect(TokenKind::RParen)?.span.end;
Ok(Expr::Call(CallExpr {
callee: Box::new(callee),
type_args,
args,
span: Span::new(start, end),
}))
}

fn parse_type_params(&mut self) -> Result<Vec<Ident>, ParseError> {
if self.peek_kind() != Some(TokenKind::LBracket) {
return Ok(Vec::new());
}
self.bump();
let mut params = Vec::new();
if self.peek_kind() != Some(TokenKind::RBracket) {
loop {
let ident = self.expect_ident()?;
params.push(ident);
if self.maybe_consume(TokenKind::Comma).is_none() {
break;
}
}
}
self.expect(TokenKind::RBracket)?;
Ok(params)
}

fn parse_type_args(&mut self) -> Result<Vec<Type>, ParseError> {
if self.peek_kind() != Some(TokenKind::LBracket) {
return Ok(Vec::new());
}
self.bump();
let mut args = Vec::new();
if self.peek_kind() != Some(TokenKind::RBracket) {
loop {
args.push(self.parse_type()?);
if self.maybe_consume(TokenKind::Comma).is_none() {
break;
}
}
}
self.expect(TokenKind::RBracket)?;
Ok(args)
}

fn expect(&mut self, kind: TokenKind) -> Result<Token, ParseError> {
match self.peek_kind() {
Some(k) if k == kind => Ok(self.bump().unwrap()),
Expand Down Expand Up @@ -1131,7 +1215,7 @@ fn infix_binding_power(op: &BinaryOp) -> (u8, u8) {

fn postfix_binding_power(kind: &TokenKind) -> Option<u8> {
match kind {
TokenKind::Dot | TokenKind::LParen | TokenKind::Question => Some(13),
TokenKind::Dot | TokenKind::LParen | TokenKind::LBracket | TokenKind::Question => Some(13),
_ => None,
}
}
Expand Down
Loading