Merge pull request #614 from HigherOrderCO/dont-allow-repeated-field-names

Dont allow repeated field names
This commit is contained in:
imaqtkatt 2024-07-03 19:22:58 +00:00 committed by GitHub
commit 4b0bdf506c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 52 additions and 4 deletions

View File

@ -35,6 +35,7 @@ and this project does not currently adhere to a particular versioning scheme.
- Add `elif` chains to functional syntax. ([#596][gh-596])
- Add local definitions to imperative syntax. ([#562][gh-562])
- Add local definitions to functional syntax.
- Add repeated field name error message.
## [0.2.35] - 2024-06-06

View File

@ -5,7 +5,7 @@ use crate::{
display::DisplayFn, Adt, Book, CtrField, Definition, FanKind, HvmDefinition, MatchRule, Name, Num, Op,
Pattern, Rule, Tag, Term, STRINGS,
},
imp::{parser::PyParser, Enum, Variant},
imp::{parser::PyParser, Enum, RepeatedNames, Variant},
maybe_grow,
};
use highlight_error::highlight_error;
@ -166,8 +166,8 @@ impl<'a> TermParser<'a> {
// name
if self.try_consume("(") {
self.skip_trivia();
let name = self.parse_top_level_name()?;
let name = Name::new(format!("{typ_name}/{name}"));
let ctr_name = self.parse_top_level_name()?;
let ctr_name = Name::new(format!("{typ_name}/{ctr_name}"));
fn parse_field(p: &mut TermParser) -> ParseResult<CtrField> {
let rec = p.try_consume("~");
@ -177,7 +177,10 @@ impl<'a> TermParser<'a> {
}
let fields = self.list_like(parse_field, "", ")", "", false, 0)?;
Ok((name, fields))
if let Some(field) = fields.find_repeated_names().into_iter().next() {
return Err(format!("Found a repeated field '{field}' in constructor {ctr_name}."));
}
Ok((ctr_name, fields))
} else {
// name
let name = self.labelled(|p| p.parse_top_level_name(), "datatype constructor name")?;

View File

@ -4,6 +4,7 @@ pub mod parser;
pub mod to_fun;
use crate::fun::{CtrField, Name, Num, Op};
use indexmap::{IndexMap, IndexSet};
use interner::global::GlobalString;
#[derive(Clone, Debug)]
@ -236,3 +237,23 @@ impl InPlaceOp {
}
}
}
pub trait RepeatedNames {
fn find_repeated_names(&self) -> IndexSet<Name>;
}
impl RepeatedNames for Vec<CtrField> {
fn find_repeated_names(&self) -> IndexSet<Name> {
let mut count = IndexMap::new();
for field in self.iter() {
*count.entry(field.nam.clone()).or_insert(0) += 1;
}
count.into_iter().filter_map(|(name, count)| if count > 1 { Some(name) } else { None }).collect()
}
}
impl RepeatedNames for Variant {
fn find_repeated_names(&self) -> IndexSet<Name> {
self.fields.find_repeated_names()
}
}

View File

@ -8,6 +8,8 @@ use crate::{
};
use TSPL::Parser;
use super::RepeatedNames;
pub struct PyParser<'i> {
pub input: &'i str,
pub index: usize,
@ -1041,6 +1043,9 @@ impl<'a> PyParser<'a> {
if self.starts_with("{") {
fields = self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)?;
}
if let Some(field) = fields.find_repeated_names().into_iter().next() {
return Err(format!("Found a repeated field '{field}' in constructor {ctr_name}."));
}
Ok(Variant { name: ctr_name, fields })
}
@ -1059,6 +1064,9 @@ impl<'a> PyParser<'a> {
} else {
vec![]
};
if let Some(field) = fields.find_repeated_names().into_iter().next() {
return Err(format!("Found a repeated field '{field}' in object {name}."));
}
if !self.is_eof() {
self.consume_new_line()?;
}

View File

@ -0,0 +1,8 @@
type Expr
= (Lit Int)
| (Plus Expr Expr)
eval (Expr/Lit i) = i
eval (Expr/Plus l r) = (+ (eval l) (eval r))
main = (eval (Expr/Plus (Expr/Lit 80) (Expr/Lit 90)))

View File

@ -0,0 +1,7 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/repeated_datatype_name.bend
---
Errors:
In tests/golden_tests/parse_file/repeated_datatype_name.bend :
Found a repeated field 'Expr' in constructor Expr/Plus.