1use std::rc::Rc;23use jrsonnet_gcmodule::Acyclic;4use jrsonnet_ir::{5 unescape, ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BinaryOpType, BindSpec, CompSpec,6 Destruct, DestructRest, Expr, ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr,7 IfElse, IfSpecData, ImportKind, IndexPart, LiteralType, Member, ObjBody, ObjComp, ObjMembers,8 Slice, SliceDesc, Source, Span, Spanned, UnaryOpType, Visibility,9};10use jrsonnet_lexer::{collect_lexed_str_block, Lexeme, Lexer, SyntaxKind, T};1112pub struct ParserSettings {13 pub source: Source,14}1516#[derive(Debug, Clone)]17pub struct ParseErrorLocation {18 pub offset: usize,19}2021#[derive(Debug, Clone)]22pub struct ParseError {23 pub message: String,24 pub location: ParseErrorLocation,25}2627impl std::fmt::Display for ParseError {28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {29 write!(f, "{}", self.message)30 }31}3233type R<T> = Result<T, ParseError>;3435struct Parser<'a> {36 lexemes: Vec<Lexeme<'a>>,37 offset: usize,38 source: Source,39}4041impl<'a> Parser<'a> {42 fn new(code: &'a str, source: Source) -> Self {43 Self {44 lexemes: Lexer::new(code)45 .filter(|l| {46 !matches!(47 l.kind,48 SyntaxKind::WHITESPACE49 | SyntaxKind::SINGLE_LINE_SLASH_COMMENT50 | SyntaxKind::SINGLE_LINE_HASH_COMMENT51 | SyntaxKind::MULTI_LINE_COMMENT52 )53 })54 .collect(),55 offset: 0,56 source,57 }58 }5960 fn peek(&self) -> SyntaxKind {61 if self.at_eof() {62 SyntaxKind::EOF63 } else {64 self.lexemes[self.offset].kind65 }66 }6768 fn text(&self) -> &'a str {69 self.lexemes[self.offset].text70 }7172 fn at(&self, kind: SyntaxKind) -> bool {73 !self.at_eof() && self.peek() == kind74 }7576 fn eat_any(&mut self) {77 self.offset += 1;78 }7980 fn at_eof(&self) -> bool {81 self.offset >= self.lexemes.len()82 }8384 fn try_eat(&mut self, t: SyntaxKind) -> bool {85 if self.at(t) {86 self.eat_any();87 return true;88 }89 false90 }9192 fn current_desc(&self) -> String {93 if self.at_eof() {94 return "end of file".to_owned();95 }96 let kind = self.peek();97 let text = self.text();98 let name = kind.display_name();99 if matches!(kind, SyntaxKind::IDENT | SyntaxKind::FLOAT) {100 format!("{name} \"{text}\"")101 } else {102 name.to_owned()103 }104 }105106 fn eat(&mut self, t: SyntaxKind) -> R<()> {107 if !self.at(t) {108 return Err(self.error(format!(109 "expected {}, got {}",110 t.display_name(),111 self.current_desc(),112 )));113 }114 self.eat_any();115 Ok(())116 }117118 fn span_start(&self) -> u32 {119 if self.at_eof() {120 if let Some(last) = self.lexemes.last() {121 return last.range.1;122 }123 return 0;124 }125 self.lexemes[self.offset].range.0126 }127128 fn span_end(&self) -> u32 {129 self.lexemes[self.offset - 1].range.1130 }131132 fn error(&self, message: String) -> ParseError {133 ParseError {134 location: ParseErrorLocation {135 offset: self.span_start() as usize,136 },137 message,138 }139 }140141 fn expect_ident(&mut self) -> R<IStr> {142 if !self.at(SyntaxKind::IDENT) {143 return Err(self.error(format!("expected identifier, got {}", self.current_desc())));144 }145 let text = self.text();146 if is_reserved(text) {147 return Err(self.error(format!(148 "expected identifier, got reserved word '{text}'"149 )));150 }151 let s: IStr = text.into();152 self.eat_any();153 Ok(s)154 }155156 fn at_ident(&self) -> bool {157 self.at(SyntaxKind::IDENT) && !is_reserved(self.lexemes[self.offset].text)158 }159}160161fn is_reserved(s: &str) -> bool {162 matches!(163 s,164 "assert"165 | "else" | "error"166 | "false" | "for"167 | "function" | "if"168 | "import" | "importstr"169 | "importbin" | "in"170 | "local" | "null"171 | "tailstrict" | "then"172 | "self" | "super"173 | "true"174 )175}176177fn spanned<T: Acyclic>(p: &mut Parser<'_>, cb: impl FnOnce(&mut Parser<'_>) -> R<T>) -> R<Spanned<T>> {178 let start = p.span_start();179 let v = cb(p)?;180 let end = p.span_end();181 Ok(Spanned::new(v, Span(p.source.clone(), start, end)))182}183184fn parse_string_content(p: &mut Parser<'_>) -> R<IStr> {185 let kind = p.peek();186 let text = p.text();187 let s = match kind {188 SyntaxKind::STRING_DOUBLE => {189 let inner = &text[1..text.len() - 1];190 unescape::unescape(inner)191 .ok_or_else(|| p.error("invalid string escape".into()))?192 }193 SyntaxKind::STRING_SINGLE => {194 let inner = &text[1..text.len() - 1];195 unescape::unescape(inner)196 .ok_or_else(|| p.error("invalid string escape".into()))?197 }198 SyntaxKind::STRING_DOUBLE_VERBATIM => {199 let inner = &text[2..text.len() - 1];200 inner.replace("\"\"", "\"")201 }202 SyntaxKind::STRING_SINGLE_VERBATIM => {203 let inner = &text[2..text.len() - 1];204 inner.replace("''", "'")205 }206 SyntaxKind::STRING_BLOCK => {207 let inner = &text[3..];208 let collected = collect_lexed_str_block(inner)209 .map_err(|_| p.error("invalid string block".into()))?;210 let mut result = String::new();211 for (i, line) in collected.lines.iter().enumerate() {212 if i > 0 {213 result.push('\n');214 }215 result.push_str(line);216 }217 if !collected.truncate {218 result.push('\n');219 }220 result221 }222 _ => return Err(p.error(format!("expected string, got {}", p.current_desc()))),223 };224 p.eat_any();225 Ok(s.into())226}227228fn is_string_token(kind: SyntaxKind) -> bool {229 matches!(230 kind,231 SyntaxKind::STRING_DOUBLE232 | SyntaxKind::STRING_SINGLE233 | SyntaxKind::STRING_DOUBLE_VERBATIM234 | SyntaxKind::STRING_SINGLE_VERBATIM235 | SyntaxKind::STRING_BLOCK236 )237}238239fn parse_number(p: &mut Parser<'_>) -> R<f64> {240 let text = p.text();241 let n: f64 = text242 .replace('_', "")243 .parse()244 .map_err(|_| p.error(format!("invalid number literal: {text}")))?;245 if !n.is_finite() {246 return Err(p.error("numbers are finite".into()));247 }248 p.eat_any();249 Ok(n)250}251252fn literal(p: &mut Parser<'_>) -> Option<LiteralType> {253 let t = match p.peek() {254 T![self] => LiteralType::This,255 T![super] => LiteralType::Super,256 T!['$'] => LiteralType::Dollar,257 T![null] => LiteralType::Null,258 T![true] => LiteralType::True,259 T![false] => LiteralType::False,260 _ => return None,261 };262 p.eat_any();263 Some(t)264}265266fn assert_stmt(p: &mut Parser<'_>) -> R<AssertStmt> {267 p.eat(T![assert])?;268 let cond = spanned(p, expr)?;269 let msg = if p.try_eat(T![:]) {270 Some(spanned(p, expr)?)271 } else {272 None273 };274 Ok(AssertStmt(cond, msg))275}276277fn if_spec_data(p: &mut Parser<'_>) -> R<IfSpecData> {278 let v = spanned(p, |p| p.eat(T![if]))?;279 let cond = expr(p)?;280 Ok(IfSpecData { span: v.span, cond })281}282283fn if_else(p: &mut Parser<'_>) -> R<IfElse> {284 let cond = if_spec_data(p)?;285 p.eat(T![then])?;286 let cond_then = expr(p)?;287 let cond_else = if p.try_eat(T![else]) {288 Some(expr(p)?)289 } else {290 None291 };292 Ok(IfElse {293 cond,294 cond_then,295 cond_else,296 })297}298299fn slice_desc(p: &mut Parser<'_>, start: Option<Spanned<Expr>>) -> R<SliceDesc> {300 p.eat(T![:])?;301 let end = if !p.at(T![:]) && !p.at(T![']']) {302 Some(spanned(p, expr)?)303 } else {304 None305 };306 let step = if p.try_eat(T![:]) {307 if !p.at(T![']']) {308 Some(spanned(p, expr)?)309 } else {310 None311 }312 } else {313 None314 };315 Ok(SliceDesc { start, end, step })316}317318fn destruct(p: &mut Parser<'_>) -> R<Destruct> {319 if p.at_ident() {320 return Ok(Destruct::Full(p.expect_ident()?));321 }322 #[cfg(not(feature = "exp-destruct"))]323 return Err(p.error(format!(324 "expected identifier, got {}",325 p.current_desc()326 )));327 #[cfg(feature = "exp-destruct")]328 {329 if p.try_eat(T![?]) {330 return Ok(Destruct::Skip);331 }332 if p.at(T!['[']) {333 return destruct_array(p);334 }335 if p.at(T!['{']) {336 return destruct_object(p);337 }338 Err(p.error(format!(339 "expected destructure pattern, got {}",340 p.current_desc()341 )))342 }343}344345#[cfg(feature = "exp-destruct")]346fn destruct_rest(p: &mut Parser<'_>) -> R<DestructRest> {347 p.eat(T![...])?;348 if p.at_ident() {349 Ok(DestructRest::Keep(p.expect_ident()?))350 } else {351 Ok(DestructRest::Drop)352 }353}354355#[cfg(feature = "exp-destruct")]356fn destruct_array(p: &mut Parser<'_>) -> R<Destruct> {357 p.eat(T!['['])?;358 let mut start = Vec::new();359 let mut rest = None;360 let mut end = Vec::new();361 if !p.at(T![']']) {362 loop {363 if p.at(T![...]) {364 rest = Some(destruct_rest(p)?);365 if p.try_eat(T![,]) {366 if !p.at(T![']']) {367 loop {368 end.push(destruct(p)?);369 if !p.try_eat(T![,]) {370 break;371 }372 if p.at(T![']']) {373 break;374 }375 }376 }377 }378 break;379 }380 start.push(destruct(p)?);381 if !p.try_eat(T![,]) {382 break;383 }384 if p.at(T![']']) {385 break;386 }387 }388 }389 p.eat(T![']'])?;390 Ok(Destruct::Array { start, rest, end })391}392393#[cfg(feature = "exp-destruct")]394fn destruct_object(p: &mut Parser<'_>) -> R<Destruct> {395 p.eat(T!['{'])?;396 let mut fields = Vec::new();397 let mut rest = None;398 if !p.at(T!['}']) {399 loop {400 if p.at(T![...]) {401 rest = Some(destruct_rest(p)?);402 p.try_eat(T![,]);403 break;404 }405 let name = p.expect_ident()?;406 let into = if p.try_eat(T![:]) {407 Some(destruct(p)?)408 } else {409 None410 };411 let default = if p.try_eat(T![=]) {412 Some(Rc::new(spanned(p, expr)?))413 } else {414 None415 };416 fields.push((name, into, default));417 if !p.try_eat(T![,]) {418 break;419 }420 if p.at(T!['}']) {421 break;422 }423 }424 }425 p.eat(T!['}'])?;426 Ok(Destruct::Object { fields, rest })427}428429fn params(p: &mut Parser<'_>) -> R<ExprParams> {430 if p.at(T![')']) {431 return Ok(ExprParams::new(Vec::new()));432 }433 let mut result = Vec::new();434 loop {435 let d = destruct(p)?;436 let default = if p.try_eat(T![=]) {437 Some(Rc::new(expr(p)?))438 } else {439 None440 };441 result.push(ExprParam {442 destruct: d,443 default,444 });445 if !p.try_eat(T![,]) {446 break;447 }448 if p.at(T![')']) {449 break;450 }451 }452 Ok(ExprParams::new(result))453}454455fn args(p: &mut Parser<'_>) -> R<ArgsDesc> {456 if p.at(T![')']) {457 return Ok(ArgsDesc::new(Vec::new(), Vec::new()));458 }459 let mut unnamed = Vec::new();460 let mut named = Vec::new();461 let mut named_started = false;462 loop {463 let is_named = p.at_ident() && {464 let next_offset = p.offset + 1;465 next_offset < p.lexemes.len() && p.lexemes[next_offset].kind == T![=] && {466 let after_eq = next_offset + 1;467 after_eq >= p.lexemes.len() || p.lexemes[after_eq].kind != T![=]468 }469 };470 if is_named {471 let name: IStr = p.expect_ident()?;472 p.eat(T![=])?;473 let value = Rc::new(expr(p)?);474 named.push((name, value));475 named_started = true;476 } else {477 if named_started {478 return Err(p.error("positional argument after named argument".into()));479 }480 unnamed.push(Rc::new(expr(p)?));481 }482 if !p.try_eat(T![,]) {483 break;484 }485 if p.at(T![')']) {486 break;487 }488 }489 Ok(ArgsDesc::new(unnamed, named))490}491492fn bind(p: &mut Parser<'_>) -> R<BindSpec> {493 #[cfg(feature = "exp-destruct")]494 {495 if !p.at_ident() {496 let d = destruct(p)?;497 p.eat(T![=])?;498 let value = Rc::new(expr(p)?);499 return Ok(BindSpec::Field { into: d, value });500 }501 }502 let name = p.expect_ident()?;503 if p.try_eat(T!['(']) {504 let ps = params(p)?;505 p.eat(T![')'])?;506 p.eat(T![=])?;507 let value = Rc::new(expr(p)?);508 Ok(BindSpec::Function {509 name,510 params: ps,511 value,512 })513 } else {514 p.eat(T![=])?;515 let value = Rc::new(expr(p)?);516 Ok(BindSpec::Field {517 into: Destruct::Full(name),518 value,519 })520 }521}522523fn visibility(p: &mut Parser<'_>) -> R<Visibility> {524 p.eat(T![:])?;525 if p.try_eat(T![:]) {526 if p.try_eat(T![:]) {527 Ok(Visibility::Unhide)528 } else {529 Ok(Visibility::Hidden)530 }531 } else {532 Ok(Visibility::Normal)533 }534}535536fn field_name(p: &mut Parser<'_>) -> R<FieldName> {537 if p.at_ident() {538 Ok(FieldName::Fixed(p.expect_ident()?))539 } else if is_string_token(p.peek()) {540 Ok(FieldName::Fixed(parse_string_content(p)?))541 } else if p.at(T!['[']) {542 p.eat(T!['['])?;543 let e = expr(p)?;544 p.eat(T![']'])?;545 Ok(FieldName::Dyn(e))546 } else {547 Err(p.error(format!("expected field name, got {}", p.current_desc())))548 }549}550551fn field(p: &mut Parser<'_>) -> R<FieldMember> {552 let name = spanned(p, field_name)?;553554 if p.at(T!['(']) {555 p.eat(T!['('])?;556 let ps = params(p)?;557 p.eat(T![')'])?;558 let vis = visibility(p)?;559 let value = Rc::new(expr(p)?);560 Ok(FieldMember {561 name,562 plus: false,563 params: Some(ps),564 visibility: vis,565 value,566 })567 } else {568 let plus = p.try_eat(T![+]);569 let vis = visibility(p)?;570 let value = Rc::new(expr(p)?);571 Ok(FieldMember {572 name,573 plus,574 params: None,575 visibility: vis,576 value,577 })578 }579}580581fn member(p: &mut Parser<'_>) -> R<Member> {582 if p.at(T![local]) {583 p.eat(T![local])?;584 Ok(Member::BindStmt(bind(p)?))585 } else if p.at(T![assert]) {586 Ok(Member::AssertStmt(assert_stmt(p)?))587 } else {588 Ok(Member::Field(field(p)?))589 }590}591592fn for_spec(p: &mut Parser<'_>) -> R<ForSpecData> {593 p.eat(T![for])?;594 let d = destruct(p)?;595 p.eat(T![in])?;596 let over = expr(p)?;597 Ok(ForSpecData { destruct: d, over })598}599600fn compspecs(p: &mut Parser<'_>) -> R<Vec<CompSpec>> {601 let mut specs = Vec::new();602 specs.push(CompSpec::ForSpec(for_spec(p)?));603 loop {604 if p.at(T![for]) {605 specs.push(CompSpec::ForSpec(for_spec(p)?));606 } else if p.at(T![if]) {607 let isd = if_spec_data(p)?;608 specs.push(CompSpec::IfSpec(isd));609 } else {610 break;611 }612 }613 Ok(specs)614}615616fn objinside(p: &mut Parser<'_>) -> R<ObjBody> {617 if p.at(T!['}']) {618 return Ok(ObjBody::MemberList(ObjMembers {619 locals: Rc::new(Vec::new()),620 asserts: Rc::new(Vec::new()),621 fields: Vec::new(),622 }));623 }624625 let mut members = Vec::new();626 loop {627 members.push(member(p)?);628 if !p.try_eat(T![,]) {629 break;630 }631 if p.at(T!['}']) || p.at(T![for]) {632 break;633 }634 }635636 if p.at(T![for]) {637 let specs = compspecs(p)?;638 let mut locals = Vec::new();639 let mut field_member = None;640 for m in members {641 match m {642 Member::Field(f) => {643 if field_member.is_some() {644 return Err(p.error(645 "object comprehension can only contain one field".into(),646 ));647 }648 field_member = Some(f);649 }650 Member::BindStmt(b) => locals.push(b),651 Member::AssertStmt(_) => {652 return Err(p.error(653 "asserts are unsupported in object comprehension".into(),654 ));655 }656 }657 }658 Ok(ObjBody::ObjComp(ObjComp {659 locals: Rc::new(locals),660 field: Rc::new(661 field_member.ok_or_else(|| {662 p.error("missing object comprehension field".into())663 })?,664 ),665 compspecs: specs,666 }))667 } else {668 let mut locals = Vec::new();669 let mut asserts = Vec::new();670 let mut fields = Vec::new();671 for m in members {672 match m {673 Member::Field(f) => fields.push(f),674 Member::BindStmt(b) => locals.push(b),675 Member::AssertStmt(a) => asserts.push(a),676 }677 }678 Ok(ObjBody::MemberList(ObjMembers {679 locals: Rc::new(locals),680 asserts: Rc::new(asserts),681 fields,682 }))683 }684}685686fn expr_basic(p: &mut Parser<'_>) -> R<Expr> {687 if let Some(lit) = literal(p) {688 return Ok(Expr::Literal(lit));689 }690691 match p.peek() {692 SyntaxKind::STRING_DOUBLE693 | SyntaxKind::STRING_SINGLE694 | SyntaxKind::STRING_DOUBLE_VERBATIM695 | SyntaxKind::STRING_SINGLE_VERBATIM696 | SyntaxKind::STRING_BLOCK => Ok(Expr::Str(parse_string_content(p)?)),697698 SyntaxKind::FLOAT => Ok(Expr::Num(parse_number(p)?)),699700 T!['('] => {701 p.eat(T!['('])?;702 let e = expr(p)?;703 p.eat(T![')'])?;704 Ok(e)705 }706707 T!['['] => {708 p.eat(T!['['])?;709 if p.at(T![']']) {710 p.eat(T![']'])?;711 return Ok(Expr::Arr(Rc::new(Vec::new())));712 }713 let first = expr(p)?;714 if p.at(T![for]) {715 let specs = compspecs(p)?;716 p.eat(T![']'])?;717 Ok(Expr::ArrComp(Rc::new(first), specs))718 } else if p.at(T![,]) && {719 let next = p.offset + 1;720 next < p.lexemes.len() && p.lexemes[next].kind == T![for]721 } {722 p.eat(T![,])?;723 let specs = compspecs(p)?;724 p.eat(T![']'])?;725 Ok(Expr::ArrComp(Rc::new(first), specs))726 } else {727 let mut elems = vec![first];728 while p.try_eat(T![,]) {729 if p.at(T![']']) {730 break;731 }732 elems.push(expr(p)?);733 }734 p.eat(T![']'])?;735 Ok(Expr::Arr(Rc::new(elems)))736 }737 }738739 T!['{'] => {740 p.eat(T!['{'])?;741 let body = objinside(p)?;742 p.eat(T!['}'])?;743 Ok(Expr::Obj(body))744 }745746 T![local] => {747 p.eat(T![local])?;748 let mut binds = Vec::new();749 loop {750 binds.push(bind(p)?);751 if !p.try_eat(T![,]) {752 break;753 }754 }755 p.eat(T![;])?;756 let body = expr(p)?;757 Ok(Expr::LocalExpr(binds, Box::new(body)))758 }759760 T![if] => Ok(Expr::IfElse(Box::new(if_else(p)?))),761762 T![function] => {763 p.eat(T![function])?;764 p.eat(T!['('])?;765 let ps = params(p)?;766 p.eat(T![')'])?;767 let body = expr(p)?;768 Ok(Expr::Function(ps, Rc::new(body)))769 }770771 T![assert] => {772 let a = assert_stmt(p)?;773 p.eat(T![;])?;774 let rest = expr(p)?;775 Ok(Expr::AssertExpr(Rc::new(AssertExpr { assert: a, rest })))776 }777778 T![error] => {779 let span = spanned(p, |p| p.eat(T![error]))?;780 let e = expr(p)?;781 Ok(Expr::ErrorStmt(span.span, Box::new(e)))782 }783784 T![importstr] => {785 let kind = spanned(p, |p| {786 p.eat(T![importstr])?;787 Ok(ImportKind::Str)788 })?;789 let path = expr(p)?;790 Ok(Expr::Import(kind, Box::new(path)))791 }792793 T![importbin] => {794 let kind = spanned(p, |p| {795 p.eat(T![importbin])?;796 Ok(ImportKind::Bin)797 })?;798 let path = expr(p)?;799 Ok(Expr::Import(kind, Box::new(path)))800 }801802 T![import] => {803 let kind = spanned(p, |p| {804 p.eat(T![import])?;805 Ok(ImportKind::Normal)806 })?;807 let path = expr(p)?;808 Ok(Expr::Import(kind, Box::new(path)))809 }810811 SyntaxKind::IDENT => {812 let text = p.text();813 if is_reserved(text) {814 return Err(p.error(format!("unexpected reserved word '{text}'")));815 }816 let n = spanned(p, |p| {817 let s: IStr = p.text().into();818 p.eat_any();819 Ok(s)820 })?;821 Ok(Expr::Var(n))822 }823824 _ => Err(p.error(format!("unexpected {}", p.current_desc()))),825 }826}827828829fn flush_index_parts(e: &mut Expr, parts: &mut Vec<IndexPart>) {830 if parts.is_empty() {831 return;832 }833 let old = std::mem::replace(e, Expr::Literal(LiteralType::Null));834 *e = Expr::Index {835 indexable: Box::new(old),836 parts: std::mem::take(parts),837 };838}839840fn expr_suffix(p: &mut Parser<'_>) -> R<Expr> {841 let mut e = expr_basic(p)?;842 843 844 845 let mut parts: Vec<IndexPart> = Vec::new();846847 loop {848 #[cfg(feature = "exp-null-coaelse")]849 if p.at(T![?]) {850 p.eat_any();851 if p.try_eat(T![.]) {852 if p.at(T!['[']) {853 854 p.eat(T!['['])?;855 let idx = spanned(p, expr)?;856 p.eat(T![']'])?;857 parts.push(IndexPart {858 span: idx.span,859 value: idx.value,860 null_coaelse: true,861 });862 } else {863 864 let id_spanned = spanned(p, |p| {865 let name = p.expect_ident()?;866 Ok(Expr::Str(name))867 })?;868 parts.push(IndexPart {869 span: id_spanned.span,870 value: id_spanned.value,871 null_coaelse: true,872 });873 }874 } else {875 return Err(p.error("expected '.' after '?'".into()));876 }877 continue;878 }879880 if p.at(T![.]) {881 p.eat(T![.])?;882 let id_spanned = spanned(p, |p| {883 let name = p.expect_ident()?;884 Ok(Expr::Str(name))885 })?;886 parts.push(IndexPart {887 span: id_spanned.span,888 value: id_spanned.value,889 #[cfg(feature = "exp-null-coaelse")]890 null_coaelse: false,891 });892 } else if p.at(T!['[']) {893 p.eat(T!['['])?;894895 if p.at(T![:]) {896 897 flush_index_parts(&mut e, &mut parts);898 let slice = slice_desc(p, None)?;899 p.eat(T![']'])?;900 e = Expr::Slice(Box::new(Slice { value: e, slice }));901 } else {902 let idx = spanned(p, expr)?;903 if p.at(T![:]) {904 905 flush_index_parts(&mut e, &mut parts);906 let slice = slice_desc(p, Some(idx))?;907 p.eat(T![']'])?;908 e = Expr::Slice(Box::new(Slice { value: e, slice }));909 } else {910 911 p.eat(T![']'])?;912 parts.push(IndexPart {913 span: idx.span,914 value: idx.value,915 #[cfg(feature = "exp-null-coaelse")]916 null_coaelse: false,917 });918 }919 }920 } else if p.at(T!['(']) {921 flush_index_parts(&mut e, &mut parts);922 let args_spanned = spanned(p, |p| {923 p.eat(T!['('])?;924 let a = args(p)?;925 p.eat(T![')'])?;926 Ok(a)927 })?;928 let tailstrict = p.try_eat(T![tailstrict]);929 e = Expr::Apply(Box::new(e), args_spanned, tailstrict);930 } else if p.at(T!['{']) {931 flush_index_parts(&mut e, &mut parts);932 p.eat(T!['{'])?;933 let body = objinside(p)?;934 p.eat(T!['}'])?;935 e = Expr::ObjExtend(Rc::new(e), body);936 } else {937 break;938 }939 }940941 flush_index_parts(&mut e, &mut parts);942 Ok(e)943}944945fn prefix_binding_power(op: UnaryOpType) -> u8 {946 match op {947 UnaryOpType::Plus | UnaryOpType::Minus | UnaryOpType::Not | UnaryOpType::BitNot => 20,948 }949}950951fn infix_binding_power(op: BinaryOpType) -> (u8, u8) {952 match op {953 BinaryOpType::Or => (2, 3),954 #[cfg(feature = "exp-null-coaelse")]955 BinaryOpType::NullCoaelse => (2, 3),956 BinaryOpType::And => (4, 5),957 BinaryOpType::BitOr => (6, 7),958 BinaryOpType::BitXor => (8, 9),959 BinaryOpType::BitAnd => (10, 11),960 BinaryOpType::Eq | BinaryOpType::Neq => (12, 13),961 BinaryOpType::Lt962 | BinaryOpType::Gt963 | BinaryOpType::Lte964 | BinaryOpType::Gte965 | BinaryOpType::In => (14, 15),966 BinaryOpType::Lhs | BinaryOpType::Rhs => (16, 17),967 BinaryOpType::Add | BinaryOpType::Sub => (18, 19),968 BinaryOpType::Mul | BinaryOpType::Div | BinaryOpType::Mod => (20, 21),969 }970}971972fn unary_op(kind: SyntaxKind) -> Option<UnaryOpType> {973 match kind {974 T![+] => Some(UnaryOpType::Plus),975 T![-] => Some(UnaryOpType::Minus),976 T![!] => Some(UnaryOpType::Not),977 T![~] => Some(UnaryOpType::BitNot),978 _ => None,979 }980}981982fn binary_op(p: &Parser<'_>) -> Option<BinaryOpType> {983 match p.peek() {984 T![||] => Some(BinaryOpType::Or),985 T![&&] => Some(BinaryOpType::And),986 T![|] => Some(BinaryOpType::BitOr),987 T![^] => Some(BinaryOpType::BitXor),988 T![&] => Some(BinaryOpType::BitAnd),989 T![==] => Some(BinaryOpType::Eq),990 T![!=] => Some(BinaryOpType::Neq),991 T![<] => Some(BinaryOpType::Lt),992 T![>] => Some(BinaryOpType::Gt),993 T![<=] => Some(BinaryOpType::Lte),994 T![>=] => Some(BinaryOpType::Gte),995 T![<<] => Some(BinaryOpType::Lhs),996 T![>>] => Some(BinaryOpType::Rhs),997 T![+] => Some(BinaryOpType::Add),998 T![-] => Some(BinaryOpType::Sub),999 T![*] => Some(BinaryOpType::Mul),1000 T![/] => Some(BinaryOpType::Div),1001 T![%] => Some(BinaryOpType::Mod),1002 T![in] => Some(BinaryOpType::In),1003 #[cfg(feature = "exp-null-coaelse")]1004 T![??] => Some(BinaryOpType::NullCoaelse),1005 _ => None,1006 }1007}10081009fn expr_bp(p: &mut Parser<'_>, min_bp: u8) -> R<Expr> {1010 let mut lhs = if let Some(op) = unary_op(p.peek()) {1011 p.eat_any();1012 let rbp = prefix_binding_power(op);1013 let rhs = expr_bp(p, rbp)?;1014 Expr::UnaryOp(op, Box::new(rhs))1015 } else {1016 expr_suffix(p)?1017 };10181019 loop {1020 if p.at_eof() {1021 break;1022 }10231024 let Some(op) = binary_op(p) else {1025 break;1026 };10271028 let (lbp, rbp) = infix_binding_power(op);1029 if lbp < min_bp {1030 break;1031 }10321033 p.eat_any();1034 let rhs = expr_bp(p, rbp)?;1035 lhs = Expr::BinaryOp(Box::new(BinaryOp { lhs, op, rhs }));1036 }10371038 Ok(lhs)1039}10401041fn expr(p: &mut Parser<'_>) -> R<Expr> {1042 expr_bp(p, 0)1043}10441045pub fn parse(str: &str, settings: &ParserSettings) -> Result<Expr, ParseError> {1046 let mut p = Parser::new(str, settings.source.clone());1047 for lexeme in &p.lexemes {1048 if let Some(desc) = lexeme.kind.error_description() {1049 return Err(ParseError {1050 message: desc.to_owned(),1051 location: ParseErrorLocation {1052 offset: lexeme.range.0 as usize,1053 },1054 });1055 }1056 }1057 let e = expr(&mut p)?;1058 if !p.at_eof() {1059 return Err(p.error(format!(1060 "expected end of file, got {}",1061 p.current_desc(),1062 )));1063 }1064 Ok(e)1065}10661067pub fn string_to_expr(s: IStr, settings: &ParserSettings) -> Spanned<Expr> {1068 let len = s.len();1069 Spanned::new(1070 Expr::Str(s),1071 Span(settings.source.clone(), 0, len as u32),1072 )1073}10741075#[cfg(test)]1076mod tests {1077 use std::fs;10781079 use insta::{assert_snapshot, glob};1080 use jrsonnet_ir::{IStr, Source};10811082 use super::*;10831084 fn parse_str(input: &str) -> Expr {1085 let source = Source::new_virtual("<test>".into(), input.into());1086 let settings = ParserSettings { source };1087 parse(input, &settings).unwrap()1088 }10891090 #[test]1091 #[cfg(not(feature = "exp-null-coaelse"))]1092 fn basic_test() {1093 let v = parse_str("assert true[false] : false ; true");1094 assert_snapshot!(format!("{v:#?}"));1095 }10961097 #[test]1098 fn literals() {1099 let v = parse_str("[null, true, false, self, super, $]");1100 assert_snapshot!(format!("{v:#?}"));1101 }11021103 #[test]1104 fn basic_math() {1105 let v = parse_str("2+2*2");1106 assert_snapshot!(format!("{v:#?}"));1107 }11081109 #[test]1110 fn underscore_numbers() {1111 let v = parse_str("[1_000, 1_000.000_1, 1_0e1_0]");1112 assert_snapshot!(format!("{v:#?}"));1113 }11141115 #[test]1116 fn strings() {1117 let v = parse_str(r#"["hello", 'world', @"raw""str", @'raw''str']"#);1118 assert_snapshot!(format!("{v:#?}"));1119 }11201121 #[test]1122 fn object() {1123 let v = parse_str("{a: 1, b:: 2, c::: 3}");1124 assert_snapshot!(format!("{v:#?}"));1125 }11261127 #[test]1128 fn function_and_call() {1129 let v = parse_str("local f(x, y=1) = x + y; f(2, y=3)");1130 assert_snapshot!(format!("{v:#?}"));1131 }11321133 #[test]1134 fn if_then_else() {1135 let v = parse_str("if true then 1 else 2");1136 assert_snapshot!(format!("{v:#?}"));1137 }11381139 #[test]1140 fn imports() {1141 let v = parse_str(r#"[import "a", importstr "b", importbin "c"]"#);1142 assert_snapshot!(format!("{v:#?}"));1143 }11441145 #[test]1146 fn array_comp() {1147 let v = parse_str("[x for x in arr]");1148 assert_snapshot!(format!("{v:#?}"));1149 }11501151 #[test]1152 #[cfg(not(feature = "exp-null-coaelse"))]1153 fn index_and_suffix() {1154 let v = parse_str("std.test(2).field[0]");1155 assert_snapshot!(format!("{v:#?}"));1156 }11571158 #[test]1159 fn obj_extend() {1160 let v = parse_str("{} { x: 1 }");1161 assert_snapshot!(format!("{v:#?}"));1162 }11631164 #[test]1165 fn unary_ops() {1166 let v = parse_str("!a && !b");1167 assert_snapshot!(format!("{v:#?}"));1168 }11691170 #[test]1171 fn error_expr() {1172 let v = parse_str("error \"bad\"");1173 assert_snapshot!(format!("{v:#?}"));1174 }11751176 #[test]1177 fn slice() {1178 let v = parse_str("[a[1:], a[1::], a[:1:], a[::1]]");1179 assert_snapshot!(format!("{v:#?}"));1180 }11811182 #[test]1183 #[cfg(not(feature = "exp-null-coaelse"))]1184 fn peg_snapshots() {1185 glob!("../../jrsonnet-peg-parser/src", "tests/*.jsonnet", |path| {1186 let input = fs::read_to_string(path).expect("read test file");1187 let source = Source::new_virtual("<test>".into(), IStr::empty());1188 let settings = ParserSettings { source };1189 let v = parse(&input, &settings).unwrap();1190 let v = format!("{v:#?}");1191 assert_snapshot!(v);1192 });1193 }1194}