add support for (void) block expression

This commit is contained in:
2021-11-13 17:43:04 +01:00
parent e4bf292e47
commit 29b8c04700
6 changed files with 84 additions and 33 deletions

View File

@@ -0,0 +1,20 @@
import "env.memory" memory(4);
import "env.circle" fn circle(f32, f32, f32, i32);
import "env.sin" fn sin(f32) -> f32;
import "env.cls" fn cls(i32);
export fn tic(time: i32) {
cls(0);
let i: i32;
loop outer {
let lazy fi = i as f32 / 4 as f32;
let lazy t = time as f32 / 5000 as f32;
circle(
(sin(fi / 2 as f32 + t * 5 as f32) + 1 as f32) * 160 as f32,
(sin(fi / 3 as f32 + t * 4 as f32) + 1 as f32) * 128 as f32,
(sin(t * 17 as f32 + fi * 2 as f32) + 2 as f32) * 16 as f32, i * 2 + 63);
branch_if (i := i + 1) < 63: outer
}
}

View File

@@ -155,6 +155,10 @@ pub enum Expr {
label: String, label: String,
block: Box<Expression>, block: Box<Expression>,
}, },
LabelBlock {
label: String,
block: Box<Expression>,
},
Branch(String), Branch(String),
BranchIf { BranchIf {
condition: Box<Expression>, condition: Box<Expression>,

View File

@@ -242,6 +242,7 @@ fn fold_expr(expr: &mut ast::Expression) {
ast::Expr::Assign { ref mut value, .. } => fold_expr(value), ast::Expr::Assign { ref mut value, .. } => fold_expr(value),
ast::Expr::LocalTee { ref mut value, .. } => fold_expr(value), ast::Expr::LocalTee { ref mut value, .. } => fold_expr(value),
ast::Expr::Loop { ref mut block, .. } => fold_expr(block), ast::Expr::Loop { ref mut block, .. } => fold_expr(block),
ast::Expr::LabelBlock { ref mut block, .. } => fold_expr(block),
ast::Expr::Branch(_) => (), ast::Expr::Branch(_) => (),
ast::Expr::BranchIf { ast::Expr::BranchIf {
ref mut condition, .. ref mut condition, ..

View File

@@ -316,6 +316,7 @@ fn collect_locals_expr<'a>(expr: &ast::Expression, locals: &mut Vec<(String, ast
ast::Expr::Assign { value, .. } => collect_locals_expr(value, locals), ast::Expr::Assign { value, .. } => collect_locals_expr(value, locals),
ast::Expr::LocalTee { value, .. } => collect_locals_expr(value, locals), ast::Expr::LocalTee { value, .. } => collect_locals_expr(value, locals),
ast::Expr::Loop { block, .. } => collect_locals_expr(block, locals), ast::Expr::Loop { block, .. } => collect_locals_expr(block, locals),
ast::Expr::LabelBlock { block, .. } => collect_locals_expr(block, locals),
ast::Expr::Cast { value, .. } => collect_locals_expr(value, locals), ast::Expr::Cast { value, .. } => collect_locals_expr(value, locals),
ast::Expr::FuncCall { params, .. } => { ast::Expr::FuncCall { params, .. } => {
for param in params { for param in params {
@@ -610,6 +611,13 @@ fn emit_expression<'a>(ctx: &mut FunctionContext<'a>, expr: &'a ast::Expression)
ctx.labels.pop(); ctx.labels.pop();
ctx.function.instruction(&Instruction::End); ctx.function.instruction(&Instruction::End);
} }
ast::Expr::LabelBlock {label, block } => {
ctx.labels.push(label.to_string());
ctx.function.instruction(&Instruction::Block(map_block_type(block.type_)));
emit_expression(ctx, block);
ctx.labels.pop();
ctx.function.instruction(&Instruction::End);
}
ast::Expr::Variable(name) => { ast::Expr::Variable(name) => {
if let Some(index) = ctx.locals.get(name) { if let Some(index) = ctx.locals.get(name) {
if let Some((expr, let_type)) = ctx.let_values.get(name.as_str()) { if let Some((expr, let_type)) = ctx.let_values.get(name.as_str()) {

View File

@@ -14,6 +14,7 @@ enum Token {
Global, Global,
Mut, Mut,
Loop, Loop,
Block,
Branch, Branch,
BranchIf, BranchIf,
Lazy, Lazy,
@@ -45,6 +46,7 @@ impl fmt::Display for Token {
Token::Global => write!(f, "global"), Token::Global => write!(f, "global"),
Token::Mut => write!(f, "mut"), Token::Mut => write!(f, "mut"),
Token::Loop => write!(f, "loop"), Token::Loop => write!(f, "loop"),
Token::Block => write!(f, "block"),
Token::Branch => write!(f, "branch"), Token::Branch => write!(f, "branch"),
Token::BranchIf => write!(f, "branch_if"), Token::BranchIf => write!(f, "branch_if"),
Token::Lazy => write!(f, "lazy"), Token::Lazy => write!(f, "lazy"),
@@ -237,6 +239,7 @@ fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
"global" => Token::Global, "global" => Token::Global,
"mut" => Token::Mut, "mut" => Token::Mut,
"loop" => Token::Loop, "loop" => Token::Loop,
"block" => Token::Block,
"branch" => Token::Branch, "branch" => Token::Branch,
"branch_if" => Token::BranchIf, "branch_if" => Token::BranchIf,
"lazy" => Token::Lazy, "lazy" => Token::Lazy,
@@ -333,39 +336,31 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
let loop_expr = just(Token::Loop) let loop_expr = just(Token::Loop)
.ignore_then(identifier) .ignore_then(identifier)
.then( .then(block.clone())
block
.clone()
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}')),
)
.map(|(label, block)| ast::Expr::Loop { .map(|(label, block)| ast::Expr::Loop {
label, label,
block: Box::new(block), block: Box::new(block),
}); });
let label_block_expr = just(Token::Block)
.ignore_then(identifier)
.then(block.clone())
.map(|(label, block)| ast::Expr::LabelBlock {
label,
block: Box::new(block),
});
let if_expr = just(Token::If) let if_expr = just(Token::If)
.ignore_then(expression.clone()) .ignore_then(expression.clone())
.then( .then(block.clone())
block .then(just(Token::Else).ignore_then(block.clone()).or_not())
.clone()
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}')),
)
.then(
just(Token::Else)
.ignore_then(
block
.clone()
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}')),
)
.or_not(),
)
.map(|((condition, if_true), if_false)| ast::Expr::If { .map(|((condition, if_true), if_false)| ast::Expr::If {
condition: Box::new(condition), condition: Box::new(condition),
if_true: Box::new(if_true), if_true: Box::new(if_true),
if_false: if_false.map(Box::new), if_false: if_false.map(Box::new),
}); });
let block_expr = loop_expr.or(if_expr).boxed(); let block_expr = loop_expr.or(label_block_expr).or(if_expr).boxed();
block_expression = Some(block_expr.clone()); block_expression = Some(block_expr.clone());
@@ -475,6 +470,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.or(expression .or(expression
.clone() .clone()
.delimited_by(Token::Ctrl('('), Token::Ctrl(')'))) .delimited_by(Token::Ctrl('('), Token::Ctrl(')')))
.or(block)
.recover_with(nested_delimiters( .recover_with(nested_delimiters(
Token::Ctrl('('), Token::Ctrl('('),
Token::Ctrl(')'), Token::Ctrl(')'),
@@ -570,7 +566,8 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
} else { } else {
make_memory_op(left, vec![(size, right)], None) make_memory_op(left, vec![(size, right)], None)
} }
}).clone(); })
.clone();
let memory_op = op_cast let memory_op = op_cast
.clone() .clone()
@@ -732,17 +729,27 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
expression expression
.clone() .clone()
.then_ignore(just(Token::Ctrl(';'))) .then(just(Token::Ctrl(';')).to(false))
.or(block_expression.map_with_span(|expr, span| expr.with_span(span))) .or(block_expression.map_with_span(|expr, span| (expr.with_span(span), true)))
.repeated() .repeated()
.then(expression.clone().or_not()) .then(expression.clone().or_not())
.map_with_span(|(statements, final_expression), span| { .map_with_span(|(mut statements, mut final_expression), span| {
if final_expression.is_none()
&& statements
.last()
.map(|(_, block_expr)| *block_expr)
.unwrap_or(false)
{
final_expression = statements.pop().map(|(expr, _)| expr);
}
ast::Expr::Block { ast::Expr::Block {
statements, statements: statements.into_iter().map(|(expr, _)| expr).collect(),
final_expression: final_expression.map(|e| Box::new(e)), final_expression: final_expression.map(|e| Box::new(e)),
} }
.with_span(span) .with_span(span)
}).boxed() })
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}'))
.boxed()
}); });
let expression = expression_out.unwrap(); let expression = expression_out.unwrap();
@@ -821,11 +828,7 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.ignore_then(type_parser()) .ignore_then(type_parser())
.or_not(), .or_not(),
) )
.then( .then(block.clone())
block
.clone()
.delimited_by(Token::Ctrl('{'), Token::Ctrl('}')),
)
.map_with_span(|((((export, name), params), type_), body), span| { .map_with_span(|((((export, name), params), type_), body), span| {
ast::TopLevelItem::Function(ast::Function { ast::TopLevelItem::Function(ast::Function {
span, span,
@@ -852,7 +855,8 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
mutable: mutable.is_some(), mutable: mutable.is_some(),
span, span,
}) })
}).boxed(); })
.boxed();
let data_i8 = just(Token::Ident("i8".to_string())) let data_i8 = just(Token::Ident("i8".to_string()))
.to(ast::DataType::I8) .to(ast::DataType::I8)
@@ -862,7 +866,8 @@ fn script_parser() -> impl Parser<Token, ast::Script, Error = Simple<Token>> + C
.or(just(Token::Ident("f32".to_string())).to(ast::DataType::F32)) .or(just(Token::Ident("f32".to_string())).to(ast::DataType::F32))
.or(just(Token::Ident("f64".to_string())).to(ast::DataType::F64)) .or(just(Token::Ident("f64".to_string())).to(ast::DataType::F64))
.then( .then(
expression.clone() expression
.clone()
.separated_by(just(Token::Ctrl(','))) .separated_by(just(Token::Ctrl(',')))
.delimited_by(Token::Ctrl('('), Token::Ctrl(')')), .delimited_by(Token::Ctrl('('), Token::Ctrl(')')),
) )

View File

@@ -577,6 +577,19 @@ fn tc_expression(context: &mut Context, expr: &mut ast::Expression) -> Result<()
context.block_stack.pop(); context.block_stack.pop();
block.type_ block.type_
} }
ast::Expr::LabelBlock {
ref label,
ref mut block,
} => {
context.block_stack.push(label.clone());
tc_expression(context, block)?;
context.block_stack.pop();
if block.type_ != None {
// TODO: implement, requires branches to optionally provide values
return type_mismatch(None, &expr.span, block.type_, &block.span, context.source);
}
None
}
ast::Expr::Branch(ref label) => { ast::Expr::Branch(ref label) => {
if !context.block_stack.contains(label) { if !context.block_stack.contains(label) {
return missing_label(&expr.span, context.source); return missing_label(&expr.span, context.source);