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