Skip to content

Commit

Permalink
remove FieldKind from DSL v2
Browse files Browse the repository at this point in the history
Makes the grammar a lot simpler, since it is no longer needed after enums were simplified in #610
  • Loading branch information
OmarTawfik committed Dec 3, 2023
1 parent 8bca40a commit 8018cea
Show file tree
Hide file tree
Showing 35 changed files with 5,424 additions and 2,355 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ fn check_precedence_items(analysis: &mut Analysis) {
}

for primary_expression in &item.primary_expressions {
let expression = &primary_expression.expression;
let reference = &primary_expression.reference;

if !current_expressions.insert(expression) {
if !current_expressions.insert(reference) {
analysis
.errors
.add(expression, &Errors::ExistingExpression(expression));
.add(reference, &Errors::ExistingExpression(reference));
}
}
}
Expand Down
176 changes: 58 additions & 118 deletions crates/codegen/language/definition/src/compiler/analysis/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ use crate::{
compiler::{analysis::Analysis, version_set::VersionSet},
internals::Spanned,
model::{
Identifier, SpannedEnumItem, SpannedEnumVariant, SpannedField, SpannedFieldKind,
SpannedFragmentItem, SpannedItem, SpannedKeywordDefinition, SpannedKeywordItem,
SpannedPrecedenceExpression, SpannedPrecedenceItem, SpannedPrecedenceOperator,
SpannedPrimaryExpression, SpannedRepeatedItem, SpannedScanner, SpannedSeparatedItem,
SpannedStructItem, SpannedTokenDefinition, SpannedTokenItem, SpannedTriviaItem,
SpannedTriviaParser, SpannedVersionSpecifier,
Identifier, SpannedEnumItem, SpannedEnumVariant, SpannedField, SpannedFragmentItem,
SpannedItem,
SpannedItemDiscriminants::{self, *},
SpannedKeywordDefinition, SpannedKeywordItem, SpannedPrecedenceExpression,
SpannedPrecedenceItem, SpannedPrecedenceOperator, SpannedPrimaryExpression,
SpannedRepeatedItem, SpannedScanner, SpannedSeparatedItem, SpannedStructItem,
SpannedTokenDefinition, SpannedTokenItem, SpannedTriviaItem, SpannedTriviaParser,
SpannedVersionSpecifier,
},
};
use indexmap::IndexMap;
use semver::Version;
use std::fmt::Debug;
use strum_macros::Display;

pub(crate) fn analyze_references(analysis: &mut Analysis) {
let language = analysis.language.clone();
Expand All @@ -26,7 +27,7 @@ pub(crate) fn analyze_references(analysis: &mut Analysis) {
None,
&language.root_item,
&enablement,
ReferenceFilter::NonTerminals,
&[Struct, Enum, Repeated, Separated, Precedence],
);

check_trivia_parser(analysis, &language.leading_trivia, &enablement);
Expand Down Expand Up @@ -105,7 +106,9 @@ fn check_enum(analysis: &mut Analysis, item: &SpannedEnumItem, enablement: &Vers
Some(name),
reference,
&enablement,
ReferenceFilter::Nodes,
&[
Struct, Enum, Repeated, Separated, Precedence, Keyword, Token,
],
);
}
}
Expand All @@ -125,7 +128,9 @@ fn check_repeated(analysis: &mut Analysis, item: &SpannedRepeatedItem, enablemen
Some(name),
repeated,
&enablement,
ReferenceFilter::Nodes,
&[
Struct, Enum, Repeated, Separated, Precedence, Keyword, Token,
],
);
}

Expand All @@ -145,15 +150,12 @@ fn check_separated(analysis: &mut Analysis, item: &SpannedSeparatedItem, enablem
Some(name),
separated,
&enablement,
ReferenceFilter::Nodes,
);
check_reference(
analysis,
Some(name),
separator,
&enablement,
ReferenceFilter::Tokens,
&[
Struct, Enum, Repeated, Separated, Precedence, Keyword, Token,
],
);

check_reference(analysis, Some(name), separator, &enablement, &[Token]);
}

fn check_precedence(
Expand Down Expand Up @@ -192,19 +194,18 @@ fn check_precedence(
}

for primary_expression in primary_expressions {
let SpannedPrimaryExpression {
expression,
enabled,
} = primary_expression;
let SpannedPrimaryExpression { reference, enabled } = primary_expression;

let enablement = update_enablement(analysis, &enablement, enabled);

check_reference(
analysis,
Some(name),
expression,
reference,
&enablement,
ReferenceFilter::Nodes,
&[
Struct, Enum, Repeated, Separated, Precedence, Keyword, Token,
],
);
}
}
Expand All @@ -217,46 +218,34 @@ fn check_fields(
) {
for field in fields.values() {
match field {
SpannedField::Required { kind } => {
check_field_kind(analysis, source, kind, enablement);
SpannedField::Required { reference } => {
check_reference(
analysis,
source,
reference,
enablement,
&[
Struct, Enum, Repeated, Separated, Precedence, Keyword, Token,
],
);
}
SpannedField::Optional { kind, enabled } => {
SpannedField::Optional { reference, enabled } => {
let enablement = update_enablement(analysis, enablement, enabled);

check_field_kind(analysis, source, kind, &enablement);
}
};
}
}

fn check_field_kind(
analysis: &mut Analysis,
source: Option<&Identifier>,
kind: &SpannedFieldKind,
enablement: &VersionSet,
) {
match kind {
SpannedFieldKind::NonTerminal { item } => {
check_reference(
analysis,
source,
item,
enablement,
ReferenceFilter::NonTerminals,
);
}
SpannedFieldKind::Terminal { items } => {
for item in items {
check_reference(
analysis,
source,
item,
enablement,
ReferenceFilter::Terminals,
reference,
&enablement,
&[
// TODO(#638): remove [Separated] and [Repeated] from here, once they are allowed to be empty.
// Therefore, we ensure we always produce the parent node, even if it has no children.
Struct, Enum, Repeated, Separated, Precedence, Keyword, Token,
],
);
}
}
};
};
}
}

fn check_trivia_parser(
Expand All @@ -276,7 +265,7 @@ fn check_trivia_parser(
check_trivia_parser(analysis, parser, enablement);
}
SpannedTriviaParser::Trivia { trivia } => {
check_reference(analysis, None, trivia, enablement, ReferenceFilter::Trivia);
check_reference(analysis, None, trivia, enablement, &[Trivia]);
}
};
}
Expand All @@ -294,13 +283,7 @@ fn check_keyword(analysis: &mut Analysis, item: &SpannedKeywordItem, enablement:
definitions,
} = item;

check_reference(
analysis,
Some(name),
identifier,
enablement,
ReferenceFilter::Tokens,
);
check_reference(analysis, Some(name), identifier, enablement, &[Token]);

for definition in definitions {
let SpannedKeywordDefinition {
Expand Down Expand Up @@ -374,65 +357,17 @@ fn check_scanner(
check_scanner(analysis, source, not_followed_by, enablement);
}
SpannedScanner::Fragment { reference } => {
check_reference(
analysis,
source,
reference,
enablement,
ReferenceFilter::Fragments,
);
check_reference(analysis, source, reference, enablement, &[Fragment]);
}
};
}

/// Used by different checks above to make sure that references to other items are valid.
/// For example, struct fields can reference anything, but tokens can only reference fragments.
#[derive(Debug, Display, PartialEq, Eq)]
enum ReferenceFilter {
Nodes,

NonTerminals,
Terminals,

Trivia,
Tokens,

Fragments,
}

impl ReferenceFilter {
fn apply(&self, item: &SpannedItem) -> bool {
match (self, item) {
(Self::Nodes, item) => Self::NonTerminals.apply(item) || Self::Terminals.apply(item),
(
Self::NonTerminals,
SpannedItem::Struct { .. }
| SpannedItem::Enum { .. }
| SpannedItem::Repeated { .. }
| SpannedItem::Separated { .. }
| SpannedItem::Precedence { .. },
)
| (
Self::Terminals,
SpannedItem::Trivia { .. }
| SpannedItem::Keyword { .. }
| SpannedItem::Token { .. },
)
| (Self::Trivia, SpannedItem::Trivia { .. })
| (Self::Tokens, SpannedItem::Token { .. })
| (Self::Fragments, SpannedItem::Fragment { .. }) => true,

_ => false,
}
}
}

fn check_reference(
analysis: &mut Analysis,
source: Option<&Identifier>,
reference: &Spanned<Identifier>,
enablement: &VersionSet,
filter: ReferenceFilter,
expected_kinds: &[SpannedItemDiscriminants],
) {
let target = match analysis.metadata.get_mut(&**reference) {
Some(target) => target,
Expand All @@ -452,10 +387,11 @@ fn check_reference(
);
}

if !filter.apply(&target.item) {
let actual_kind: SpannedItemDiscriminants = target.item.as_ref().into();
if !expected_kinds.contains(&actual_kind) {
analysis.errors.add(
reference,
&Errors::InvalidReferenceFilter(reference, &filter),
&Errors::InvalidReferenceFilter(reference, &actual_kind, expected_kinds),
);
}

Expand Down Expand Up @@ -538,6 +474,10 @@ enum Errors<'err> {
UnknownReference(&'err Identifier),
#[error("Reference '{0}' is only defined in '{1}', but not in '{2}'.")]
InvalidReferenceVersion(&'err Identifier, &'err VersionSet, &'err VersionSet),
#[error("Reference '{0}' is not valid. Expected: {1}.")]
InvalidReferenceFilter(&'err Identifier, &'err ReferenceFilter),
#[error("Reference '{0}' of kind '{1:?}' is not valid. Expected: {2:?}")]
InvalidReferenceFilter(
&'err Identifier,
&'err SpannedItemDiscriminants,
&'err [SpannedItemDiscriminants],
),
}
3 changes: 2 additions & 1 deletion crates/codegen/language/definition/src/model/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use crate::model::{
};
use codegen_language_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOutputTokens};
use serde::{Deserialize, Serialize};
use strum_macros::EnumDiscriminants;

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(ParseInputTokens, WriteOutputTokens)]
#[derive_spanned_type(EnumDiscriminants, ParseInputTokens, WriteOutputTokens)]
pub enum Item {
Struct { item: StructItem },
Enum { item: EnumItem },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::model::{Identifier, VersionSpecifier};
use codegen_language_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOutputTokens};
use indexmap::IndexSet;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
Expand All @@ -21,18 +20,11 @@ pub struct FieldDelimiters {
#[derive_spanned_type(ParseInputTokens, WriteOutputTokens)]
pub enum Field {
Required {
kind: FieldKind,
reference: Identifier,
},
Optional {
kind: FieldKind,
reference: Identifier,

enabled: Option<VersionSpecifier>,
},
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(ParseInputTokens, WriteOutputTokens)]
pub enum FieldKind {
NonTerminal { item: Identifier },
Terminal { items: IndexSet<Identifier> },
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub enum OperatorModel {
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(ParseInputTokens, WriteOutputTokens)]
pub struct PrimaryExpression {
pub expression: Identifier,
pub reference: Identifier,

pub enabled: Option<VersionSpecifier>,
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ fn get_spanned_type(input: Type) -> Type {
| "EnumVariant"
| "Field"
| "FieldDelimiters"
| "FieldKind"
| "FieldsErrorRecovery"
| "FragmentItem"
| "InputItem"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ codegen_language_macros::compile!(Language(
PrecedenceExpression(name = Expression2, rule_name = X, operators = []),
PrecedenceExpression(name = Expression1, rule_name = X, operators = [])
],
primary_expressions = [PrimaryExpression(expression = Baz)]
primary_expressions = [PrimaryExpression(reference = Baz)]
),
Token(
name = Baz,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ codegen_language_macros::compile!(Language(
topics = [Topic(
title = "Topic One",
items = [
Struct(name = Bar1, fields = (field = Required(NonTerminal(Bar2)))),
Struct(name = Bar1, fields = (field = Required(NonTerminal(Bar2)))),
Struct(name = Bar2, fields = (field = Required(NonTerminal(Bar1))))
Struct(name = Bar1, fields = (field = Required(Bar2))),
Struct(name = Bar1, fields = (field = Required(Bar2))),
Struct(name = Bar2, fields = (field = Required(Bar1)))
]
)]
)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: An item with the name 'Bar1' already exists.
--> src/fail/definitions/duplicate_item_name/test.rs:15:31
|
15 | Struct(name = Bar1, fields = (field = Required(NonTerminal(Bar2)))),
15 | Struct(name = Bar1, fields = (field = Required(Bar2))),
| ^^^^
Loading

0 comments on commit 8018cea

Please sign in to comment.