Lines
84.81 %
Functions
33.85 %
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),
"variant" => parse_variant(ctx, node),
"tuple" => parse_tuple(ctx, node),
"matrix" => parse_matrix(ctx, node),
"set_literal" => parse_set_literal(ctx, node),
"mset_literal" => parse_mset_literal(ctx, node),
"sequence_literal" => parse_sequence_literal(ctx, node),
"function_literal" => parse_function_literal(ctx, node),
"relation_literal" => parse_relation_literal(ctx, node),
"partition_literal" => parse_partition_literal(ctx, node),
"permutation_literal" => parse_permutation_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::Permutation => "permutation",
TypecheckingContext::Sequence => "sequence",
TypecheckingContext::Function => "function",
TypecheckingContext::Relation => "relation",
TypecheckingContext::Unknown => "unknown",
};
let got = match node.kind() {
"set_literal" => "set",
"mset_literal" => "mset",
"sequence_literal" => "sequence",
"function_literal" => "function",
"relation_literal" => "relation",
"partition_literal" => "partition",
"permutation_literal" => "permutation",
"matrix" => "matrix",
"tuple" => "tuple",
"record" => "record",
"variant" => "variant",
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_variant(
let Some(pair) = node.child_by_field_name("name_value_pair") else {
let Some(name_node) = field!(recover, ctx, pair, "name") else {
let name = conjure_cp_core::ast::Name::user(
&ctx.source_code[name_node.start_byte()..name_node.end_byte()],
);
let Some(value_node) = field!(recover, ctx, pair, "value") else {
let saved_context = ctx.typechecking_context;
let saved_inner_context = ctx.inner_typechecking_context;
ctx.typechecking_context = ctx.inner_typechecking_context;
ctx.inner_typechecking_context = TypecheckingContext::Unknown;
let value = parse_expression(ctx, value_node)?;
ctx.typechecking_context = saved_context;
ctx.inner_typechecking_context = saved_inner_context;
let Some(value) = value else {
Ok(Some(AbstractLiteral::Variant(
conjure_cp_core::ast::Moo::new(conjure_cp_core::ast::records::Field { name, value }),
)))
fn parse_record(
let saved_ctx = ctx.typechecking_context;
let saved_inner_ctx = ctx.inner_typechecking_context;
let mut values = Vec::new();
let mut had_error = false;
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 {
// Record fields are heterogeneous, just like tuple positions.
ctx.typechecking_context = TypecheckingContext::Unknown;
let Some(value) = parse_expression(ctx, value_node)? else {
had_error = true;
continue;
values.push(conjure_cp_core::ast::records::Field { name, value });
ctx.typechecking_context = saved_ctx;
ctx.inner_typechecking_context = saved_inner_ctx;
if had_error {
} else {
Ok(Some(AbstractLiteral::Record(values)))
fn parse_tuple(
// Save the typechecking context
let mut elements = Vec::new();
for child in named_children(node) {
// A tuple is heterogeneous: each position has its own type, so the surrounding scalar
// context says nothing about any given element. `(y, z)` of type `(int, bool)` must not
// have `z` checked against the tuple's own context.
let Some(expr) = parse_expression(ctx, child)? else {
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 matches!(child.kind(), "single_line_comment" | "language_declaration") {
if child.kind() == "arithmetic_expr"
|| child.kind() == "bool_expr"
|| child.kind() == "comparison_expr"
|| child.kind() == "atom"
// Parse elements with inner typechecking context
ctx.typechecking_context = saved_inner_ctx;
// Parse domains with unknown typechecking context
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)))
fn parse_mset_literal(
let Some(expression) = parse_expression(ctx, child)? else {
elements.push(expression);
Ok(Some(AbstractLiteral::MSet(elements)))
fn parse_sequence_literal(
Ok(Some(AbstractLiteral::Sequence(elements)))
fn parse_function_literal(
let mut pairs = Vec::new();
for pair in named_children(node) {
// key/value can be of any type (matching the domain/codomain of the function), so
// typecheck context is relaxed here, mirroring the other abstract literal parsers.
let Some(key_node) = field!(recover, ctx, pair, "key") else {
let Some(key) = parse_expression(ctx, key_node)? else {
pairs.push((key, value));
Ok(Some(AbstractLiteral::Function(pairs)))
fn parse_relation_literal(
let mut tuples = Vec::new();
for tuple_node in named_children(node) {
let mut fields = Vec::new();
for field_node in named_children(&tuple_node) {
// Each column can be of any type (matching the relation's own column domains), so
let Some(expr) = parse_expression(ctx, field_node)? else {
fields.push(expr);
tuples.push(fields);
Ok(Some(AbstractLiteral::Relation(tuples)))
fn parse_partition_literal(
let mut parts = Vec::new();
for part_node in named_children(node) {
// Each part is itself a set literal; its elements can be of any type (matching the
// partition's own inner domain), so typecheck context is relaxed here, mirroring the
// other abstract literal parsers.
let Some(AbstractLiteral::Set(elements)) = parse_set_literal(ctx, &part_node)? else {
parts.push(elements);
Ok(Some(AbstractLiteral::Partition(parts)))
fn parse_permutation_literal(
let mut cycles = Vec::new();
for cycle_node in named_children(node) {
for element_node in named_children(&cycle_node) {
// Each element can be of any type (matching the permutation's own inner domain), so
let Some(expr) = parse_expression(ctx, element_node)? else {
cycles.push(elements);
Ok(Some(AbstractLiteral::Permutation(cycles)))