ambiguous keyword operators are not treated as function calls
This commit is contained in:
parent
00a7659e69
commit
48de92fdc0
@ -232,6 +232,65 @@ impl<'a> Parser<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_fn_call(
|
||||||
|
&mut self,
|
||||||
|
mut s: String,
|
||||||
|
lower: String,
|
||||||
|
) -> SassResult<Spanned<IntermediateValue>> {
|
||||||
|
if lower == "min" || lower == "max" {
|
||||||
|
match self.try_parse_min_max(&lower, true)? {
|
||||||
|
Some(val) => {
|
||||||
|
self.toks.truncate_iterator_to_cursor();
|
||||||
|
return Ok(IntermediateValue::Value(HigherIntermediateValue::Literal(
|
||||||
|
Value::String(val, QuoteKind::None),
|
||||||
|
))
|
||||||
|
.span(self.span_before));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.toks.reset_cursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let as_ident = Identifier::from(&s);
|
||||||
|
let func = match self.scopes.get_fn(as_ident, self.global_scope) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => {
|
||||||
|
if let Some(f) = GLOBAL_FUNCTIONS.get(as_ident.as_str()) {
|
||||||
|
return Ok(IntermediateValue::Value(HigherIntermediateValue::Function(
|
||||||
|
SassFunction::Builtin(f.clone(), as_ident),
|
||||||
|
self.parse_call_args()?,
|
||||||
|
))
|
||||||
|
.span(self.span_before));
|
||||||
|
} else {
|
||||||
|
// check for special cased CSS functions
|
||||||
|
match unvendor(&lower) {
|
||||||
|
"calc" | "element" | "expression" => {
|
||||||
|
s = lower;
|
||||||
|
self.parse_calc_args(&mut s)?;
|
||||||
|
}
|
||||||
|
"url" => match self.try_parse_url()? {
|
||||||
|
Some(val) => s = val,
|
||||||
|
None => s.push_str(&self.parse_call_args()?.to_css_string()?),
|
||||||
|
},
|
||||||
|
_ => s.push_str(&self.parse_call_args()?.to_css_string()?),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(IntermediateValue::Value(HigherIntermediateValue::Literal(
|
||||||
|
Value::String(s, QuoteKind::None),
|
||||||
|
))
|
||||||
|
.span(self.span_before));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let call_args = self.parse_call_args()?;
|
||||||
|
Ok(
|
||||||
|
IntermediateValue::Value(HigherIntermediateValue::Function(func, call_args))
|
||||||
|
.span(self.span_before),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_ident_value(
|
fn parse_ident_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
predicate: &dyn Fn(&mut PeekMoreIterator<IntoIter<Token>>) -> bool,
|
predicate: &dyn Fn(&mut PeekMoreIterator<IntoIter<Token>>) -> bool,
|
||||||
@ -255,72 +314,22 @@ impl<'a> Parser<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.toks.peek() {
|
if !is_keyword_operator(&s) {
|
||||||
Some(Token { kind: '(', .. }) => {
|
match self.toks.peek() {
|
||||||
self.toks.next();
|
Some(Token { kind: '(', .. }) => {
|
||||||
|
self.span_before = span;
|
||||||
if lower == "min" || lower == "max" {
|
|
||||||
match self.try_parse_min_max(&lower, true)? {
|
|
||||||
Some(val) => {
|
|
||||||
self.toks.truncate_iterator_to_cursor();
|
|
||||||
return Ok(IntermediateValue::Value(HigherIntermediateValue::Literal(
|
|
||||||
Value::String(val, QuoteKind::None),
|
|
||||||
))
|
|
||||||
.span(span));
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.toks.reset_cursor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let as_ident = Identifier::from(&s);
|
|
||||||
let func = match self.scopes.get_fn(as_ident, self.global_scope) {
|
|
||||||
Some(f) => f,
|
|
||||||
None => {
|
|
||||||
if let Some(f) = GLOBAL_FUNCTIONS.get(as_ident.as_str()) {
|
|
||||||
return Ok(IntermediateValue::Value(
|
|
||||||
HigherIntermediateValue::Function(
|
|
||||||
SassFunction::Builtin(f.clone(), as_ident),
|
|
||||||
self.parse_call_args()?,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.span(span));
|
|
||||||
} else {
|
|
||||||
// check for special cased CSS functions
|
|
||||||
match unvendor(&lower) {
|
|
||||||
"calc" | "element" | "expression" => {
|
|
||||||
s = lower;
|
|
||||||
self.parse_calc_args(&mut s)?;
|
|
||||||
}
|
|
||||||
"url" => match self.try_parse_url()? {
|
|
||||||
Some(val) => s = val,
|
|
||||||
None => s.push_str(&self.parse_call_args()?.to_css_string()?),
|
|
||||||
},
|
|
||||||
_ => s.push_str(&self.parse_call_args()?.to_css_string()?),
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(IntermediateValue::Value(HigherIntermediateValue::Literal(
|
|
||||||
Value::String(s, QuoteKind::None),
|
|
||||||
))
|
|
||||||
.span(span));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let call_args = self.parse_call_args()?;
|
|
||||||
return Ok(IntermediateValue::Value(HigherIntermediateValue::Function(
|
|
||||||
func, call_args,
|
|
||||||
))
|
|
||||||
.span(span));
|
|
||||||
}
|
|
||||||
Some(Token { kind: '.', .. }) => {
|
|
||||||
if !predicate(self.toks) {
|
|
||||||
self.toks.next();
|
self.toks.next();
|
||||||
return self.parse_module_item(&s, span);
|
|
||||||
|
return self.parse_fn_call(s, lower);
|
||||||
}
|
}
|
||||||
|
Some(Token { kind: '.', .. }) => {
|
||||||
|
if !predicate(self.toks) {
|
||||||
|
self.toks.next();
|
||||||
|
return self.parse_module_item(&s, span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for named colors
|
// check for named colors
|
||||||
@ -1356,3 +1365,7 @@ fn parse_i64(s: &str) -> i64 {
|
|||||||
.iter()
|
.iter()
|
||||||
.fold(0, |total, this| total * 10 + i64::from(this - b'0'))
|
.fold(0, |total, this| total * 10 + i64::from(this - b'0'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_keyword_operator(s: &str) -> bool {
|
||||||
|
matches!(s, "and" | "or" | "not")
|
||||||
|
}
|
||||||
|
@ -41,3 +41,45 @@ test!(
|
|||||||
"a {\n $primary: #f2ece4;\n $accent: #e1d7d2;\n color: radial-gradient($primary, $accent);\n}\n",
|
"a {\n $primary: #f2ece4;\n $accent: #e1d7d2;\n color: radial-gradient($primary, $accent);\n}\n",
|
||||||
"a {\n color: radial-gradient(#f2ece4, #e1d7d2);\n}\n"
|
"a {\n color: radial-gradient(#f2ece4, #e1d7d2);\n}\n"
|
||||||
);
|
);
|
||||||
|
test!(
|
||||||
|
fn_named_not_is_evaluated_as_unary_op,
|
||||||
|
"a {\n color: not(true);\n}\n",
|
||||||
|
"a {\n color: false;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
fn_named_true_is_plain_css,
|
||||||
|
"a {\n color: true(true);\n}\n",
|
||||||
|
"a {\n color: true(true);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
fn_named_false_is_plain_css,
|
||||||
|
"a {\n color: false(true);\n}\n",
|
||||||
|
"a {\n color: false(true);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
fn_named_null_is_plain_css,
|
||||||
|
"a {\n color: null(true);\n}\n",
|
||||||
|
"a {\n color: null(true);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
fn_named_and_is_evaluated_as_binop,
|
||||||
|
"a {\n color: true and(foo);\n}\n",
|
||||||
|
"a {\n color: foo;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
fn_named_or_is_evaluated_as_binop,
|
||||||
|
"a {\n color: true or(foo);\n}\n",
|
||||||
|
"a {\n color: true;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
#[ignore = "this is not currently parsed correctly"]
|
||||||
|
fn_named_and_alone_is_not_evaluated_as_binop,
|
||||||
|
"a {\n color: and(foo);\n}\n",
|
||||||
|
"a {\n color: and(foo);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
#[ignore = "this is not currently parsed correctly"]
|
||||||
|
fn_named_or_alone_is_not_evaluated_as_binop,
|
||||||
|
"a {\n color: or(foo);\n}\n",
|
||||||
|
"a {\n color: or(foo);\n}\n"
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user