///|
suberror ParseError {
ParseError(String)
} derive(Debug)
///|
priv struct Parser {
input : String
len : Int
mut i : Int
}
///|
fn Parser::new(input : String) -> Parser {
{ input, len: input.length(), i: 0 }
}
///|
fn Parser::peek(self : Parser) -> Char? {
if self.i < self.len {
Some(self.input[self.i].to_int().unsafe_to_char())
} else {
None
}
}
///|
fn Parser::next(self : Parser) -> Char? {
if self.i < self.len {
let c = self.input[self.i].to_int().unsafe_to_char()
self.i = self.i + 1
Some(c)
} else {
None
}
}
///|
fn Parser::skip_whitespace(self : Parser) -> Unit {
while self.i < self.len {
let c = self.input[self.i].to_int().unsafe_to_char()
if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
self.i = self.i + 1
} else {
break
}
}
}
///|
fn Parser::read_ident(self : Parser) -> String {
let start = self.i
if self.i >= self.len {
return ""
}
let c = self.input[self.i].to_int().unsafe_to_char()
if !is_ident_start_char(c) {
return ""
}
self.i = self.i + 1
while self.i < self.len {
let ch = self.input[self.i].to_int().unsafe_to_char()
if is_ident_char(ch) {
self.i = self.i + 1
} else {
break
}
}
self.input[start:self.i].to_owned()
}
///|
fn is_ident_start_char(c : Char) -> Bool {
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}
///|
fn is_ident_char(c : Char) -> Bool {
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '_' ||
c == '-'
}
///|
fn Parser::parse_integer(self : Parser) -> Int raise ParseError {
self.skip_whitespace()
let p = self.peek()
if p == None {
raise ParseError("Expected integer")
}
let mut is_neg = false
let c = p.unwrap()
if c == '-' {
is_neg = true
self.i = self.i + 1
} else if c == '+' {
self.i = self.i + 1
}
let mut val = 0
let mut has_digit = false
while self.i < self.len {
let ch = self.input[self.i].to_int().unsafe_to_char()
if ch >= '0' && ch <= '9' {
val = val * 10 + (ch.to_int() - 48)
has_digit = true
self.i = self.i + 1
} else {
break
}
}
if !has_digit {
raise ParseError("Expected digit in integer")
}
if is_neg {
-val
} else {
val
}
}
///|
fn Parser::parse_string_literal(self : Parser) -> String raise ParseError {
let quote = self.next().unwrap()
let mut s = ""
while self.i < self.len {
let c = self.input[self.i].to_int().unsafe_to_char()
if c == quote {
self.i = self.i + 1
return s
}
if c == '\\' {
self.i = self.i + 1
if self.i >= self.len {
raise ParseError("Unterminated string escape sequence")
}
let esc = self.input[self.i].to_int().unsafe_to_char()
self.i = self.i + 1
match esc {
'n' => s = s + "\n"
't' => s = s + "\t"
'r' => s = s + "\r"
'b' => s = s + "\u0008"
'f' => s = s + "\u000c"
'/' => s = s + "/"
'\\' => s = s + "\\"
'\'' => s = s + "'"
'"' => s = s + "\""
'u' => {
if self.i + 4 > self.len {
raise ParseError("Invalid unicode escape sequence")
}
let hex = self.input[self.i:self.i + 4].to_owned()
self.i = self.i + 4
let mut code = 0
for k = 0; k < 4; k = k + 1 {
let h = hex[k].to_int().unsafe_to_char()
let digit = if h >= '0' && h <= '9' {
h.to_int() - 48
} else if h >= 'a' && h <= 'f' {
h.to_int() - 97 + 10
} else if h >= 'A' && h <= 'F' {
h.to_int() - 65 + 10
} else {
raise ParseError("Invalid hex digit in unicode escape")
}
code = code * 16 + digit
}
s = s + code.unsafe_to_char().to_string()
}
_ => raise ParseError("Unknown escape sequence: \\" + esc.to_string())
}
} else {
s = s + c.to_string()
self.i = self.i + 1
}
}
raise ParseError("Unterminated string literal")
}
///|
fn Parser::parse_path(
self : Parser,
expected_start : Char,
) -> JSONPath raise ParseError {
self.skip_whitespace()
match self.next() {
Some(c) =>
if c != expected_start {
raise ParseError(
"Expected path starting with '" + expected_start.to_string() + "'",
)
}
None =>
raise ParseError(
"Expected path starting with '" +
expected_start.to_string() +
"', got empty input",
)
}
let segments = [Root]
while self.i < self.len {
self.skip_whitespace()
let p = self.peek()
if p == None {
break
}
let c = p.unwrap()
if c == '.' {
self.i = self.i + 1
let is_descendant = match self.peek() {
Some('.') => {
self.i = self.i + 1
true
}
_ => false
}
self.skip_whitespace()
match self.peek() {
Some('*') => {
self.i = self.i + 1
if is_descendant {
segments.push(DescendantWildcard)
} else {
segments.push(Wildcard)
}
}
Some('[') => {
let bracket_sel = self.parse_bracket_segment()
match bracket_sel {
Bracket(selectors) =>
if is_descendant {
segments.push(DescendantBracket(selectors))
} else {
segments.push(Bracket(selectors))
}
_ => raise ParseError("Expected bracket selectors")
}
}
_ => {
let name = self.read_ident()
if name == "" {
raise ParseError("Expected identifier after '.' or '..'")
}
if is_descendant {
segments.push(Descendant(name))
} else {
segments.push(Child(name))
}
}
}
} else if c == '[' {
let bracket_sel = self.parse_bracket_segment()
segments.push(bracket_sel)
} else {
break
}
}
segments
}
///|
fn Parser::parse_bracket_segment(self : Parser) -> PathSegment raise ParseError {
self.i = self.i + 1
self.skip_whitespace()
let selectors = []
while true {
let sel = self.parse_selector()
selectors.push(sel)
self.skip_whitespace()
match self.peek() {
Some(',') => {
self.i = self.i + 1
self.skip_whitespace()
}
Some(']') => {
self.i = self.i + 1
break
}
_ => raise ParseError("Expected ',' or ']' inside bracket selector")
}
}
Bracket(selectors)
}
///|
fn Parser::parse_selector(self : Parser) -> Selector raise ParseError {
self.skip_whitespace()
let p = self.peek()
if p == None {
raise ParseError("Unexpected end of input in bracket selector")
}
let c = p.unwrap()
if c == '*' {
self.i = self.i + 1
return Wildcard
}
if c == '\'' || c == '"' {
let str_val = self.parse_string_literal()
return Name(str_val)
}
if c == '?' {
self.i = self.i + 1
self.skip_whitespace()
match self.next() {
Some('(') => ()
_ => raise ParseError("Expected '(' after '?' in filter")
}
let expr = self.parse_filter_expr()
self.skip_whitespace()
match self.next() {
Some(')') => ()
_ => raise ParseError("Expected ')' to close filter expression")
}
return Filter(expr)
}
let has_sign = c == '-' || c == '+'
if has_sign || (c >= '0' && c <= '9') || c == ':' {
let start = if c != ':' {
let val = self.parse_integer()
Some(val)
} else {
None
}
self.skip_whitespace()
match self.peek() {
Some(':') => {
self.i = self.i + 1
self.skip_whitespace()
let end = if self.peek() != Some(':') &&
self.peek() != Some(']') &&
self.peek() != Some(',') {
let val = self.parse_integer()
Some(val)
} else {
None
}
self.skip_whitespace()
let step = match self.peek() {
Some(':') => {
self.i = self.i + 1
self.skip_whitespace()
let val = self.parse_integer()
Some(val)
}
_ => None
}
return Slice(start, end, step)
}
_ =>
match start {
Some(val) => return Index(val)
None => raise ParseError("Invalid index/slice inside brackets")
}
}
}
raise ParseError("Unsupported or invalid selector: '" + c.to_string() + "'")
}
///|
fn filter_expr_to_val(expr : FilterExpr) -> FilterVal raise ParseError {
match expr {
Value(val) => val
Exists(path) => Path(path)
_ =>
raise ParseError(
"Expected value or path in comparison, got complex expression",
)
}
}
///|
fn Parser::parse_filter_expr(self : Parser) -> FilterExpr raise ParseError {
self.parse_filter_or()
}
///|
fn Parser::parse_filter_or(self : Parser) -> FilterExpr raise ParseError {
let mut expr = self.parse_filter_and()
while true {
self.skip_whitespace()
if self.i + 2 <= self.len && self.input[self.i:self.i + 2] == "||" {
self.i = self.i + 2
let right = self.parse_filter_and()
expr = Or(expr, right)
} else {
break
}
}
expr
}
///|
fn Parser::parse_filter_and(self : Parser) -> FilterExpr raise ParseError {
let mut expr = self.parse_filter_comparison()
while true {
self.skip_whitespace()
if self.i + 2 <= self.len && self.input[self.i:self.i + 2] == "&&" {
self.i = self.i + 2
let right = self.parse_filter_comparison()
expr = And(expr, right)
} else {
break
}
}
expr
}
///|
fn Parser::parse_filter_comparison(
self : Parser,
) -> FilterExpr raise ParseError {
self.skip_whitespace()
let left = self.parse_filter_unary()
self.skip_whitespace()
let op_str2 = if self.i + 2 <= self.len {
self.input[self.i:self.i + 2]
} else {
""
}
if op_str2 == "==" {
self.i = self.i + 2
let right = self.parse_filter_unary()
let left_val = filter_expr_to_val(left)
let right_val = filter_expr_to_val(right)
return Eq(left_val, right_val)
}
if op_str2 == "!=" {
self.i = self.i + 2
let right = self.parse_filter_unary()
let left_val = filter_expr_to_val(left)
let right_val = filter_expr_to_val(right)
return Ne(left_val, right_val)
}
if op_str2 == "<=" {
self.i = self.i + 2
let right = self.parse_filter_unary()
let left_val = filter_expr_to_val(left)
let right_val = filter_expr_to_val(right)
return Le(left_val, right_val)
}
if op_str2 == ">=" {
self.i = self.i + 2
let right = self.parse_filter_unary()
let left_val = filter_expr_to_val(left)
let right_val = filter_expr_to_val(right)
return Ge(left_val, right_val)
}
let op_str1 = if self.i < self.len {
self.input[self.i].to_int().unsafe_to_char().to_string()
} else {
""
}
if op_str1 == "<" {
self.i = self.i + 1
let right = self.parse_filter_unary()
let left_val = filter_expr_to_val(left)
let right_val = filter_expr_to_val(right)
return Lt(left_val, right_val)
}
if op_str1 == ">" {
self.i = self.i + 1
let right = self.parse_filter_unary()
let left_val = filter_expr_to_val(left)
let right_val = filter_expr_to_val(right)
return Gt(left_val, right_val)
}
left
}
///|
fn Parser::parse_filter_unary(self : Parser) -> FilterExpr raise ParseError {
self.skip_whitespace()
if self.peek() == Some('!') {
if self.i + 2 <= self.len && self.input[self.i:self.i + 2] == "!=" {
// comparison
} else {
self.i = self.i + 1
let sub = self.parse_filter_unary()
return Not(sub)
}
}
self.parse_filter_primary()
}
///|
fn Parser::parse_filter_primary(self : Parser) -> FilterExpr raise ParseError {
self.skip_whitespace()
let p = self.peek()
if p == None {
raise ParseError("Unexpected end of input in filter expression")
}
let c = p.unwrap()
if c == '(' {
self.i = self.i + 1
let sub = self.parse_filter_expr()
self.skip_whitespace()
match self.next() {
Some(')') => sub
_ => raise ParseError("Expected ')' to close parenthesis")
}
} else if c == '@' {
let path = self.parse_path_expr()
Exists(path)
} else if c == '$' {
let path = self.parse_path_expr()
Exists(path)
} else {
let lit = self.parse_json_literal()
Value(Literal(lit))
}
}
///|
fn Parser::parse_path_expr(self : Parser) -> PathExpr raise ParseError {
self.skip_whitespace()
let p = self.peek()
if p == None {
raise ParseError("Expected path expression")
}
let c = p.unwrap()
if c == '@' {
let path = self.parse_path('@')
Relative(path)
} else if c == '$' {
let path = self.parse_path('$')
Absolute(path)
} else {
raise ParseError("Expected path starting with '@' or '$'")
}
}
///|
fn Parser::parse_json_literal(self : Parser) -> Json raise ParseError {
self.skip_whitespace()
let p = self.peek()
if p == None {
raise ParseError("Expected JSON literal")
}
let c = p.unwrap()
if c == '\'' || c == '"' {
let s = self.parse_string_literal()
return s.to_json()
}
if self.i + 4 <= self.len && self.input[self.i:self.i + 4] == "null" {
self.i = self.i + 4
return null
}
if self.i + 4 <= self.len && self.input[self.i:self.i + 4] == "true" {
self.i = self.i + 4
return true
}
if self.i + 5 <= self.len && self.input[self.i:self.i + 5] == "false" {
self.i = self.i + 5
return false
}
if c == '-' || c == '+' || (c >= '0' && c <= '9') {
let mut is_neg = false
if c == '-' {
is_neg = true
self.i = self.i + 1
} else if c == '+' {
self.i = self.i + 1
}
let mut num_str = ""
while self.i < self.len {
let ch = self.input[self.i].to_int().unsafe_to_char()
if ch >= '0' && ch <= '9' {
num_str = num_str + ch.to_string()
self.i = self.i + 1
} else {
break
}
}
if num_str == "" {
raise ParseError("Expected digits in number literal")
}
if self.i < self.len && self.input[self.i].to_int().unsafe_to_char() == '.' {
num_str = num_str + "."
self.i = self.i + 1
let mut has_frac_digit = false
while self.i < self.len {
let ch = self.input[self.i].to_int().unsafe_to_char()
if ch >= '0' && ch <= '9' {
num_str = num_str + ch.to_string()
has_frac_digit = true
self.i = self.i + 1
} else {
break
}
}
if !has_frac_digit {
raise ParseError("Expected digits after '.' in decimal number")
}
}
let double_str = if is_neg { "-" + num_str } else { num_str }
try {
let val = @string.parse_double(double_str)
val.to_json()
} catch {
_ => raise ParseError("Invalid double literal: " + double_str)
}
} else {
raise ParseError("Unexpected literal character: " + c.to_string())
}
}
///|
pub fn parse(input : String) -> Result[JSONPath, String] {
let parser = Parser::new(input)
try {
let path = parser.parse_path('$')
parser.skip_whitespace()
if parser.i < parser.len {
raise ParseError("Unexpected characters at end of JSONPath")
}
Ok(path)
} catch {
ParseError(err) => Err(err)
}
}