368 lines
12 KiB
Rust
368 lines
12 KiB
Rust
use crate::ast::{
|
|
Block, ClassItem, Expression, FunctionDecl, Identifier, MatchItem, Postfix, Prefix, Spanned,
|
|
Statement, StatementBranch, TopLevel, TopLevelKind, VarDeclStmt,
|
|
};
|
|
|
|
pub trait GetMutability {
|
|
fn get_mutability(&self) -> Vec<Identifier>;
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct SemanticError {
|
|
pub line: usize,
|
|
pub column: usize,
|
|
pub error_message: String,
|
|
}
|
|
|
|
/// Class semantics - Ensuring fields are initialized
|
|
|
|
pub fn find_function_call<'a>(
|
|
items: &'a Vec<ClassItem>,
|
|
name: &Identifier,
|
|
) -> Option<&'a Spanned<FunctionDecl>> {
|
|
items.iter().find_map(|v| {
|
|
if let ClassItem::Method(m) = v {
|
|
if m.item.self_param.is_some() && &m.item.name == name {
|
|
Some(m)
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), Vec<SemanticError>> {
|
|
if let TopLevelKind::ClassDecl {
|
|
fields,
|
|
constructor,
|
|
items,
|
|
name,
|
|
inherits,
|
|
..
|
|
} = &top_level.0.item
|
|
{
|
|
if let Some(constructor) = constructor {
|
|
let mut fields = fields
|
|
.iter()
|
|
.map(|field| Spanned {
|
|
item: field.item.decl.name.clone(),
|
|
line: field.line,
|
|
column: field.column,
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
if inherits.is_some() {
|
|
fields.push(Spanned {
|
|
line: top_level.0.line,
|
|
column: top_level.0.column,
|
|
item: Identifier("_super".to_string()),
|
|
});
|
|
}
|
|
|
|
let mut mutability = constructor.item.body.get_mutability();
|
|
|
|
let mut i = 0;
|
|
while i < mutability.len() {
|
|
let name = mutability[i].clone();
|
|
if let Some(method) = find_function_call(items, &name) {
|
|
if let Some(body) = &method.item.body {
|
|
for entry in body.get_mutability() {
|
|
if !mutability.contains(&entry) {
|
|
mutability.push(entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
let mut errs = Vec::new();
|
|
|
|
for field in &fields {
|
|
if !mutability.contains(&field.item) {
|
|
errs.push(SemanticError {
|
|
line: field.line,
|
|
column: field.column,
|
|
error_message: format!(
|
|
"class field `{}.{}` is uninitialized",
|
|
name.0, field.item.0
|
|
),
|
|
});
|
|
}
|
|
}
|
|
|
|
if errs.len() > 0 {
|
|
return Err(errs);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<T: GetMutability> GetMutability for Vec<T> {
|
|
fn get_mutability(&self) -> Vec<Identifier> {
|
|
self.iter().flat_map(T::get_mutability).collect()
|
|
}
|
|
}
|
|
|
|
impl GetMutability for Block {
|
|
fn get_mutability(&self) -> Vec<Identifier> {
|
|
let mut items: Vec<Identifier> = self
|
|
.statements
|
|
.iter()
|
|
.flat_map(|v| v.item.get_mutability())
|
|
.collect();
|
|
|
|
if let Some(v) = &self.soft_return {
|
|
items.append(&mut v.item.get_mutability());
|
|
}
|
|
|
|
items
|
|
}
|
|
}
|
|
|
|
impl GetMutability for Statement {
|
|
fn get_mutability(&self) -> Vec<Identifier> {
|
|
match self {
|
|
Statement::Block(body) | Statement::UnsafeBlock(body) => body.get_mutability(),
|
|
Statement::If {
|
|
initial,
|
|
else_if,
|
|
else_branch,
|
|
} => {
|
|
let mut all_sets: Vec<Vec<Identifier>> = Vec::new();
|
|
all_sets.push(initial.body.get_mutability());
|
|
|
|
for branch in else_if {
|
|
all_sets.push(branch.body.get_mutability());
|
|
}
|
|
|
|
if let Some(else_block) = else_branch {
|
|
all_sets.push(else_block.get_mutability());
|
|
} else {
|
|
return Vec::new();
|
|
}
|
|
|
|
let mut result = all_sets.remove(0);
|
|
for set in all_sets {
|
|
result.retain(|id| set.contains(id));
|
|
}
|
|
result
|
|
}
|
|
Statement::Loop(body) => body.get_mutability(),
|
|
Statement::While(branch) => branch.body.get_mutability(),
|
|
Statement::CStyleFor {
|
|
body,
|
|
init,
|
|
condition,
|
|
update,
|
|
..
|
|
} => {
|
|
let mut a = init.get_mutability();
|
|
a.append(&mut condition.get_mutability());
|
|
a.append(&mut update.get_mutability());
|
|
a.append(&mut body.get_mutability());
|
|
a
|
|
}
|
|
Statement::For { body, iterator, .. } => {
|
|
let mut a = iterator.get_mutability();
|
|
a.append(&mut body.get_mutability());
|
|
a
|
|
}
|
|
Statement::Match(_, items) => {
|
|
if items.is_empty() {
|
|
return Vec::new();
|
|
}
|
|
let mut all_sets: Vec<Vec<Identifier>> = items
|
|
.iter()
|
|
.map(|item| item.item.get_mutability())
|
|
.collect();
|
|
let mut result = all_sets.remove(0);
|
|
for set in all_sets {
|
|
result.retain(|id| set.contains(id));
|
|
}
|
|
result
|
|
}
|
|
Statement::VarDecl(stmt) => stmt
|
|
.init
|
|
.as_ref()
|
|
.map(|e| e.get_mutability())
|
|
.unwrap_or_default(),
|
|
Statement::Return(expr) => expr
|
|
.as_ref()
|
|
.map(|e| e.get_mutability())
|
|
.unwrap_or_default(),
|
|
Statement::Break | Statement::Continue => Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl GetMutability for StatementBranch {
|
|
fn get_mutability(&self) -> Vec<Identifier> {
|
|
let mut a = self.condition.get_mutability();
|
|
a.append(&mut self.body.get_mutability());
|
|
a
|
|
}
|
|
}
|
|
|
|
impl GetMutability for MatchItem {
|
|
fn get_mutability(&self) -> Vec<Identifier> {
|
|
self.1.get_mutability()
|
|
}
|
|
}
|
|
|
|
impl GetMutability for VarDeclStmt {
|
|
fn get_mutability(&self) -> Vec<Identifier> {
|
|
self.init
|
|
.as_ref()
|
|
.map(|e| e.get_mutability())
|
|
.unwrap_or_default()
|
|
}
|
|
}
|
|
|
|
fn get_self_ref(initial: &Expression, postfixes: &[Postfix]) -> Option<Identifier> {
|
|
match initial {
|
|
Expression::Path(v) => {
|
|
let ident = v.0.first()?.ident.0.as_str();
|
|
if ident == "self" || ident == "super" {
|
|
if let Some(Postfix::FieldAccess(field, _)) = postfixes.first() {
|
|
if !matches!(postfixes.get(1), Some(Postfix::Call(_))) {
|
|
return Some(field.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn get_self_method_call(initial: &Expression, postfixes: &[Postfix]) -> Option<Identifier> {
|
|
match initial {
|
|
Expression::Path(v) => {
|
|
let ident = v.0.first()?.ident.0.as_str();
|
|
if ident == "self" || ident == "super" {
|
|
if let Some(Postfix::FieldAccess(method, _)) = postfixes.first() {
|
|
if matches!(postfixes.get(1), Some(Postfix::Call(_))) {
|
|
return Some(method.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
None
|
|
}
|
|
|
|
impl Expression {
|
|
fn get_self_ref(&self) -> Option<Identifier> {
|
|
match self {
|
|
Self::Fix {
|
|
initial, postfixes, ..
|
|
} => get_self_ref(initial, postfixes),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl GetMutability for Expression {
|
|
fn get_mutability(&self) -> Vec<Identifier> {
|
|
match self {
|
|
Self::Array(v) => v.get_mutability(),
|
|
Self::ArrayRepeat(v, i) => {
|
|
let mut a = v.get_mutability();
|
|
a.append(&mut i.get_mutability());
|
|
a
|
|
}
|
|
Self::Binary { lhs, op, rhs } => {
|
|
if mutates_lhs(op) {
|
|
let mut a = if let Some(self_ref) = lhs.get_self_ref() {
|
|
vec![self_ref]
|
|
} else {
|
|
lhs.get_mutability()
|
|
};
|
|
|
|
a.append(&mut rhs.get_mutability());
|
|
a
|
|
} else {
|
|
let mut a = lhs.get_mutability();
|
|
a.append(&mut rhs.get_mutability());
|
|
a
|
|
}
|
|
}
|
|
Self::Statement(stmt) => stmt.get_mutability(),
|
|
Self::Fix {
|
|
initial,
|
|
prefixes,
|
|
postfixes,
|
|
} => {
|
|
let mut result = Vec::new();
|
|
|
|
// &mut self.field / &mut super.field / &mut super pattern
|
|
if prefixes.iter().any(|p| matches!(p, Prefix::RefMut)) {
|
|
if let Some(self_ref) = get_self_ref(initial, postfixes) {
|
|
result.push(self_ref);
|
|
} else if let Expression::Path(v) = &**initial {
|
|
if v.0.first().map_or(false, |s| s.ident.0 == "super") {
|
|
result.push(Identifier("_super".to_string()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recurse into all postfix sub-expressions
|
|
for postfix in postfixes {
|
|
match postfix {
|
|
Postfix::Assign(_, rhs) => {
|
|
if let Some(self_ref) = get_self_ref(initial, postfixes) {
|
|
result.push(self_ref);
|
|
}
|
|
result.append(&mut rhs.get_mutability());
|
|
}
|
|
Postfix::Call(args) => {
|
|
for arg in args {
|
|
result.append(&mut arg.get_mutability());
|
|
}
|
|
}
|
|
Postfix::StructCall(fields) => {
|
|
for (_, expr) in fields {
|
|
if let Some(expr) = expr {
|
|
result.append(&mut expr.get_mutability());
|
|
}
|
|
}
|
|
}
|
|
Postfix::Index(expr) => {
|
|
result.append(&mut expr.get_mutability());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
// Method calls on self: self.method_name(args)
|
|
if let Some(method) = get_self_method_call(initial, postfixes) {
|
|
result.push(method);
|
|
}
|
|
|
|
result
|
|
}
|
|
Self::Closure { body, .. } => body.get_mutability(),
|
|
Self::Literal(_) => Vec::new(),
|
|
Self::Path(v) => {
|
|
if v.0.first().map_or(false, |s| s.ident.0 == "super") {
|
|
vec![Identifier("_super".to_string())]
|
|
} else {
|
|
Vec::new()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn mutates_lhs(op: &str) -> bool {
|
|
matches!(op, "=" | "->")
|
|
}
|