Last active
September 16, 2023 15:04
-
-
Save frozolotl/a527ecd9716fe9eb84e0f66b4dcfbc86 to your computer and use it in GitHub Desktop.
An incomplete implementation of JSONPath in Typst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#let data = ( | |
books: ( | |
(title: "The Final Empire", author: "Brandon Sanderson"), | |
(title: "The Sword of Kaigen", author: "M.L. Wang"), | |
), | |
query: 0, | |
) | |
#faux-jsonpath(data, "$.books[0].author") | |
#faux-jsonpath(data, "$.books[$.query].title") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// A limited version of JSONPath. | |
/// It supports the following syntaxes: | |
/// - `$`: The root object | |
/// - `@`: The current object | |
/// - `.field`: Field access | |
/// - `[0]`: Index by number | |
/// - `['foo']`: Index by string | |
#let faux-jsonpath(obj, path) = { | |
let expr(root, obj, i, path) = { | |
let current = obj | |
let i = i | |
while i < path.len() { | |
let c = path.at(i) | |
if c == "$" { | |
// The root object. | |
current = root | |
i += 1 | |
} else if c == "@" { | |
// The current object. | |
current = obj | |
i += 1 | |
} else if c == "." { | |
// Parse identifier. | |
i += 1 | |
let match = path.slice(i).match(regex("\p{XID_Start}\p{XID_Continue}*")) | |
if match == none { | |
panic("syntax error: invalid field access syntax") | |
} | |
current = current.at(match.text) | |
i += match.end | |
} else if c == "[" { | |
i += 1 | |
let (value, i_) = expr(root, current, i, path) | |
current = current.at(value) | |
i = i_ | |
if path.at(i) != "]" { | |
panic("syntax error: missing closing bracket `]`") | |
} | |
i += 1 | |
} else if c == "'" { | |
// Parse string. | |
i += 1 | |
let start = i | |
while i < path.len() and path.at(i) != "'" { | |
i += 1 | |
} | |
if i == path.len() { | |
panic("syntax error: missing closing mark `'`") | |
} | |
current = path.slice(start, i) | |
i += 1 | |
break | |
} else { | |
// Parse integer. | |
let match = path.slice(i).match(regex("[0-9]+")) | |
if match != none { | |
current = int(match.text) | |
i += match.end | |
} | |
break | |
} | |
} | |
(current, i) | |
} | |
let (current, i) = expr(obj, obj, 0, path) | |
if i != path.len() { | |
panic("syntax error: unexpected character `" + c + "`") | |
} | |
current | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment