Lines
87.59 %
Functions
40 %
use crate::errors::{FatalParseError, RecoverableParseError};
use crate::expression::parse_expression;
use crate::field;
use crate::parser::ParseContext;
use crate::parser::domain::parse_domain;
use crate::util::{TypecheckingContext, named_children};
use conjure_cp_core::ast::{AbstractLiteral, DomainPtr, Expression};
use conjure_cp_core::{domain_int, range};
use tree_sitter::Node;
pub fn parse_abstract(
ctx: &mut ParseContext,
node: &Node,
) -> Result<Option<AbstractLiteral<Expression>>, FatalParseError> {
if typecheck_abstract_literal(ctx, node) {
return Ok(None);
}
match node.kind() {
"record" => parse_record(ctx, node),
"tuple" => parse_tuple(ctx, node),
"matrix" => parse_matrix(ctx, node),
"set_literal" => parse_set_literal(ctx, node),
_ => {
ctx.record_error(RecoverableParseError::new(
format!("Expected abstract literal, got: {}", node.kind()),
Some(node.range()),
));
Ok(None)
fn typecheck_abstract_literal(ctx: &mut ParseContext, node: &Node) -> bool {
let expected = match ctx.typechecking_context {
TypecheckingContext::Boolean => "bool",
TypecheckingContext::Arithmetic => "int",
TypecheckingContext::Set => "set",
TypecheckingContext::SetOrMatrix => "set or matrix",
TypecheckingContext::MSet => "mset",
TypecheckingContext::Matrix => "matrix",
TypecheckingContext::Tuple => "tuple",
TypecheckingContext::Record => "record",
TypecheckingContext::Partition => "partition",
TypecheckingContext::Sequence => "sequence",
TypecheckingContext::Unknown => "unknown",
};
let got = match node.kind() {
"set_literal" => "set",
"matrix" => "matrix",
"tuple" => "tuple",
"record" => "record",
return true;
if expected != "unknown"
&& !(ctx.typechecking_context == TypecheckingContext::SetOrMatrix
&& matches!(got, "set" | "matrix"))
&& expected != got
{
format!(
"Type error: {}\n\tExpected: {}\n\tGot: {}",
ctx.source_code[node.start_byte()..node.end_byte()].trim(),
expected,
got
),
false
fn parse_record(
let mut values = Vec::new();
for child in node.children_by_field_name("name_value_pair", &mut node.walk()) {
let Some(name_node) = field!(recover, ctx, child, "name") else {
let name_str = &ctx.source_code[name_node.start_byte()..name_node.end_byte()];
let name = conjure_cp_core::ast::Name::user(name_str);
let Some(value_node) = field!(recover, ctx, child, "value") else {
// Parse value with inner typechecking context
let saved_ctx = ctx.typechecking_context;
ctx.typechecking_context = ctx.inner_typechecking_context;
ctx.inner_typechecking_context = TypecheckingContext::Unknown;
let Some(value) = parse_expression(ctx, value_node)? else {
// Reset contexts
ctx.inner_typechecking_context = ctx.typechecking_context;
ctx.typechecking_context = saved_ctx;
values.push(conjure_cp_core::ast::records::FieldValue { name, value });
Ok(Some(AbstractLiteral::Record(values)))
fn parse_tuple(
// Save the typechecking context
let saved_inner_ctx = ctx.inner_typechecking_context;
let mut elements = Vec::new();
for child in named_children(node) {
// Parse elements with inner typechecking context
ctx.typechecking_context = saved_inner_ctx;
let Some(expr) = parse_expression(ctx, child)? else {
ctx.inner_typechecking_context = saved_inner_ctx;
elements.push(expr);
Ok(Some(AbstractLiteral::Tuple(elements)))
fn parse_matrix(
// Save the typechecking contexts
let mut elements = vec![];
let mut domain: Option<DomainPtr> = None;
if child.kind() == "arithmetic_expr"
|| child.kind() == "bool_expr"
|| child.kind() == "comparison_expr"
|| child.kind() == "atom"
} else {
// Parse domains with unknown typechecking context
ctx.typechecking_context = TypecheckingContext::Unknown;
let Some(parsed_domain) = parse_domain(ctx, child)? else {
domain = Some(parsed_domain);
if domain.is_none() {
let count = elements.len() as i32;
domain = Some(domain_int!(1..count));
Ok(Some(AbstractLiteral::Matrix(elements, domain.unwrap())))
fn parse_set_literal(
Ok(Some(AbstractLiteral::Set(elements)))