allow returning a second value via rdx
This commit is contained in:
@@ -330,7 +330,7 @@ _builtin_environ:
|
||||
Stmt::Function {
|
||||
name,
|
||||
params,
|
||||
return_type: _,
|
||||
return_types: _,
|
||||
body,
|
||||
exported,
|
||||
} => {
|
||||
@@ -387,8 +387,17 @@ _builtin_environ:
|
||||
emit!(&mut self.output, " ret");
|
||||
}
|
||||
}
|
||||
Stmt::Return { expr, keyword: _ } => {
|
||||
self.compile_expr(env, expr)?;
|
||||
Stmt::Return { keyword: _, exprs } => {
|
||||
if exprs.len() == 2 {
|
||||
self.compile_expr(env, &exprs[1])?;
|
||||
emit!(&mut self.output, " push rax");
|
||||
self.compile_expr(env, &exprs[0])?;
|
||||
emit!(&mut self.output, " pop rdx");
|
||||
} else if exprs.len() == 1 {
|
||||
self.compile_expr(env, &exprs[0])?;
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
emit!(&mut self.output, " mov rsp, rbp");
|
||||
emit!(&mut self.output, " pop rbp");
|
||||
emit!(&mut self.output, " ret");
|
||||
|
||||
@@ -52,13 +52,13 @@ pub enum Stmt {
|
||||
Function {
|
||||
name: Token,
|
||||
params: Params,
|
||||
return_type: Token,
|
||||
return_types: Vec<Token>,
|
||||
body: Box<Stmt>,
|
||||
exported: bool,
|
||||
},
|
||||
Return {
|
||||
expr: Expr,
|
||||
keyword: Token,
|
||||
exprs: Vec<Expr>,
|
||||
},
|
||||
Break,
|
||||
Continue,
|
||||
@@ -213,7 +213,14 @@ impl Parser {
|
||||
|
||||
self.consume(TokenType::RightBracket, "expected ']' after arguments")?;
|
||||
self.consume(TokenType::Colon, "expected ':' after '['")?;
|
||||
let return_type = self.consume(TokenType::Identifier, "expected return type")?;
|
||||
|
||||
let mut return_types = vec![];
|
||||
loop {
|
||||
return_types.push(self.consume(TokenType::Identifier, "expected return type")?);
|
||||
if !self.match_token(&[TokenType::Comma]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.is_inside_function = true;
|
||||
let body = Box::new(self.block()?);
|
||||
@@ -226,7 +233,7 @@ impl Parser {
|
||||
} else {
|
||||
Params::Normal(params)
|
||||
},
|
||||
return_type,
|
||||
return_types,
|
||||
body,
|
||||
exported,
|
||||
})
|
||||
@@ -303,10 +310,14 @@ impl Parser {
|
||||
self.for_statement()
|
||||
} else if self.match_token(&[TokenType::KeywordReturn]) {
|
||||
let keyword = self.previous().clone();
|
||||
Ok(Stmt::Return {
|
||||
expr: self.expression()?,
|
||||
keyword,
|
||||
})
|
||||
let mut exprs = vec![];
|
||||
loop {
|
||||
exprs.push(self.expression()?);
|
||||
if !self.match_token(&[TokenType::Comma]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Stmt::Return { keyword, exprs })
|
||||
} else if self.match_token(&[TokenType::KeywordBreak]) {
|
||||
Ok(Stmt::Break)
|
||||
} else if self.match_token(&[TokenType::KeywordContinue]) {
|
||||
|
||||
@@ -93,18 +93,23 @@ impl SymbolTable {
|
||||
Stmt::Function {
|
||||
name,
|
||||
params,
|
||||
return_type,
|
||||
return_types,
|
||||
body: _,
|
||||
exported: _,
|
||||
} => {
|
||||
if self.is_name_defined(&name.lexeme) {
|
||||
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
||||
}
|
||||
let return_type = return_types
|
||||
.iter()
|
||||
.map(|t| t.lexeme.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
match params {
|
||||
Params::Normal(params) => self.functions.insert(
|
||||
name.lexeme.clone(),
|
||||
FnType {
|
||||
return_type: return_type.lexeme.clone(),
|
||||
return_type,
|
||||
params: Some(
|
||||
params.iter().map(|x| x.var_type.lexeme.clone()).collect(),
|
||||
),
|
||||
@@ -113,7 +118,7 @@ impl SymbolTable {
|
||||
Params::Variadic => self.functions.insert(
|
||||
name.lexeme.clone(),
|
||||
FnType {
|
||||
return_type: return_type.lexeme.clone(),
|
||||
return_type,
|
||||
params: None,
|
||||
},
|
||||
),
|
||||
|
||||
@@ -95,6 +95,12 @@ impl<'a> TypeChecker<'a> {
|
||||
initializer,
|
||||
} => {
|
||||
let mut actual_type = self.typecheck_expr(env, initializer)?;
|
||||
if actual_type.contains(',') {
|
||||
return error!(
|
||||
&name.loc,
|
||||
"cannot assign multi-return call to a single variable"
|
||||
);
|
||||
}
|
||||
if let Some(var_type) = var_type {
|
||||
if !self.is_valid_type_name(&var_type.lexeme) {
|
||||
return error!(
|
||||
@@ -113,6 +119,12 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
Stmt::Assign { left, op, value } => {
|
||||
let value_type = self.typecheck_expr(env, value)?;
|
||||
if value_type.contains(',') {
|
||||
return error!(
|
||||
&op.loc,
|
||||
"cannot assign multi-return call to a single variable"
|
||||
);
|
||||
}
|
||||
|
||||
match &left.kind {
|
||||
ExprKind::Variable(name) => {
|
||||
@@ -217,12 +229,18 @@ impl<'a> TypeChecker<'a> {
|
||||
Stmt::Function {
|
||||
name,
|
||||
params,
|
||||
return_type,
|
||||
return_types,
|
||||
body,
|
||||
exported: _,
|
||||
} => {
|
||||
let return_type = return_types
|
||||
.iter()
|
||||
.map(|t| t.lexeme.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
|
||||
if name.lexeme == "main" {
|
||||
if return_type.lexeme != "i64" {
|
||||
if return_type != "i64" {
|
||||
return error!(&name.loc, "main function must return i64");
|
||||
}
|
||||
|
||||
@@ -256,14 +274,14 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if !self.is_valid_type_name(&return_type.lexeme) {
|
||||
if !self.is_valid_type_name(&return_type) {
|
||||
return error!(
|
||||
&return_type.loc,
|
||||
"unrecognized type: ".to_owned() + &return_type.lexeme
|
||||
&return_types[0].loc,
|
||||
"unrecognized type: ".to_owned() + &return_type
|
||||
);
|
||||
}
|
||||
|
||||
self.current_function_return_type = return_type.lexeme.clone();
|
||||
self.current_function_return_type = return_type.clone();
|
||||
|
||||
env.push_scope();
|
||||
|
||||
@@ -290,13 +308,18 @@ impl<'a> TypeChecker<'a> {
|
||||
|
||||
env.pop_scope();
|
||||
}
|
||||
Stmt::Return { expr, keyword } => {
|
||||
Stmt::Return { keyword, exprs } => {
|
||||
let expected = if self.current_function_return_type == "void" {
|
||||
"i64".into()
|
||||
} else {
|
||||
self.current_function_return_type.clone()
|
||||
};
|
||||
expect_type!(self.typecheck_expr(env, expr)?, expected, keyword.loc);
|
||||
let joined_type = exprs
|
||||
.iter()
|
||||
.map(|e| self.typecheck_expr(env, e))
|
||||
.collect::<Result<Vec<String>, _>>()?
|
||||
.join(",");
|
||||
expect_type!(joined_type, expected, keyword.loc);
|
||||
}
|
||||
Stmt::Break => {}
|
||||
Stmt::Continue => {}
|
||||
@@ -528,6 +551,9 @@ impl<'a> TypeChecker<'a> {
|
||||
}
|
||||
|
||||
fn is_valid_type_name(&self, name: &str) -> bool {
|
||||
if name.contains(',') {
|
||||
return name.split(',').all(|part| self.is_valid_type_name(part));
|
||||
}
|
||||
if BUILTIN_TYPES.contains(&name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user