Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions crates/codegen-v2/parser/src/lexer/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,22 @@ impl LexerModelBuilder {
}
Lexeme::Keyword { kind, reserved, .. } => {
if match reserved {
None => true,
Some(VersionSpecifier::Never) => false,
Some(VersionSpecifier::From { .. }) => true,
Some(VersionSpecifier::Till { .. }) => true,
Some(VersionSpecifier::Range { .. }) => true,
VersionSpecifier::Always => true,
VersionSpecifier::Never => false,
VersionSpecifier::From { .. } => true,
VersionSpecifier::Till { .. } => true,
VersionSpecifier::Range { .. } => true,
} {
kinds.insert(format!("{kind}_Reserved"));
}

if reserved.is_some() {
if match reserved {
VersionSpecifier::Always => false,
VersionSpecifier::Never => true,
VersionSpecifier::From { .. } => true,
VersionSpecifier::Till { .. } => true,
VersionSpecifier::Range { .. } => true,
} {
kinds.insert(format!("{kind}_Unreserved"));
}
}
Expand All @@ -126,7 +132,7 @@ impl LexerModelBuilder {
item.definitions.iter().map(|def| Lexeme::Keyword {
kind: item.name.to_string(),
regex: self.convert_keyword_value(&def.value),
reserved: def.reserved.clone(),
reserved: def.reserved.clone().unwrap_or_default(),
})
}

Expand Down
2 changes: 1 addition & 1 deletion crates/codegen-v2/parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ pub enum Lexeme {
Keyword {
kind: String,
regex: String,
reserved: Option<VersionSpecifier>,
reserved: VersionSpecifier,
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ fn check_version_specifier(
};

match &**specifier {
SpannedVersionSpecifier::Always => {}
SpannedVersionSpecifier::Never => {}
SpannedVersionSpecifier::From { from } => {
check_version(analysis, from);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ impl VersionSet {
language: &SpannedLanguage,
) {
match specifier {
SpannedVersionSpecifier::Always => {
self.add_version_range(&language.versions[0], &MAX_VERSION);
}
SpannedVersionSpecifier::Never => {
// Do nothing.
}
Expand Down
3 changes: 3 additions & 0 deletions crates/language-v2/definition/src/model/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use crate::model::{
StructItem, TokenItem, TriviaItem,
};

/// An item is the smallest unit of a language definition.
///
/// It represents both terminals and nonterminals.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, EnumDiscriminants, ParseInputTokens, WriteOutputTokens)]
#[serde(tag = "type")]
Expand Down
19 changes: 14 additions & 5 deletions crates/language-v2/definition/src/model/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::collections::BTreeSet;
use std::path::PathBuf;

use indexmap::IndexSet;
use language_v2_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOutputTokens};
Expand All @@ -9,21 +8,29 @@ use serde::{Deserialize, Serialize};
use super::BuiltIn;
use crate::model::{BuiltInContext, Field, Identifier, Item, TriviaParser, VersionSpecifier};

/// A representation of a Language definition
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
pub struct Language {
/// The name of the language
pub name: Identifier,

pub binding_rules_file: PathBuf,

/// Each language must have a single root item
pub root_item: Identifier,

/// The leading trivia parser
pub leading_trivia: TriviaParser,

/// The trailing trivia parser
pub trailing_trivia: TriviaParser,

/// The supported versions of the language
pub versions: IndexSet<Version>,

/// The sections of the language
pub sections: Vec<Section>,

/// The built-in contexts
pub built_ins: Vec<BuiltInContext>,
}

Expand Down Expand Up @@ -57,7 +64,7 @@ impl Language {

let mut add_spec = |spec: &Option<VersionSpecifier>| {
if let Some(spec) = spec {
res.extend(spec.versions().cloned());
res.extend(spec.breaking_versions().cloned());
}
};

Expand Down Expand Up @@ -130,7 +137,7 @@ impl Language {

let mut add_spec = |spec: &Option<VersionSpecifier>| {
if let Some(spec) = spec {
res.extend(spec.versions().cloned());
res.extend(spec.breaking_versions().cloned());
}
};

Expand Down Expand Up @@ -160,6 +167,7 @@ impl Language {
}
}

/// A section is a named container for topics, used for organizing the large grammar definition in user documentation.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
pub struct Section {
Expand All @@ -174,6 +182,7 @@ impl Section {
}
}

/// A topic is a named container for items, used for organizing the large grammar definition in user documentation.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
pub struct Topic {
Expand Down
19 changes: 19 additions & 0 deletions crates/language-v2/definition/src/model/nonterminals/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,28 @@ use serde::{Deserialize, Serialize};

use crate::model::{Identifier, VersionSpecifier};

/// Error recovery for fields, this is used by the parser to recover in case of missing or unexpected tokens.
///
/// Note: Some nonterminals have both a terminator and delimiters, ie `DoWhileStatement`.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
pub struct FieldsErrorRecovery {
/// Error recovery happens at a terminator.
///
/// For example `PragmaDirective` has a `Semicolon` terminator, that could
/// be used to recover from an invalid `pragma` directive like
/// ```
/// pragma soldity ^0.8.0;
/// ```
#[serde(skip_serializing_if = "Option::is_none")]
pub terminator: Option<Identifier>,

/// Error recovery happens at delimiters.
///
/// For example `TupleExpression` has a `OpenParen` and `CloseParen` delimiters,
/// that could be used to recover from an invalid tuple like
/// `(pragma, bar)`
/// ~~~~~ -> This is not a valid expression
#[serde(skip_serializing_if = "Option::is_none")]
pub delimiters: Option<FieldDelimiters>,
}
Expand All @@ -29,6 +45,9 @@ pub struct FieldDelimiters {
pub terminals_matched_acceptance_threshold: Option<u8>,
}

/// A `Field` of a nonterminal that can be either required or optional.
///
/// Note: `Optional` fields are versioned, `Required` fields are not.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
#[serde(tag = "type")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ use serde::{Deserialize, Serialize};

use crate::model::{Field, FieldsErrorRecovery, Identifier, VersionSpecifier};

/// A `StructItem` is a nonterminal that can have fields.
/// It roughly corresponds to a sequence of `Item`s.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
pub struct StructItem {
pub name: Identifier,

/// Whether the struct is enabled
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<VersionSpecifier>,

/// Error recovery information if this struct supports it
#[serde(skip_serializing_if = "Option::is_none")]
pub error_recovery: Option<FieldsErrorRecovery>,

/// The fields of the struct, in the order they should appear in the source code
#[serde(with = "indexmap::map::serde_seq")]
pub fields: IndexMap<Identifier, Field>,
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@ use language_v2_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOu
use semver::Version;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
#[serde(tag = "type")]
pub enum VersionSpecifier {
#[default]
Always,
Never,
From { from: Version },
Till { till: Version },
Range { from: Version, till: Version },
From {
from: Version,
},
Till {
till: Version,
},
Range {
from: Version,
till: Version,
},
}

impl VersionSpecifier {
pub fn contains(&self, version: &Version) -> bool {
match self {
VersionSpecifier::Always => true,
VersionSpecifier::Never => false,
VersionSpecifier::From { from } => from <= version,
VersionSpecifier::Till { till } => version < till,
Expand All @@ -23,9 +33,9 @@ impl VersionSpecifier {
}

/// Returns an iterator over the versions specified as the upper and lower bound.
pub fn versions(&self) -> impl Iterator<Item = &Version> {
pub fn breaking_versions(&self) -> impl Iterator<Item = &Version> {
match self {
VersionSpecifier::Never => [None, None],
VersionSpecifier::Always | VersionSpecifier::Never => [None, None],
VersionSpecifier::From { from } => [Some(from), None],
VersionSpecifier::Till { till } => [None, Some(till)],
VersionSpecifier::Range { from, till } => [Some(from), Some(till)],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: Duplicate map key.
--> src/fail/p0_parsing/duplicate_map_key/test.rs:20:25
--> src/fail/p0_parsing/duplicate_map_key/test.rs:19:25
|
20 | field_1 = Required(Baz),
19 | field_1 = Required(Baz),
| ^^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: Set entries must be unique.
--> src/fail/p0_parsing/duplicate_set_entry/test.rs:9:26
--> src/fail/p0_parsing/duplicate_set_entry/test.rs:8:26
|
9 | versions = ["1.0.0", "1.0.0", "3.0.0"],
8 | versions = ["1.0.0", "1.0.0", "3.0.0"],
| ^^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: Expected a non-empty string.
--> src/fail/p0_parsing/empty_string/test.rs:15:76
--> src/fail/p0_parsing/empty_string/test.rs:14:76
|
15 | items = [Token(name = Bar, definitions = [TokenDefinition(Atom(""))])]
14 | items = [Token(name = Bar, definitions = [TokenDefinition(Atom(""))])]
| ^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: Expected field: 'title'.
--> src/fail/p0_parsing/missing_field/test.rs:12:9
--> src/fail/p0_parsing/missing_field/test.rs:11:9
|
12 | topics = []
11 | topics = []
| ^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: unexpected token, expected `)`
--> src/fail/p0_parsing/unrecognized_field/test.rs:19:13
--> src/fail/p0_parsing/unrecognized_field/test.rs:18:13
|
19 | unrecognized_field = true
18 | unrecognized_field = true
| ^^^^^^^^^^^^^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar1,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: Expected a variant: 'Struct' or 'Enum' or 'Repeated' or 'Separated' or 'Precedence' or 'Trivia' or 'Keyword' or 'Token' or 'Fragment'
--> src/fail/p0_parsing/unrecognized_variant/test.rs:24:26
--> src/fail/p0_parsing/unrecognized_variant/test.rs:23:26
|
24 | items = [Unrecognized(true)]
23 | items = [Unrecognized(true)]
| ^^^^^^^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: An expression with the name 'Expression1' already exists.
--> src/fail/p1_definitions/duplicate_expression_name/test.rs:34:36
--> src/fail/p1_definitions/duplicate_expression_name/test.rs:33:36
|
34 | ... name = Expression1,
33 | ... name = Expression1,
| ^^^^^^^^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar1,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
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/p1_definitions/duplicate_item_name/test.rs:17:31
--> src/fail/p1_definitions/duplicate_item_name/test.rs:16:31
|
17 | Struct(name = Bar1, fields = (field = Required(Bar2))),
16 | Struct(name = Bar1, fields = (field = Required(Bar2))),
| ^^^^
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

language_v2_macros::compile!(Language(
name = Foo,
binding_rules_file = "bindings/rules.msgb",
root_item = Bar,
leading_trivia = Sequence([]),
trailing_trivia = Sequence([]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: A variant referencing 'Baz1' already exists.
--> src/fail/p1_definitions/duplicate_variant_name/test.rs:21:49
--> src/fail/p1_definitions/duplicate_variant_name/test.rs:20:49
|
21 | EnumVariant(reference = Baz1)
20 | EnumVariant(reference = Baz1)
| ^^^^
Loading