Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions crates/analysis/src/alpha050/stmnts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,119 @@ pub fn analyze_stmnt(
return_ty: exp_analysis.return_ty,
}
}
Statement::ArrayIndexSet((var, var_span), index_exp, value_exp) => {
let var_ty = match get_symbol_definition_info(files, var, &file, var_span.start) {
Some(info) => {
match info.symbol_type {
SymbolType::Function(_) => {
files.report_error(&file, "Cannot assign to a function", *var_span);
}
SymbolType::Variable(var) if var.is_const => {
files.report_error(&file, "Cannot assign to a constant", *var_span);
}
_ => {}
}

info.data_type
}
None => DataType::Any,
};

let element_ty = match &var_ty {
DataType::Array(inner) => *inner.clone(),
DataType::Generic(id) => {
match scoped_generic_types.get_recursive(*id) {
DataType::Array(inner) => match *inner {
DataType::Any => {
// Generic array with unknown inner type; create a
// parametric inner type so it can be resolved later.
let inner_id = scoped_generic_types.new_generic_id();
scoped_generic_types.constrain_generic_type(
*id,
DataType::Array(Box::new(DataType::Generic(inner_id))),
);
DataType::Generic(inner_id)
}
other => other,
},
DataType::Any => {
// Unconstrained generic; we now know it is used as an array.
let inner_id = scoped_generic_types.new_generic_id();
scoped_generic_types.constrain_generic_type(
*id,
DataType::Array(Box::new(DataType::Generic(inner_id))),
);
DataType::Generic(inner_id)
}
_ => {
files.report_error(
&file,
&format!(
"Cannot index into value of type {}",
var_ty.to_string(scoped_generic_types)
),
*var_span,
);
DataType::Any
}
}
}
DataType::Any => DataType::Any,
_ => {
files.report_error(
&file,
&format!(
"Cannot index into value of type {}",
var_ty.to_string(scoped_generic_types)
),
*var_span,
);
DataType::Any
}
};

analyze_exp(
file_id,
file_version,
index_exp,
DataType::Int,
files,
scoped_generic_types,
contexts,
);

let exp_analysis = analyze_exp(
file_id,
file_version,
value_exp,
element_ty.clone(),
files,
scoped_generic_types,
contexts,
);

// Constrain the generic element type if we learned a concrete type.
if let DataType::Generic(id) = &element_ty {
scoped_generic_types.constrain_generic_type(*id, exp_analysis.exp_ty);
}

insert_symbol_reference(
var,
files,
&SymbolLocation {
file,
start: var_span.start,
end: var_span.end,
},
scoped_generic_types,
contexts,
);

StmntAnalysisResult {
is_propagating_failure: exp_analysis.is_propagating_failure,
return_ty: exp_analysis.return_ty,
}
}
Statement::Break => {
if !contexts.iter().any(|c| matches!(c, Context::Loop)) {
files.report_error(&file, "Break statement outside of loop", *span);
Expand Down
5 changes: 5 additions & 0 deletions crates/grammar/src/alpha050/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ pub enum Statement {
VariableInit(Spanned<String>, Spanned<String>, Spanned<VariableInitType>),
ConstInit(Spanned<String>, Spanned<String>, Box<Spanned<Expression>>),
VariableSet(Spanned<String>, Box<Spanned<Expression>>),
ArrayIndexSet(
Spanned<String>,
Box<Spanned<Expression>>,
Box<Spanned<Expression>>,
),
IfCondition(
Spanned<String>,
Spanned<IfCondition>,
Expand Down
11 changes: 11 additions & 0 deletions crates/grammar/src/alpha050/semantic_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,17 @@ fn semantic_tokens_from_stmnts(stmnts: &[Spanned<Statement>]) -> Vec<SpannedSema

tokens
}
Statement::ArrayIndexSet((_, var_span), index, value) => {
let mut tokens = vec![(
hash_semantic_token_type(SemanticTokenType::VARIABLE),
*var_span,
)];

tokens.extend(semantic_tokens_from_expr(index));
tokens.extend(semantic_tokens_from_expr(value));

tokens
}
Statement::Error => vec![],
})
.collect()
Expand Down
48 changes: 48 additions & 0 deletions crates/grammar/src/alpha050/statements/array_index_set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use chumsky::prelude::*;

use crate::alpha050::expressions::parse_expr;
use crate::alpha050::parser::{
default_recovery,
ident,
};
use crate::alpha050::{
AmberParser,
Expression,
Spanned,
Statement,
};
use crate::T;

pub fn array_index_set_parser<'a>(
stmnts: impl AmberParser<'a, Spanned<Statement>>,
) -> impl AmberParser<'a, Spanned<Statement>> {
ident("variable".to_string())
.map_with(|name, e| (name, e.span()))
.then_ignore(just(T!['[']))
.then(
parse_expr(stmnts.clone()).recover_with(via_parser(
default_recovery()
.or_not()
.map_with(|_, e| (Expression::Error, e.span())),
)),
)
.then_ignore(
just(T![']']).recover_with(via_parser(default_recovery().or_not().map(|_| T![']']))),
)
.then_ignore(just(T!["="]))
.then(
parse_expr(stmnts).recover_with(via_parser(
default_recovery()
.or_not()
.map_with(|_, e| (Expression::Error, e.span())),
)),
)
.map_with(|((name, index), value), e| {
(
Statement::ArrayIndexSet(name, Box::new(index), Box::new(value)),
e.span(),
)
})
.boxed()
.labelled("array index set")
}
2 changes: 2 additions & 0 deletions crates/grammar/src/alpha050/statements/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use super::{
Statement,
};

pub mod array_index_set;
pub mod block;
pub mod comment;
pub mod const_init;
Expand All @@ -29,6 +30,7 @@ pub fn statement_parser<'a>() -> impl AmberParser<'a, Spanned<Statement>> {
comment::comment_parser().map_with(|com, e| (Statement::Comment(com), e.span())),
shebang::shebang_parser(),
var_init::var_init_parser(stmnt.clone()),
array_index_set::array_index_set_parser(stmnt.clone()),
var_set::var_set_parser(stmnt.clone()),
block::block_parser_statement(stmnt.clone()),
if_cond::if_chain_parser(stmnt.clone()),
Expand Down
Loading