Skip to content

Commit 268c53d

Browse files
teofrggiraldez
andauthored
Added a default value for VersionSpecifier and some documentation (#1500)
Originally I wanted to stop wrapping `VersionSpecifier` in an `Option`, and parse it as `Always` if it's not present, but that brings some issues with the span information, and we'd need to differentiate between an `Always` with span (explicit) and one without (implicit). However, I think having the default value representable is good to simplify some algorithms, in particular, the information of what a missing field on the language definition means is now explicit. I also added some documentation here and there. --------- Co-authored-by: Gustavo Giráldez <ggiraldez@manas.tech>
1 parent b124729 commit 268c53d

File tree

51 files changed

+130
-3505
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+130
-3505
lines changed

crates/codegen-v2/parser/src/lexer/builder.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,22 @@ impl LexerModelBuilder {
9595
}
9696
Lexeme::Keyword { kind, reserved, .. } => {
9797
if match reserved {
98-
None => true,
99-
Some(VersionSpecifier::Never) => false,
100-
Some(VersionSpecifier::From { .. }) => true,
101-
Some(VersionSpecifier::Till { .. }) => true,
102-
Some(VersionSpecifier::Range { .. }) => true,
98+
VersionSpecifier::Always => true,
99+
VersionSpecifier::Never => false,
100+
VersionSpecifier::From { .. } => true,
101+
VersionSpecifier::Till { .. } => true,
102+
VersionSpecifier::Range { .. } => true,
103103
} {
104104
kinds.insert(format!("{kind}_Reserved"));
105105
}
106106

107-
if reserved.is_some() {
107+
if match reserved {
108+
VersionSpecifier::Always => false,
109+
VersionSpecifier::Never => true,
110+
VersionSpecifier::From { .. } => true,
111+
VersionSpecifier::Till { .. } => true,
112+
VersionSpecifier::Range { .. } => true,
113+
} {
108114
kinds.insert(format!("{kind}_Unreserved"));
109115
}
110116
}
@@ -126,7 +132,7 @@ impl LexerModelBuilder {
126132
item.definitions.iter().map(|def| Lexeme::Keyword {
127133
kind: item.name.to_string(),
128134
regex: self.convert_keyword_value(&def.value),
129-
reserved: def.reserved.clone(),
135+
reserved: def.reserved.clone().unwrap_or_default(),
130136
})
131137
}
132138

crates/codegen-v2/parser/src/lexer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ pub enum Lexeme {
3939
Keyword {
4040
kind: String,
4141
regex: String,
42-
reserved: Option<VersionSpecifier>,
42+
reserved: VersionSpecifier,
4343
},
4444
}

crates/language-v2/definition/src/compiler/analysis/p2_version_specifiers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ fn check_version_specifier(
263263
};
264264

265265
match &**specifier {
266+
SpannedVersionSpecifier::Always => {}
266267
SpannedVersionSpecifier::Never => {}
267268
SpannedVersionSpecifier::From { from } => {
268269
check_version(analysis, from);

crates/language-v2/definition/src/compiler/utils/version_set.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ impl VersionSet {
2828
language: &SpannedLanguage,
2929
) {
3030
match specifier {
31+
SpannedVersionSpecifier::Always => {
32+
self.add_version_range(&language.versions[0], &MAX_VERSION);
33+
}
3134
SpannedVersionSpecifier::Never => {
3235
// Do nothing.
3336
}

crates/language-v2/definition/src/model/item.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use crate::model::{
99
StructItem, TokenItem, TriviaItem,
1010
};
1111

12+
/// An item is the smallest unit of a language definition.
13+
///
14+
/// It represents both terminals and nonterminals.
1215
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1316
#[derive_spanned_type(Clone, Debug, EnumDiscriminants, ParseInputTokens, WriteOutputTokens)]
1417
#[serde(tag = "type")]

crates/language-v2/definition/src/model/manifest.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::collections::BTreeSet;
2-
use std::path::PathBuf;
32

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

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

17-
pub binding_rules_file: PathBuf,
18-
18+
/// Each language must have a single root item
1919
pub root_item: Identifier,
2020

21+
/// The leading trivia parser
2122
pub leading_trivia: TriviaParser,
23+
24+
/// The trailing trivia parser
2225
pub trailing_trivia: TriviaParser,
2326

27+
/// The supported versions of the language
2428
pub versions: IndexSet<Version>,
2529

30+
/// The sections of the language
2631
pub sections: Vec<Section>,
32+
33+
/// The built-in contexts
2734
pub built_ins: Vec<BuiltInContext>,
2835
}
2936

@@ -57,7 +64,7 @@ impl Language {
5764

5865
let mut add_spec = |spec: &Option<VersionSpecifier>| {
5966
if let Some(spec) = spec {
60-
res.extend(spec.versions().cloned());
67+
res.extend(spec.breaking_versions().cloned());
6168
}
6269
};
6370

@@ -130,7 +137,7 @@ impl Language {
130137

131138
let mut add_spec = |spec: &Option<VersionSpecifier>| {
132139
if let Some(spec) = spec {
133-
res.extend(spec.versions().cloned());
140+
res.extend(spec.breaking_versions().cloned());
134141
}
135142
};
136143

@@ -160,6 +167,7 @@ impl Language {
160167
}
161168
}
162169

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

185+
/// A topic is a named container for items, used for organizing the large grammar definition in user documentation.
177186
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
178187
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
179188
pub struct Topic {

crates/language-v2/definition/src/model/nonterminals/field.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,28 @@ use serde::{Deserialize, Serialize};
33

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

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

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

48+
/// A `Field` of a nonterminal that can be either required or optional.
49+
///
50+
/// Note: `Optional` fields are versioned, `Required` fields are not.
3251
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
3352
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
3453
#[serde(tag = "type")]

crates/language-v2/definition/src/model/nonterminals/struct_.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ use serde::{Deserialize, Serialize};
44

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

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

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

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

22+
/// The fields of the struct, in the order they should appear in the source code
1823
#[serde(with = "indexmap::map::serde_seq")]
1924
pub fields: IndexMap<Identifier, Field>,
2025
}

crates/language-v2/definition/src/model/utils/version_specifier.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,29 @@ use language_v2_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOu
22
use semver::Version;
33
use serde::{Deserialize, Serialize};
44

5-
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
5+
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
66
#[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)]
77
#[serde(tag = "type")]
88
pub enum VersionSpecifier {
9+
#[default]
10+
Always,
911
Never,
10-
From { from: Version },
11-
Till { till: Version },
12-
Range { from: Version, till: Version },
12+
From {
13+
from: Version,
14+
},
15+
Till {
16+
till: Version,
17+
},
18+
Range {
19+
from: Version,
20+
till: Version,
21+
},
1322
}
1423

1524
impl VersionSpecifier {
1625
pub fn contains(&self, version: &Version) -> bool {
1726
match self {
27+
VersionSpecifier::Always => true,
1828
VersionSpecifier::Never => false,
1929
VersionSpecifier::From { from } => from <= version,
2030
VersionSpecifier::Till { till } => version < till,
@@ -23,9 +33,9 @@ impl VersionSpecifier {
2333
}
2434

2535
/// Returns an iterator over the versions specified as the upper and lower bound.
26-
pub fn versions(&self) -> impl Iterator<Item = &Version> {
36+
pub fn breaking_versions(&self) -> impl Iterator<Item = &Version> {
2737
match self {
28-
VersionSpecifier::Never => [None, None],
38+
VersionSpecifier::Always | VersionSpecifier::Never => [None, None],
2939
VersionSpecifier::From { from } => [Some(from), None],
3040
VersionSpecifier::Till { till } => [None, Some(till)],
3141
VersionSpecifier::Range { from, till } => [Some(from), Some(till)],

crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
language_v2_macros::compile!(Language(
44
name = Foo,
5-
binding_rules_file = "bindings/rules.msgb",
65
root_item = Bar,
76
leading_trivia = Sequence([]),
87
trailing_trivia = Sequence([]),

0 commit comments

Comments
 (0)