Parse @image-url

This commit is contained in:
Olivier Goffart 2021-01-28 11:23:43 +01:00
parent 48b226c017
commit 5debc08f98
4 changed files with 68 additions and 12 deletions

View file

@ -197,6 +197,7 @@ fn fill_token_vec(stream: impl Iterator<Item = TokenTree>, vec: &mut Vec<parser:
SyntaxKind::Error SyntaxKind::Error
} }
'$' => SyntaxKind::Dollar, '$' => SyntaxKind::Dollar,
'@' => SyntaxKind::At,
_ => SyntaxKind::Error, _ => SyntaxKind::Error,
}; };
prev_spacing = p.spacing(); prev_spacing = p.spacing();

View file

@ -272,6 +272,7 @@ declare_syntax! {
Dot -> ".", Dot -> ".",
Question -> "?", Question -> "?",
Dollar -> "$", Dollar -> "$",
At -> "@",
} }
// syntax kind // syntax kind
{ {
@ -309,11 +310,13 @@ declare_syntax! {
// FIXME: the test should test that as alternative rather than several of them (but it can also be a literal) // FIXME: the test should test that as alternative rather than several of them (but it can also be a literal)
Expression-> [ ?Expression, ?BangExpression, ?FunctionCallExpression, ?SelfAssignment, Expression-> [ ?Expression, ?BangExpression, ?FunctionCallExpression, ?SelfAssignment,
?ConditionalExpression, ?QualifiedName, ?BinaryExpression, ?Array, ?ObjectLiteral, ?ConditionalExpression, ?QualifiedName, ?BinaryExpression, ?Array, ?ObjectLiteral,
?UnaryOpExpression, ?CodeBlock, ?StringTemplate], ?UnaryOpExpression, ?CodeBlock, ?StringTemplate, ?AtImageUrl],
/// Concetenate the Expressions to make a string (usually expended from a template string) /// Concetenate the Expressions to make a string (usually expended from a template string)
StringTemplate -> [*Expression], StringTemplate -> [*Expression],
/// `foo!bar` /// `foo!bar`
BangExpression -> [Expression], BangExpression -> [Expression],
/// `@image-url("foo.png")`
AtImageUrl -> [],
/// expression() /// expression()
FunctionCallExpression -> [*Expression], FunctionCallExpression -> [*Expression],
/// `expression += expression` /// `expression += expression`

View file

@ -19,7 +19,7 @@ use super::prelude::*;
/// 42px /// 42px
/// #aabbcc /// #aabbcc
/// (something) /// (something)
/// img!"something" /// @image-url("something")
/// some_id.some_property /// some_id.some_property
/// function_call() /// function_call()
/// function_call(hello, world) /// function_call(hello, world)
@ -81,20 +81,13 @@ fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence)
} }
SyntaxKind::LBracket => parse_array(&mut *p), SyntaxKind::LBracket => parse_array(&mut *p),
SyntaxKind::LBrace => parse_object_notation(&mut *p), SyntaxKind::LBrace => parse_object_notation(&mut *p),
SyntaxKind::Plus => { SyntaxKind::Plus | SyntaxKind::Minus | SyntaxKind::Bang => {
let mut p = p.start_node(SyntaxKind::UnaryOpExpression); let mut p = p.start_node(SyntaxKind::UnaryOpExpression);
p.consume(); p.consume();
parse_expression_helper(&mut *p, OperatorPrecedence::Unary); parse_expression_helper(&mut *p, OperatorPrecedence::Unary);
} }
SyntaxKind::Minus => { SyntaxKind::At => {
let mut p = p.start_node(SyntaxKind::UnaryOpExpression); parse_at_keyword(&mut *p);
p.consume();
parse_expression_helper(&mut *p, OperatorPrecedence::Unary);
}
SyntaxKind::Bang => {
let mut p = p.start_node(SyntaxKind::UnaryOpExpression);
p.consume();
parse_expression_helper(&mut *p, OperatorPrecedence::Unary);
} }
_ => { _ => {
p.error("invalid expression"); p.error("invalid expression");
@ -196,6 +189,27 @@ fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence)
} }
} }
#[cfg_attr(test, parser_test)]
/// ```test
/// @image-url("/foo/bar.png")
/// ```
fn parse_at_keyword(p: &mut impl Parser) {
let checkpoint = p.checkpoint();
p.expect(SyntaxKind::At);
match p.peek().as_str() {
"image-url" | "image_url" => {
let mut p = p.start_node_at(checkpoint, SyntaxKind::AtImageUrl);
p.consume(); // "image-url"
p.expect(SyntaxKind::LParent);
p.expect(SyntaxKind::StringLiteral);
p.expect(SyntaxKind::RParent);
}
_ => {
p.error("Expected 'image-url' after '@'");
}
}
}
#[cfg_attr(test, parser_test)] #[cfg_attr(test, parser_test)]
/// ```test,BangExpression /// ```test,BangExpression
/// foo!bar /// foo!bar

View file

@ -370,6 +370,7 @@ impl Expression {
.or_else(|| { .or_else(|| {
node.BangExpression().map(|n| Self::from_bang_expression_node(n.into(), ctx)) node.BangExpression().map(|n| Self::from_bang_expression_node(n.into(), ctx))
}) })
.or_else(|| node.AtImageUrl().map(|n| Self::from_at_image_url_node(n, ctx)))
.or_else(|| node.QualifiedName().map(|s| Self::from_qualified_name_node(s.into(), ctx))) .or_else(|| node.QualifiedName().map(|s| Self::from_qualified_name_node(s.into(), ctx)))
.or_else(|| { .or_else(|| {
node.child_text(SyntaxKind::StringLiteral).map(|s| { node.child_text(SyntaxKind::StringLiteral).map(|s| {
@ -473,6 +474,43 @@ impl Expression {
} }
} }
fn from_at_image_url_node(node: syntax_nodes::AtImageUrl, ctx: &mut LookupCtx) -> Self {
let s = match node.child_text(SyntaxKind::StringLiteral).and_then(|x| unescape_string(&x)) {
Some(s) => s,
None => {
ctx.diag.push_error("Cannot parse string literal".into(), &node);
return Self::Invalid;
}
};
let absolute_source_path = {
let path = std::path::Path::new(&s);
if path.is_absolute() || s.starts_with("http://") || s.starts_with("https://") {
s
} else {
ctx.type_loader
.and_then(|loader| {
loader
.import_file(
node.source_file.as_ref().map(|path_rc| path_rc.as_path()),
&s,
)
.map(|resolved_file| resolved_file.path)
})
.unwrap_or_else(|| {
std::env::current_dir()
.map(|b| b.join(&path))
.unwrap_or_else(|_| path.into())
})
.to_string_lossy()
.to_string()
}
};
Expression::ResourceReference(ResourceReference::AbsolutePath(absolute_source_path))
}
/// Perform the lookup /// Perform the lookup
fn from_qualified_name_node(node: SyntaxNodeWithSourceFile, ctx: &mut LookupCtx) -> Self { fn from_qualified_name_node(node: SyntaxNodeWithSourceFile, ctx: &mut LookupCtx) -> Self {
debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName); debug_assert_eq!(node.kind(), SyntaxKind::QualifiedName);