feat: execute source control flow and exact real arithmetic
This commit is contained in:
+121
-44
@@ -167,6 +167,16 @@ pub fn analyze(root: &AstRoot, options: &CompileOptions) -> Result<SymbolTable,
|
||||
StmtKind::Expression(expression) => {
|
||||
root_scope.expression(expression, &globals)?
|
||||
}
|
||||
StmtKind::If(_) | StmtKind::While(_) => {
|
||||
let statements = std::slice::from_ref(statement);
|
||||
collect_locals(
|
||||
statements,
|
||||
&mut declared,
|
||||
&mut root_scope.scope.layout.names,
|
||||
)?;
|
||||
globals.extend(root_scope.scope.layout.names.iter().cloned());
|
||||
root_scope.statements(statements, &globals)?;
|
||||
}
|
||||
StmtKind::Return(_) => {
|
||||
return Err(error(statement.span, "return outside a function"));
|
||||
}
|
||||
@@ -207,21 +217,38 @@ fn function_scope(function: &Function, globals: &BTreeSet<String>) -> Result<Sco
|
||||
.push(parameter.name.text.clone());
|
||||
builder.initialized.insert(parameter.name.text.clone());
|
||||
}
|
||||
for statement in &function.body.statements {
|
||||
collect_locals(
|
||||
&function.body.statements,
|
||||
&mut locals,
|
||||
&mut builder.scope.layout.locals,
|
||||
)?;
|
||||
builder.statements(&function.body.statements, globals)?;
|
||||
Ok(builder.scope)
|
||||
}
|
||||
|
||||
fn collect_locals(
|
||||
statements: &[frontend::ast::Stmt],
|
||||
locals: &mut BTreeSet<String>,
|
||||
layout: &mut Vec<String>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
for statement in statements {
|
||||
match &statement.kind {
|
||||
StmtKind::Binding(binding) => {
|
||||
declare(&mut locals, &binding.name.text, binding.name.span)?;
|
||||
builder.scope.layout.locals.push(binding.name.text.clone());
|
||||
StmtKind::Binding(b) => {
|
||||
declare(locals, &b.name.text, b.name.span)?;
|
||||
layout.push(b.name.text.clone());
|
||||
}
|
||||
StmtKind::Assignment(assignment) => {
|
||||
if locals.insert(assignment.name.text.clone()) {
|
||||
builder
|
||||
.scope
|
||||
.layout
|
||||
.locals
|
||||
.push(assignment.name.text.clone());
|
||||
StmtKind::Assignment(a) => {
|
||||
if locals.insert(a.name.text.clone()) {
|
||||
layout.push(a.name.text.clone());
|
||||
}
|
||||
}
|
||||
StmtKind::If(b) => {
|
||||
collect_locals(&b.body.statements, locals, layout)?;
|
||||
if let Some(other) = &b.otherwise {
|
||||
collect_locals(&other.statements, locals, layout)?;
|
||||
}
|
||||
}
|
||||
StmtKind::While(b) => collect_locals(&b.body.statements, locals, layout)?,
|
||||
StmtKind::Declaration(_) => {
|
||||
return Err(error(
|
||||
statement.span,
|
||||
@@ -231,39 +258,7 @@ fn function_scope(function: &Function, globals: &BTreeSet<String>) -> Result<Sco
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut returned = false;
|
||||
for statement in &function.body.statements {
|
||||
if returned {
|
||||
return Err(error(
|
||||
statement.span,
|
||||
"statement after return is unreachable",
|
||||
));
|
||||
}
|
||||
match &statement.kind {
|
||||
StmtKind::Binding(binding) => {
|
||||
builder.expression(&binding.value, globals)?;
|
||||
builder.initialized.insert(binding.name.text.clone());
|
||||
}
|
||||
StmtKind::Assignment(assignment) => {
|
||||
builder.expression(&assignment.value, globals)?;
|
||||
builder.initialized.insert(assignment.name.text.clone());
|
||||
}
|
||||
StmtKind::Return(statement) => {
|
||||
if let Some(value) = &statement.value {
|
||||
builder.expression(value, globals)?;
|
||||
}
|
||||
returned = true;
|
||||
}
|
||||
StmtKind::MemberAssignment(assignment) => {
|
||||
builder.expression(&assignment.object, globals)?;
|
||||
builder.expression(&assignment.value, globals)?;
|
||||
builder.intern(&assignment.name.text);
|
||||
}
|
||||
StmtKind::Expression(expression) => builder.expression(expression, globals)?,
|
||||
StmtKind::Declaration(_) => unreachable!("nested declaration rejected above"),
|
||||
}
|
||||
}
|
||||
Ok(builder.scope)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct Builder {
|
||||
@@ -300,6 +295,88 @@ impl Builder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Function-scoped locals; branches merge definite initialization. A loop
|
||||
/// may execute zero times, so its writes do not initialize later reads.
|
||||
fn statements(
|
||||
&mut self,
|
||||
statements: &[frontend::ast::Stmt],
|
||||
globals: &BTreeSet<String>,
|
||||
) -> Result<bool, Diagnostic> {
|
||||
let mut returned = false;
|
||||
for statement in statements {
|
||||
if returned {
|
||||
return Err(error(
|
||||
statement.span,
|
||||
"statement after return is unreachable",
|
||||
));
|
||||
}
|
||||
match &statement.kind {
|
||||
StmtKind::Binding(b) => {
|
||||
self.expression(&b.value, globals)?;
|
||||
self.initialized.insert(b.name.text.clone());
|
||||
if self.scope.kind != ScopeKind::Function {
|
||||
self.intern(&b.name.text);
|
||||
}
|
||||
}
|
||||
StmtKind::Assignment(a) => {
|
||||
self.expression(&a.value, globals)?;
|
||||
self.initialized.insert(a.name.text.clone());
|
||||
if self.scope.kind != ScopeKind::Function {
|
||||
self.intern(&a.name.text);
|
||||
}
|
||||
}
|
||||
StmtKind::MemberAssignment(a) => {
|
||||
self.expression(&a.object, globals)?;
|
||||
self.expression(&a.value, globals)?;
|
||||
self.intern(&a.name.text);
|
||||
}
|
||||
StmtKind::Expression(e) => self.expression(e, globals)?,
|
||||
StmtKind::Return(r) => {
|
||||
if self.scope.kind != ScopeKind::Function {
|
||||
return Err(error(statement.span, "return outside a function"));
|
||||
}
|
||||
if let Some(e) = &r.value {
|
||||
self.expression(e, globals)?;
|
||||
}
|
||||
returned = true;
|
||||
}
|
||||
StmtKind::If(b) => {
|
||||
self.expression(&b.condition, globals)?;
|
||||
let before = self.initialized.clone();
|
||||
let yes_return = self.statements(&b.body.statements, globals)?;
|
||||
let yes = self.initialized.clone();
|
||||
self.initialized = before;
|
||||
let no_return = if let Some(other) = &b.otherwise {
|
||||
self.statements(&other.statements, globals)?
|
||||
} else {
|
||||
false
|
||||
};
|
||||
self.initialized = if yes_return {
|
||||
self.initialized.clone()
|
||||
} else if no_return {
|
||||
yes
|
||||
} else {
|
||||
yes.intersection(&self.initialized).cloned().collect()
|
||||
};
|
||||
returned = yes_return && no_return;
|
||||
}
|
||||
StmtKind::While(b) => {
|
||||
self.expression(&b.condition, globals)?;
|
||||
let before = self.initialized.clone();
|
||||
self.statements(&b.body.statements, globals)?;
|
||||
self.initialized = before;
|
||||
}
|
||||
StmtKind::Declaration(_) => {
|
||||
return Err(error(
|
||||
statement.span,
|
||||
"nested declarations are not compiled yet",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(returned)
|
||||
}
|
||||
|
||||
fn expression(
|
||||
&mut self,
|
||||
expression: &Expr,
|
||||
|
||||
Reference in New Issue
Block a user