Skip to content

Commit

Permalink
Parse user-defined operators
Browse files Browse the repository at this point in the history
+ Make sure var modifier parser doesn't dig into the var name
  • Loading branch information
werediver committed Sep 4, 2023
1 parent ae3c4d3 commit e3352f8
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 5 deletions.
41 changes: 41 additions & 0 deletions dart-parser/src/dart/func_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use super::{ty::Type, Expr, MaybeRequired, TypeParam, WithMeta};
#[derive(PartialEq, Eq, Debug)]
pub enum FuncLike<'s> {
Func(Func<'s>),
Operator(Operator<'s>),
Getter(Getter<'s>),
Setter(Setter<'s>),
}
Expand All @@ -19,6 +20,46 @@ pub struct Func<'s> {
pub body: Option<FuncBody<'s>>,
}

#[derive(PartialEq, Eq, Debug)]
pub struct Operator<'s> {
pub modifiers: FuncModifierSet,
pub return_type: Type<'s>,
pub operator_type: UserDefOperator,
pub type_params: Vec<TypeParam<'s>>,
pub params: FuncParams<'s, FuncParam<'s>>,
pub body: Option<FuncBody<'s>>,
}

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum UserDefOperator {
Lt,
Lte,
Gt,
Gte,
/// Could mean negation or subtraction, depending on the signature.
Minus,
Add,
Div,
DivInt,
Mul,
Mod,
Pipe,
Caret,
Amp,
/// `<<`
LShift,
/// `>>`
RShift,
/// `>>>`
RShiftTri,
/// `[]`
IndexGet,
/// `[]=`
IndexSet,
Tilde,
Eq,
}

#[derive(PartialEq, Eq, Debug)]
pub struct Getter<'s> {
pub modifiers: FuncModifierSet,
Expand Down
5 changes: 5 additions & 0 deletions dart-parser/src/parser/common.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use nom::{
branch::alt,
bytes::complete::{is_a, tag},
character::complete::one_of,
combinator::recognize,
error::{ContextError, ParseError},
multi::{fold_many0, fold_many1},
Expand Down Expand Up @@ -122,6 +123,10 @@ pub fn spbr<'s, E: ParseError<&'s str>>(s: &'s str) -> PResult<&str, E> {
is_a(" \t\r\n")(s)
}

pub fn spbr_char<'s, E: ParseError<&'s str>>(s: &'s str) -> PResult<char, E> {
one_of(" \t\r\n")(s)
}

/// Parse exactly one line break.
pub fn br<'s, E: ParseError<&'s str>>(s: &'s str) -> PResult<&str, E> {
alt((tag("\n"), tag("\r\n"), tag("\r")))(s)
Expand Down
10 changes: 9 additions & 1 deletion dart-parser/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,21 @@ mod tests {
}

#[test]
fn expr_list_test() {
fn expr_typed_list_test() {
assert_eq!(
expr::<VerboseError<_>>("<String>['asdf', 'jkl;']; "),
Ok(("; ", Expr::Verbatim("<String>['asdf', 'jkl;']")))
);
}

#[test]
fn expr_list_test() {
assert_eq!(
expr::<VerboseError<_>>("[\n'asdf',\n'jkl;'\n]; "),
Ok(("; ", Expr::Verbatim("[\n'asdf',\n'jkl;'\n]")))
);
}

#[test]
fn expr_set_test() {
assert_eq!(
Expand Down
80 changes: 79 additions & 1 deletion dart-parser/src/parser/func_like.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::char,
combinator::{cut, opt, success, value},
error::{context, ContextError, ParseError},
multi::fold_many0,
Expand All @@ -12,7 +13,7 @@ use crate::dart::{
func_like::{
Func, FuncBody, FuncBodyContent, FuncBodyModifier, FuncModifier, FuncModifierSet,
FuncParam, FuncParamModifier, FuncParamModifierSet, FuncParams, FuncParamsExtra, Getter,
Setter,
Operator, Setter, UserDefOperator,
},
FuncLike, MaybeRequired, WithMeta,
};
Expand All @@ -35,6 +36,7 @@ where
context(
"func_like",
alt((
operator.map(FuncLike::Operator),
func.map(FuncLike::Func),
getter.map(FuncLike::Getter),
setter.map(FuncLike::Setter),
Expand Down Expand Up @@ -76,6 +78,42 @@ where
.parse(s)
}

fn operator<'s, E>(s: &'s str) -> PResult<Operator, E>
where
E: ParseError<&'s str> + ContextError<&'s str>,
{
context(
"operator",
tuple((
alt((
terminated(func_modifier_set, spbr),
success(FuncModifierSet::default()),
)),
// Return type
terminated(ty, opt(spbr)),
// Function name
preceded(
pair(tag("operator"), opt(spbr)),
terminated(user_def_operator, opt(spbr)),
),
opt(terminated(type_params, opt(spbr))),
terminated(func_params, opt(spbr)),
alt((func_body.map(Some), tag(";").map(|_| None))),
))
.map(
|(modifiers, return_type, operator_type, type_params, params, body)| Operator {
modifiers,
return_type,
operator_type,
type_params: type_params.unwrap_or(Vec::new()),
params,
body,
},
),
)
.parse(s)
}

fn getter<'s, E>(s: &'s str) -> PResult<Getter, E>
where
E: ParseError<&'s str> + ContextError<&'s str>,
Expand Down Expand Up @@ -367,6 +405,46 @@ fn func_body_modifier<'s, E: ParseError<&'s str>>(s: &'s str) -> PResult<FuncBod
))(s)
}

pub fn user_def_operator<'s, E>(s: &'s str) -> PResult<UserDefOperator, E>
where
E: ParseError<&'s str> + ContextError<&'s str>,
{
use UserDefOperator as Op;

alt((
preceded(
char('<'),
alt((
value(Op::Lte, char('=')),
value(Op::RShiftTri, tag("<<")),
value(Op::LShift, char('<')),
value(Op::Lt, success(())),
)),
),
preceded(
char('>'),
alt((
value(Op::Gte, char('=')),
value(Op::RShift, char('>')),
value(Op::Gt, success(())),
)),
),
value(Op::Minus, char('-')),
value(Op::Add, char('+')),
value(Op::Div, char('/')),
value(Op::DivInt, tag("~/")),
value(Op::Mul, char('*')),
value(Op::Mod, char('%')),
value(Op::Pipe, char('|')),
value(Op::Caret, char('^')),
value(Op::Amp, char('&')),
value(Op::IndexSet, tag("[]")),
value(Op::IndexGet, tag("[]=")),
value(Op::Tilde, char('~')),
value(Op::Eq, tag("==")),
))(s)
}

#[cfg(test)]
mod tests {
use nom::error::VerboseError;
Expand Down
8 changes: 8 additions & 0 deletions dart-parser/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,14 @@ mod tests {

use super::*;

#[test]
fn identifier_test() {
assert_eq!(
identifier::<VerboseError<_>>("externalSource "),
Ok((" ", "externalSource"))
);
}

#[test]
fn type_args_test() {
assert_eq!(
Expand Down
45 changes: 42 additions & 3 deletions dart-parser/src/parser/var.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use nom::{
branch::alt,
bytes::complete::tag,
combinator::{cut, opt, success, value},
combinator::{cut, opt, peek, success, value},
error::{context, ContextError, ParseError},
multi::fold_many0,
sequence::{pair, preceded, terminated, tuple},
Expand All @@ -14,7 +14,7 @@ use crate::dart::{
};

use super::{
common::spbr,
common::{spbr, spbr_char},
expr::expr,
ty::{identifier, ty},
PResult,
Expand Down Expand Up @@ -64,7 +64,7 @@ fn var_modifier_set<'s, E: ParseError<&'s str>>(s: &'s str) -> PResult<VarModifi
let modifiers = VarModifierSet::from_iter([modifier]);

fold_many0(
preceded(spbr, var_modifier),
preceded(spbr, terminated(var_modifier, peek(spbr_char))),
move || modifiers,
|modifiers, modifier| modifiers.with(modifier),
)(s)
Expand Down Expand Up @@ -109,6 +109,27 @@ mod tests {
);
}

#[test]
fn var_modifier_test() {
const CULPRIT: &str = r#"
const externalSources = [
"https://a.b.c/",
];
"#;
assert_eq!(
var::<VerboseError<_>>(CULPRIT.trim_start()),
Ok((
"\n",
Var {
modifiers: VarModifierSet::from_iter([VarModifier::Const]),
var_type: None,
name: "externalSources",
initializer: Some(Expr::Verbatim("[\n \"https://a.b.c/\",\n]"))
}
))
);
}

#[test]
fn var_init() {
assert_eq!(
Expand All @@ -127,6 +148,24 @@ mod tests {
);
}

#[test]
fn var_const_init() {
assert_eq!(
var::<VerboseError<_>>("const urls = [\n 'https://a.b.c/',\n 'https://b.c.d/',\n]; "),
Ok((
" ",
Var {
modifiers: VarModifierSet::from_iter([VarModifier::Const]),
var_type: None,
name: "urls",
initializer: Some(Expr::Verbatim(
"[\n 'https://a.b.c/',\n 'https://b.c.d/',\n]"
)),
}
))
);
}

#[test]
fn var_list_init() {
assert_eq!(
Expand Down

0 comments on commit e3352f8

Please sign in to comment.