Skip to content

Commit

Permalink
remove named parsers from codegen (#529)
Browse files Browse the repository at this point in the history
Based on recent CST updates, we promoted named structures to top-level
productions, thus making them redundant.

This PR removes them, along with a lot of special cases to handle them
in other parts of the codebase.
  • Loading branch information
OmarTawfik authored Jul 10, 2023
1 parent 7ccca87 commit f77330a
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 262 deletions.
7 changes: 1 addition & 6 deletions crates/codegen/ebnf/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ use crate::{nodes::EbnfNode, serialization::GenerateEbnf};

impl GenerateEbnf for ParserRef {
fn generate_ebnf(&self) -> EbnfNode {
let definition = self.definition.generate_ebnf();

return match &self.name {
None => definition,
Some(name) => EbnfNode::sub_statement(name.to_owned(), None, definition),
};
return self.definition.generate_ebnf();
}
}

Expand Down
2 changes: 0 additions & 2 deletions crates/codegen/schema/src/types/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ pub type ParserRef = std::rc::Rc<Parser>;
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash)]
#[serde(deny_unknown_fields)]
pub struct Parser {
#[serde(default)]
pub name: Option<String>,
#[serde(flatten)]
pub definition: ParserDefinition,
}
Expand Down
8 changes: 4 additions & 4 deletions crates/codegen/schema/src/validation/rules/definitions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
mod keywords;
mod parsers;
mod operators;
mod productions;
mod versions;

use crate::{
types::LanguageDefinitionRef,
validation::{
rules::definitions::{
keywords::Keywords, parsers::Parsers, productions::Productions, versions::Versions,
keywords::Keywords, operators::Operators, productions::Productions, versions::Versions,
},
visitors::Reporter,
},
Expand All @@ -17,10 +17,10 @@ use codegen_utils::errors::CodegenResult;
pub fn run(language: &LanguageDefinitionRef) -> CodegenResult<()> {
let mut reporter = Reporter::new();

Keywords::validate(language, &mut reporter);
Operators::validate(language, &mut reporter);
Productions::validate(language, &mut reporter);
Versions::validate(language, &mut reporter);
Parsers::validate(language, &mut reporter);
Keywords::validate(language, &mut reporter);

return reporter.to_result();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::collections::HashMap;

use crate::{
types::{LanguageDefinitionRef, PrecedenceParserRef, ProductionRef},
validation::visitors::{run_visitor, LocationRef, Reporter, Visitor},
};

pub struct Operators {
language: LanguageDefinitionRef,
current_production: Option<ProductionRef>,
operators_already_seen: HashMap<String, ProductionRef>,
}

impl Operators {
pub fn validate(language: &LanguageDefinitionRef, reporter: &mut Reporter) {
let mut instance = Self {
language: language.to_owned(),

current_production: None,
operators_already_seen: HashMap::new(),
};

run_visitor(&mut instance, language, reporter);
}
}

impl Visitor for Operators {
fn visit_production(
&mut self,
production: &ProductionRef,
_location: &LocationRef,
_reporter: &mut Reporter,
) -> bool {
self.current_production = Some(production.to_owned());
return true;
}

fn visit_parser(
&mut self,
_parser: &crate::types::ParserRef,
_location: &LocationRef,
_reporter: &mut Reporter,
) -> bool {
return false; // skip
}

fn visit_scanner(
&mut self,
_scanner: &crate::types::ScannerRef,
_location: &LocationRef,
_reporter: &mut Reporter,
) -> bool {
return false; // skip
}

fn visit_precedence_parser(
&mut self,
parser: &PrecedenceParserRef,
location: &LocationRef,
reporter: &mut Reporter,
) -> bool {
for operator in parser.operators.iter() {
let name = &operator.name;
if self.language.productions.contains_key(name) {
reporter.report(location, Errors::OperatorNamedAsProduction(name.to_owned()));
continue;
}

let current_production = self.current_production.as_ref().unwrap();

match self.operators_already_seen.get(name) {
Some(existing_production) => {
if current_production.name == existing_production.name {
// Operators can share a common name under the same production.
continue;
}

reporter.report(
location,
Errors::OperatorDefinedInAnotherProduction(
name.to_owned(),
existing_production.name.to_owned(),
),
);
}
None => {
self.operators_already_seen
.insert(name.to_owned(), current_production.to_owned());
}
};
}

return false;
}
}

#[derive(thiserror::Error, Debug)]
enum Errors {
#[error("Operator '{0}' cannot have the same name as a top-level production.")]
OperatorNamedAsProduction(String),
#[error("Operator '{0}' is defined in another production '{1}'.")]
OperatorDefinedInAnotherProduction(String, String),
}
142 changes: 0 additions & 142 deletions crates/codegen/schema/src/validation/rules/definitions/parsers/mod.rs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl Visitor for ChildrenCount {
) -> bool {
match &parser.definition {
ParserDefinition::Choice(children) | ParserDefinition::Sequence(children) => {
if parser.name.is_none() && children.len() < 2 {
if children.len() < 2 {
reporter.report(location, Errors::MinChildrenCount(2));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,6 @@ impl Visitor for ConsistentShape {

impl ConsistentShape {
fn is_single_named(&self, parser: &ParserRef) -> bool {
if parser.name.is_some() {
return true;
}

match &parser.definition {
ParserDefinition::Choice(_)
| ParserDefinition::DelimitedBy { .. }
Expand Down
Loading

0 comments on commit f77330a

Please sign in to comment.