Skip to content

Commit a3e7396

Browse files
committed
Markdown stuff
1 parent 95f4593 commit a3e7396

File tree

9 files changed

+193
-28
lines changed

9 files changed

+193
-28
lines changed

internal/compiler/builtin_macros.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ pub fn lower_macro(
8787
}
8888
BuiltinMacroFunction::Rgb => rgb_macro(n, sub_expr.collect(), diag),
8989
BuiltinMacroFunction::Hsv => hsv_macro(n, sub_expr.collect(), diag),
90-
BuiltinMacroFunction::Markdown => markdown_macro(n, sub_expr.collect()),
9190
}
9291
}
9392

@@ -320,30 +319,6 @@ fn debug_macro(
320319
}
321320
}
322321

323-
fn markdown_macro(node: &dyn Spanned, args: Vec<(Expression, Option<NodeOrToken>)>) -> Expression {
324-
let mut string = None;
325-
for (expr, node) in args {
326-
let escaped = Expression::FunctionCall {
327-
function: BuiltinFunction::EscapeMarkdown.into(),
328-
arguments: vec![expr],
329-
source_location: Some(node.to_source_location()),
330-
};
331-
string = Some(match string {
332-
None => escaped,
333-
Some(string) => Expression::BinaryExpression {
334-
lhs: Box::new(string),
335-
op: '+',
336-
rhs: Box::new(escaped),
337-
},
338-
});
339-
}
340-
Expression::FunctionCall {
341-
function: BuiltinFunction::ParseMarkdown.into(),
342-
arguments: vec![string.unwrap_or_else(|| Expression::default_value_for_type(&Type::String))],
343-
source_location: Some(node.to_source_location()),
344-
}
345-
}
346-
347322
fn to_debug_string(
348323
expr: Expression,
349324
node: &dyn Spanned,

internal/compiler/expression_tree.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,6 @@ pub enum BuiltinMacroFunction {
144144
Hsv,
145145
/// transform `debug(a, b, c)` into debug `a + " " + b + " " + c`
146146
Debug,
147-
/// Markdown
148-
Markdown,
149147
}
150148

151149
macro_rules! declare_builtin_function_types {
@@ -279,7 +277,7 @@ declare_builtin_function_types!(
279277
RestartTimer: (Type::ElementReference) -> Type::Void,
280278
OpenUrl: (Type::String) -> Type::Void,
281279
EscapeMarkdown: (Type::String) -> Type::String,
282-
ParseMarkdown: (Type::String) -> Type::Void
280+
ParseMarkdown: (Type::String) -> Type::String
283281
);
284282

285283
impl BuiltinFunction {

internal/compiler/generator/cpp.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4213,6 +4213,14 @@ fn compile_builtin_function_call(
42134213
let url = a.next().unwrap();
42144214
format!("slint::cbindgen_private::open_url({})", url)
42154215
}
4216+
BuiltinFunction::EscapeMarkdown => {
4217+
let text = a.next().unwrap();
4218+
format!("slint::cbindgen_private::escape_markdown({})", text)
4219+
}
4220+
BuiltinFunction::ParseMarkdown => {
4221+
let text = a.next().unwrap();
4222+
format!("slint::cbindgen_private::parse_markdown({})", text)
4223+
}
42164224
}
42174225
}
42184226

internal/compiler/generator/rust.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,6 +3383,14 @@ fn compile_builtin_function_call(
33833383
let url = a.next().unwrap();
33843384
quote!(sp::open_url(&#url))
33853385
}
3386+
BuiltinFunction::EscapeMarkdown => {
3387+
let text = a.next().unwrap();
3388+
quote!(sp::escape_markdown(&#text))
3389+
}
3390+
BuiltinFunction::ParseMarkdown => {
3391+
let text = a.next().unwrap();
3392+
quote!(sp::parse_markdown(&#text))
3393+
}
33863394
}
33873395
}
33883396

internal/compiler/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ declare_syntax! {
383383
AtGradient -> [*Expression],
384384
/// `@tr("foo", ...)` // the string is a StringLiteral
385385
AtTr -> [?TrContext, ?TrPlural, *Expression],
386+
AtMarkdown -> [*Expression],
386387
/// `"foo" =>` in a `AtTr` node
387388
TrContext -> [],
388389
/// `| "foo" % n` in a `AtTr` node

internal/compiler/parser/expressions.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ fn parse_at_keyword(p: &mut impl Parser) {
255255
"tr" => {
256256
parse_tr(p);
257257
}
258+
"markdown" => {
259+
parse_markdown(p);
260+
},
258261
_ => {
259262
p.consume();
260263
p.test(SyntaxKind::Identifier); // consume the identifier, so that autocomplete works
@@ -438,6 +441,37 @@ fn parse_tr(p: &mut impl Parser) {
438441
p.expect(SyntaxKind::RParent);
439442
}
440443

444+
fn parse_markdown(p: &mut impl Parser) {
445+
let mut p = p.start_node(SyntaxKind::AtMarkdown);
446+
p.expect(SyntaxKind::At);
447+
debug_assert!(p.peek().as_str().ends_with("markdown"));
448+
p.expect(SyntaxKind::Identifier); //eg "markdown"
449+
p.expect(SyntaxKind::LParent);
450+
451+
fn consume_literal(p: &mut impl Parser) -> bool {
452+
let peek = p.peek();
453+
if peek.kind() != SyntaxKind::StringLiteral
454+
|| !peek.as_str().starts_with('"')
455+
|| !peek.as_str().ends_with('"')
456+
{
457+
p.error("Expected plain string literal");
458+
return false;
459+
}
460+
p.expect(SyntaxKind::StringLiteral)
461+
}
462+
463+
if !consume_literal(&mut *p) {
464+
return;
465+
}
466+
467+
while p.test(SyntaxKind::Comma) {
468+
if !parse_expression(&mut *p) {
469+
break;
470+
}
471+
}
472+
p.expect(SyntaxKind::RParent);
473+
}
474+
441475
#[cfg_attr(test, parser_test)]
442476
/// ```test,AtImageUrl
443477
/// @image-url("foo.png")

internal/compiler/passes/resolving.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ impl Expression {
359359
SyntaxKind::AtImageUrl => Some(Self::from_at_image_url_node(node.into(), ctx)),
360360
SyntaxKind::AtGradient => Some(Self::from_at_gradient(node.into(), ctx)),
361361
SyntaxKind::AtTr => Some(Self::from_at_tr(node.into(), ctx)),
362+
SyntaxKind::AtMarkdown => Some(Self::from_at_markdown(node.into(), ctx)),
362363
SyntaxKind::QualifiedName => Some(Self::from_qualified_name_node(
363364
node.clone().into(),
364365
ctx,
@@ -744,6 +745,126 @@ impl Expression {
744745
}
745746
}
746747
}
748+
749+
fn from_at_markdown(node: syntax_nodes::AtMarkdown, ctx: &mut LookupCtx) -> Expression {
750+
let Some(string) = std::dbg!(node
751+
.child_text(SyntaxKind::StringLiteral))
752+
.and_then(|s| crate::literals::unescape_string(&s))
753+
else {
754+
ctx.diag.push_error("Cannot parse string literal".into(), &node);
755+
return Expression::Invalid;
756+
};
757+
758+
let subs = node.Expression().map(|n| {
759+
Expression::from_expression_node(n.clone(), ctx).maybe_convert_to(
760+
Type::String,
761+
&n,
762+
ctx.diag,
763+
)
764+
});
765+
let values = subs.collect::<Vec<_>>();
766+
std::dbg!(&values);
767+
768+
let mut expr = None;
769+
770+
// check format string
771+
{
772+
let mut arg_idx = 0;
773+
let mut pos_max = 0;
774+
let mut pos = 0;
775+
let mut literal_start_pos = 0;
776+
while let Some(mut p) = string[pos..].find(['{', '}']) {
777+
if string.len() - pos < p + 1 {
778+
ctx.diag.push_error(
779+
"Unescaped trailing '{' in format string. Escape '{' with '{{'".into(),
780+
&node,
781+
);
782+
break;
783+
}
784+
p += pos;
785+
786+
// Skip escaped }
787+
if string.get(p..=p) == Some("}") {
788+
if string.get(p + 1..=p + 1) == Some("}") {
789+
pos = p + 2;
790+
continue;
791+
} else {
792+
ctx.diag.push_error(
793+
"Unescaped '}' in format string. Escape '}' with '}}'".into(),
794+
&node,
795+
);
796+
break;
797+
}
798+
}
799+
800+
// Skip escaped {
801+
if string.get(p + 1..=p + 1) == Some("{") {
802+
pos = p + 2;
803+
continue;
804+
}
805+
806+
// Find the argument
807+
let end = if let Some(end) = string[p..].find('}') {
808+
end + p
809+
} else {
810+
ctx.diag.push_error(
811+
"Unterminated placeholder in format string. '{' must be escaped with '{{'"
812+
.into(),
813+
&node,
814+
);
815+
break;
816+
};
817+
let argument = &string[p + 1..end];
818+
if argument.is_empty() {
819+
arg_idx += 1;
820+
} else if let Ok(n) = argument.parse::<u16>() {
821+
pos_max = pos_max.max(n as usize + 1);
822+
} else {
823+
ctx.diag
824+
.push_error("Invalid '{...}' placeholder in format string. The placeholder must be a number, or braces must be escaped with '{{' and '}}'".into(), &node);
825+
break;
826+
};
827+
let add = Expression::BinaryExpression {
828+
lhs: Box::new(Expression::StringLiteral((&string[literal_start_pos..p]).into())),
829+
op: '+',
830+
rhs: Box::new(values[arg_idx-1].clone())
831+
};
832+
expr = Some(match expr {
833+
None => add,
834+
Some(expr) => Expression::BinaryExpression {
835+
lhs: Box::new(expr),
836+
op: '+',
837+
rhs: Box::new(add),
838+
},
839+
});
840+
pos = end + 1;
841+
literal_start_pos = pos;
842+
}
843+
if arg_idx > 0 && pos_max > 0 {
844+
ctx.diag.push_error(
845+
"Cannot mix positional and non-positional placeholder in format string".into(),
846+
&node,
847+
);
848+
} else if arg_idx > values.len() || pos_max > values.len() {
849+
let num = arg_idx.max(pos_max);
850+
ctx.diag.push_error(
851+
format!("Format string contains {num} placeholders, but only {} extra arguments were given", values.len()),
852+
&node,
853+
);
854+
}
855+
}
856+
857+
std::dbg!(&expr);
858+
859+
Expression::FunctionCall {
860+
function: BuiltinFunction::ParseMarkdown.into(),
861+
arguments: vec![
862+
expr.unwrap()//Expression::StringLiteral(string),
863+
864+
],
865+
source_location: Some(node.to_source_location()),
866+
}
867+
}
747868

748869
fn from_at_tr(node: syntax_nodes::AtTr, ctx: &mut LookupCtx) -> Expression {
749870
let Some(string) = node

internal/core/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,13 @@ pub fn open_url(url: &str) {
172172
debug_log!("Error opening url {}: {}", url, err);
173173
}
174174
}
175+
176+
pub fn escape_markdown(text: &str) -> std::string::String {
177+
std::dbg!(text);
178+
text.into()
179+
}
180+
181+
pub fn parse_markdown(text: &str) -> std::string::String {
182+
std::dbg!(text);
183+
text.into()
184+
}

internal/interpreter/eval.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,16 @@ fn call_builtin_function(
14171417
corelib::open_url(&url);
14181418
Value::Void
14191419
}
1420+
BuiltinFunction::EscapeMarkdown => {
1421+
let text: SharedString =
1422+
eval_expression(&arguments[0], local_context).try_into().unwrap();
1423+
Value::String(corelib::escape_markdown(&text).into())
1424+
}
1425+
BuiltinFunction::ParseMarkdown => {
1426+
let text: SharedString =
1427+
eval_expression(&arguments[0], local_context).try_into().unwrap();
1428+
Value::String(corelib::parse_markdown(&text).into())
1429+
}
14201430
}
14211431
}
14221432

0 commit comments

Comments
 (0)