Skip to content

Commit 20a7574

Browse files
committed
version assumption
1 parent bd766d4 commit 20a7574

File tree

7 files changed

+102
-69
lines changed

7 files changed

+102
-69
lines changed

opslang-ast/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
pub mod compat;
22
pub mod syntax;
3-
pub mod version;
43

54
pub use compat::*;
65
pub use syntax::*;

opslang-ast/src/syntax.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ macro_rules! declare_versions {
55
($([$current:vis])? $path:ident, $ty:ident; $($tt:tt)*) => {
66
pub mod $path;
77
pub struct $ty;
8-
impl $crate::version::sealed::VersionMarker for $ty {}
98

109
$(
1110
/// Type alias for the default version of the ast.

opslang-ast/src/version.rs

Lines changed: 0 additions & 25 deletions
This file was deleted.

opslang-parser/src/lib.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
pub mod version;
2+
use std::{marker::PhantomData, path::PathBuf};
3+
24
pub use version::*;
35

46
pub(crate) mod parol_macro;
@@ -9,9 +11,56 @@ pub(crate) mod parol_macro;
911
///
1012
/// - `Parser`: The type that distinguishes the parser. This is used to
1113
/// differentiate between different parsing strategies or contexts.
12-
pub trait ParseOps<Format, Parser>: Sized {
14+
pub trait ParseOps<Format: InputFormat, Parser>: Sized {
1315
type Context;
1416
type Error;
1517

1618
fn parse(from: Format, context: Self::Context) -> Result<Self, Self::Error>;
1719
}
20+
21+
/// A marker trait for input formats.
22+
#[diagnostic::on_unimplemented(
23+
message = "the type `{Self}` is not a valid input format",
24+
label = "this type does not implement the `InputFormat` trait"
25+
)]
26+
pub trait InputFormat {}
27+
28+
/// An input structure for the parser.
29+
pub struct ParserInput<'a> {
30+
/// The string content to be parsed.
31+
pub content: &'a str,
32+
33+
/// The name of the file from which the content was read.
34+
///
35+
/// FIXME: This is specific to V1 and not used for accessing the content.
36+
pub file_name: PathBuf,
37+
}
38+
39+
impl<'a> ParserInput<'a> {
40+
/// Assumes that the input is in the format of the inferred grammar version.
41+
pub fn assume_inferred<Version: GrammarVersion>(self) -> Versioned<Version, Self> {
42+
Versioned::assume_inferred(self)
43+
}
44+
}
45+
46+
/// A wrapper type for a value that is assumed to be in the specific format.
47+
pub struct Versioned<Version: GrammarVersion, T>(pub T, PhantomData<Version>);
48+
impl<Version: GrammarVersion, T> InputFormat for Versioned<Version, T> {}
49+
50+
impl<Version: GrammarVersion, T> Versioned<Version, T> {
51+
/// Creates a [`Versioned`] instance with the given value.
52+
///
53+
/// This is useful when the type system already knows the version of the value.
54+
pub fn assume_inferred(value: T) -> Self {
55+
Self(value, PhantomData)
56+
}
57+
58+
/// Creates a [`Versioned`] instance with an assumed version and the given value.
59+
///
60+
/// This is useful when you want to explicitly specify the version at the point of parsing, not just at the type level.
61+
/// If the type system already knows the version, you can use [`Versioned::assume_inferred`].
62+
pub fn assume_version(version: Version, value: T) -> Self {
63+
let _ = version;
64+
Self(value, PhantomData)
65+
}
66+
}

opslang-parser/src/version.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
1-
macro_rules! declare_versions {
2-
() => {};
3-
($([$current:vis])? $path:ident, $ty:ident; $($tt:tt)*) => {
4-
pub mod $path;
5-
pub struct $ty;
1+
/// A marker trait for grammar versions.
2+
pub trait GrammarVersion {}
63

4+
/// A macro to declare grammar versions and their associated modules.
5+
///
6+
/// Note that this macro **re-exports** the types from the module.
7+
macro_rules! declare_versions {
8+
(
79
$(
8-
/// Type alias for the default version of the parser.
9-
$current type Default = $ty;
10-
$current use $path::*;
11-
)?
10+
$([$current:vis])? $path:ident, $ty:ident;
11+
)*
12+
) => {
13+
$(
14+
pub mod $path;
15+
pub struct $ty;
16+
impl GrammarVersion for $ty {}
17+
18+
$(
19+
/// Type alias for the default version of the parser.
20+
$current type Default = $ty;
1221

13-
declare_versions! { $($tt)* }
22+
// Re-export the types from the module
23+
$current use $path::*;
24+
)?
25+
)*
26+
27+
// assert that `[pub]` is used only once
28+
29+
/// Assertion that [`Default`] type alias exists at this scope.
30+
///
31+
/// This is a compile-time check that will fail if [`Default`] is not defined.
32+
#[allow(dead_code)]
33+
const _: () = {
34+
let _: Default;
35+
};
1436
}
1537
}
1638

17-
/// Assertion that [`Default`] type alias exists at this scope.
18-
///
19-
/// This is a compile-time check that will fail if [`Default`] is not defined.
20-
#[allow(dead_code)]
21-
const _: () = {
22-
let _: Default;
23-
};
24-
2539
declare_versions! {
2640
[pub] v0, V0;
2741
v1, V1;

opslang-parser/src/version/v0.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
use opslang_ast::v0::Row;
22

3-
use crate::ParseOps;
3+
use crate::{ParseOps, Versioned};
44

55
type This = super::V0;
66

7-
impl<'a> ParseOps<&'a str, This> for opslang_ast::v0::SRow {
7+
pub type AssumeV0Format<T> = Versioned<This, T>;
8+
9+
impl<'a> ParseOps<AssumeV0Format<&'a str>, This> for opslang_ast::v0::SRow {
810
type Context = ();
911

1012
type Error = peg::error::ParseError<<str as peg::Parse>::PositionRepr>;
1113

12-
fn parse(from: &'a str, _context: Self::Context) -> Result<Self, Self::Error> {
13-
ops_parser::row(from)
14+
fn parse(from: AssumeV0Format<&'a str>, _context: Self::Context) -> Result<Self, Self::Error> {
15+
ops_parser::row(from.0)
1416
}
1517
}
1618

17-
impl<'a> ParseOps<&'a str, This> for Vec<opslang_ast::v0::Statement> {
19+
impl<'a> ParseOps<AssumeV0Format<&'a str>, This> for Vec<opslang_ast::v0::Statement> {
1820
type Context = ();
1921

2022
type Error = peg::error::ParseError<<str as peg::Parse>::PositionRepr>;
2123

22-
fn parse(from: &'a str, _context: Self::Context) -> Result<Self, Self::Error> {
23-
ops_parser::statements(from)
24+
fn parse(from: AssumeV0Format<&'a str>, _context: Self::Context) -> Result<Self, Self::Error> {
25+
ops_parser::statements(from.0)
2426
}
2527
}
2628

opslang-parser/src/version/v1.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,23 @@ pub use context::*;
66

77
crate::redirect_parol!();
88

9-
use crate::ParseOps;
9+
use crate::{ParseOps, ParserInput, Versioned};
1010

1111
type This = super::V1;
1212

13-
pub struct ParserInput<'a> {
14-
pub string: &'a str,
15-
pub file_name: String,
16-
}
13+
pub type AssumeV1Format<T> = Versioned<This, T>;
1714

18-
impl<'a, 'cx> ParseOps<ParserInput<'a>, This> for opslang_ast::v1::Program<'cx> {
15+
impl<'cx> ParseOps<AssumeV1Format<ParserInput<'cx>>, This> for opslang_ast::v1::Program<'cx> {
1916
type Context = &'cx ParseContext<'cx>;
2017

2118
type Error = parol_runtime::ParolError;
2219

2320
fn parse(
24-
ParserInput { string, file_name }: ParserInput<'a>,
21+
Versioned(ParserInput { content, file_name }, _): AssumeV1Format<ParserInput<'cx>>,
2522
context: Self::Context,
2623
) -> Result<Self, Self::Error> {
2724
let mut action = parse::Action::new(context);
28-
let string = context.alloc_str(string);
29-
let _ = generated::parser::parse(string, file_name, &mut action)?;
25+
let _ = generated::parser::parse(content, file_name, &mut action)?;
3026
Ok(action.finish())
3127
}
3228
}
@@ -38,14 +34,13 @@ mod tests {
3834
#[test]
3935
fn test_parse_file() {
4036
let input_str = include_str!("../../tests/test_v1.ops");
37+
let input = ParserInput {
38+
content: input_str,
39+
file_name: "test_v1.ops".into(),
40+
}
41+
.assume_inferred();
4142
let context = ParseContext::new();
42-
let result = opslang_ast::v1::Program::parse(
43-
ParserInput {
44-
string: input_str,
45-
file_name: "test_v1.ops".to_string(),
46-
},
47-
&context,
48-
);
43+
let result = opslang_ast::v1::Program::parse(input, &context);
4944
assert!(
5045
result.is_ok(),
5146
"Failed to parse test_v1.ops: {:?}",

0 commit comments

Comments
 (0)