From ea219ec91d4a7c415c267b1a33a2f4e74e5ab010 Mon Sep 17 00:00:00 2001 From: ghostlyzsh Date: Thu, 1 Aug 2024 01:45:21 -0500 Subject: [PATCH] equality, comparison, unary added --- kabel/grammar.ebnf | 6 +- kabel/src/error.rs | 19 +- kabel/src/lexer.rs | 128 +++++++-- kabel/src/lib.rs | 6 +- kabel/src/parser.rs | 255 +++++++++++++++--- ....timestamp-1722468930578-9a55b81119f46.mjs | 31 ++- 6 files changed, 363 insertions(+), 82 deletions(-) diff --git a/kabel/grammar.ebnf b/kabel/grammar.ebnf index e45e9faf665a51b38da0e4d1dc8504e892bd6b8c..7997b6c4dc4d09ba2455fee2632fba5b8a92041b 100644 --- a/kabel/grammar.ebnf +++ b/kabel/grammar.ebnf @@ -18,14 +18,16 @@ logical_or = logical_and { , "||" , logical_and } ; logical_and = equality { , "&&" , equality } ; +(* implemented *) equality = comparison { , ( "==" | "!=" ) , comparison } ; comparison = term { , ( ">" | "<" | ">=" | "<=" ) , term } ; -(* implemented *) term = factor { , ( "+" | "-" ) , factor } ; -factor = primary { , ( "*" | "/" ) , primary } ; +factor = unary { , ( "*" | "/" ) , unary } ; + +unary = ( ( "!" | "-" ) , unary ) | primary ; primary = identifier | number | string | group ; diff --git a/kabel/src/error.rs b/kabel/src/error.rs index 5e8d99327483b5fe55eedee8ed125a9f86f3e3b5..bdb5c184c3eae61648be954029fb7c8c880b3652 100644 --- a/kabel/src/error.rs +++ b/kabel/src/error.rs @@ -8,8 +8,7 @@ pub struct KabelError { } impl KabelError { - pub fn new(kind: ErrorKind, message: String, line: usize, - column: usize, code: String) -> Self { + pub fn new(kind: ErrorKind, message: String, line: usize, column: usize, code: String) -> Self { Self { kind, message, @@ -22,19 +21,23 @@ impl KabelError { impl std::fmt::Display for KabelError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let caret_space: String = vec![' '; self.column-1].iter().collect(); - f.write_str(& - format!("Error {:0>4}: {1} at line {2}, column {3}\n\ + let caret_space: String = vec![' '; self.column - 1].iter().collect(); + f.write_str(&format!( + "Error {:0>4}: {1} at line {2}, column {3}\n\ {4}\n\ {5}^", - self.kind.clone() as usize, self.message, self.line+1, self.column, - self.code, caret_space)) + self.kind.clone() as usize, + self.message, + self.line + 1, + self.column, + self.code, + caret_space + )) } } impl std::error::Error for KabelError {} - #[derive(Debug, Clone)] pub enum ErrorKind { UnexpectedEof, diff --git a/kabel/src/lexer.rs b/kabel/src/lexer.rs index 73663d0fe81da8bff4cf38399b404698366201ca..69d1bbf98e571cf19a7981a4a4aaea4f45f7c157 100644 --- a/kabel/src/lexer.rs +++ b/kabel/src/lexer.rs @@ -1,6 +1,9 @@ use std::str::from_utf8; -use crate::{error::{ErrorKind, KabelError}, token}; +use crate::{ + error::{ErrorKind, KabelError}, + token, +}; pub struct Lexer { input: Vec, @@ -32,19 +35,83 @@ impl Lexer { pub fn next_token(&mut self) -> bool { self.read_char(); match self.c { - b'+' => { self.output.push(token!(self, TokenType::Plus)); self.start = self.current; }, - b'-' => { self.output.push(token!(self, TokenType::Minus)); self.start = self.current; }, - b'*' => { self.output.push(token!(self, TokenType::Star)); self.start = self.current; }, - b'/' => { self.output.push(token!(self, TokenType::Slash)); self.start = self.current; }, - b'(' => { self.output.push(token!(self, TokenType::LeftParen)); self.start = self.current; }, - b')' => { self.output.push(token!(self, TokenType::RightParen)); self.start = self.current; }, + b'+' => { + self.output.push(token!(self, TokenType::Plus)); + self.start = self.current; + } + b'-' => { + self.output.push(token!(self, TokenType::Minus)); + self.start = self.current; + } + b'*' => { + self.output.push(token!(self, TokenType::Star)); + self.start = self.current; + } + b'/' => { + self.output.push(token!(self, TokenType::Slash)); + self.start = self.current; + } + b'(' => { + self.output.push(token!(self, TokenType::LeftParen)); + self.start = self.current; + } + b')' => { + self.output.push(token!(self, TokenType::RightParen)); + self.start = self.current; + } + b'=' => { + if self.peek() == b'=' { + self.read_char(); + self.output.push(token!(self, TokenType::EqualEqual)); + self.start = self.current; + } else { + self.output.push(token!(self, TokenType::Equal)); + self.start = self.current; + } + } + b'!' => { + if self.peek() == b'=' { + self.read_char(); + self.output.push(token!(self, TokenType::BangEqual)); + self.start = self.current; + } else { + self.output.push(token!(self, TokenType::Bang)); + self.start = self.current; + } + } + b'>' => { + if self.peek() == b'=' { + self.read_char(); + self.output.push(token!(self, TokenType::GreaterEqual)); + self.start = self.current; + } else { + self.output.push(token!(self, TokenType::Greater)); + self.start = self.current; + } + } + b'<' => { + if self.peek() == b'=' { + self.read_char(); + self.output.push(token!(self, TokenType::LessEqual)); + self.start = self.current; + } else { + self.output.push(token!(self, TokenType::Less)); + self.start = self.current; + } + } b'"' => { let mut contents = String::new(); while self.read_char() != b'"' { if self.c == 0x05 { - self.errors.push(KabelError::new(ErrorKind::UnexpectedEof, - "File ended before closing quote".to_string(), - self.line, self.column, from_utf8(&self.input[self.start..self.current]).unwrap().to_string())); + self.errors.push(KabelError::new( + ErrorKind::UnexpectedEof, + "File ended before closing quote".to_string(), + self.line, + self.column, + from_utf8(&self.input[self.start..self.current]) + .unwrap() + .to_string(), + )); return false; } contents.push(self.c as char); @@ -57,7 +124,9 @@ impl Lexer { self.line_start = self.current; self.column = 0; } - b' ' | b'\r' | b'\t' => { self.start = self.current; } + b' ' | b'\r' | b'\t' => { + self.start = self.current; + } 0x05 => return false, c => { if c.is_ascii_alphabetic() { @@ -79,16 +148,20 @@ impl Lexer { number.push(self.c as char); } } - /*self.current -= 1; - self.column -= 1;*/ // panic = error in this code - self.output.push(token!(self, TokenType::Num(number.parse().unwrap()))); + self.output + .push(token!(self, TokenType::Num(number.parse().unwrap()))); self.start = self.current; } else { - self.errors.push(KabelError::new(ErrorKind::UnexpectedToken, - format!("Stray \"{0}\"", c as char), - self.line, self.column, - from_utf8(&self.input[self.line_start..self.current]).unwrap().to_string())); + self.errors.push(KabelError::new( + ErrorKind::UnexpectedToken, + format!("Stray \"{0}\"", c as char), + self.line, + self.column, + from_utf8(&self.input[self.line_start..self.current]) + .unwrap() + .to_string(), + )); } } } @@ -127,7 +200,22 @@ pub struct Token { #[derive(Debug, Clone, PartialEq)] pub enum TokenType { - Star, Slash, Plus, Minus, LeftParen, RightParen, + Star, + Slash, + Plus, + Minus, + LeftParen, + RightParen, + Equal, + EqualEqual, + Bang, + BangEqual, + Greater, + GreaterEqual, + Less, + LessEqual, - Ident(String), Str(String), Num(f32), + Ident(String), + Str(String), + Num(f32), } diff --git a/kabel/src/lib.rs b/kabel/src/lib.rs index 7cd58e51f46a28f457c8cbb8abf02208aa424526..d0a2499d9796a457498d1c5b73a2f1fee4c0af5f 100644 --- a/kabel/src/lib.rs +++ b/kabel/src/lib.rs @@ -1,10 +1,10 @@ use lexer::{Lexer, Token}; use parser::{Parser, AST}; +pub mod error; pub mod lexer; -pub mod parser; pub mod macros; -pub mod error; +pub mod parser; pub fn run_lexer(input: String) -> Lexer { let mut lexer = Lexer::new(input); @@ -12,7 +12,7 @@ pub fn run_lexer(input: String) -> Lexer { lexer } -pub fn run_parser(text:String, input: Vec) -> (AST, Parser) { +pub fn run_parser(text: String, input: Vec) -> (AST, Parser) { let mut parser = Parser::new(text, input); (parser.program(), parser) } diff --git a/kabel/src/parser.rs b/kabel/src/parser.rs index 3749db901b6dc804a7aa75688af0137cee150263..e509643df9bbc299d2b3c0c0669b854a890d7f1b 100644 --- a/kabel/src/parser.rs +++ b/kabel/src/parser.rs @@ -1,4 +1,7 @@ -use crate::{error::{ErrorKind, KabelError}, lexer::{Token, TokenType}}; +use crate::{ + error::{ErrorKind, KabelError}, + lexer::{Token, TokenType}, +}; pub struct Parser { input: Vec, @@ -31,7 +34,6 @@ impl Parser { Ok(ast) => program.push(ast), Err(e) => self.errors.push(e), } - break; } AST { ast_type: ASTType::Program(program), @@ -43,21 +45,131 @@ impl Parser { } pub fn expression(&mut self) -> Result { - let term = self.term()?; - Ok(term) + let equality = self.equality()?; + Ok(equality) + } + + pub fn equality(&mut self) -> Result { + let mut left = self.comparison()?; + + while self.current < self.input.len() + && (self.peek()?.token_type == TokenType::EqualEqual + || self.peek()?.token_type == TokenType::BangEqual) + { + let binop = self.read_token()?; + let right = self.comparison()?; + if binop.token_type == TokenType::EqualEqual { + left = AST { + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Eq, + Box::new(right.clone()), + ), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } else { + left = AST { + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Ne, + Box::new(right.clone()), + ), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } + } + + Ok(left) + } + + pub fn comparison(&mut self) -> Result { + let mut left = self.term()?; + + while self.current < self.input.len() + && (self.peek()?.token_type == TokenType::Less + || self.peek()?.token_type == TokenType::LessEqual + || self.peek()?.token_type == TokenType::Greater + || self.peek()?.token_type == TokenType::GreaterEqual) + { + let binop = self.read_token()?; + let right = self.term()?; + if binop.token_type == TokenType::Less { + left = AST { + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Ls, + Box::new(right.clone()), + ), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } else if binop.token_type == TokenType::LessEqual { + left = AST { + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Le, + Box::new(right.clone()), + ), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } else if binop.token_type == TokenType::Greater { + left = AST { + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Gr, + Box::new(right.clone()), + ), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } else { + left = AST { + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Ge, + Box::new(right.clone()), + ), + start: left.start, + end: right.end, + line: left.line, + column: left.column, + }; + } + } + + Ok(left) } pub fn term(&mut self) -> Result { let mut left = self.factor()?; - while self.current < self.input.len() && - (self.peek()?.token_type == TokenType::Plus || self.peek()?.token_type == TokenType::Minus) { + while self.current < self.input.len() + && (self.peek()?.token_type == TokenType::Plus + || self.peek()?.token_type == TokenType::Minus) + { let binop = self.read_token()?; let right = self.factor()?; if binop.token_type == TokenType::Plus { left = AST { - ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Add, Box::new(right.clone())), + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Add, + Box::new(right.clone()), + ), start: left.start, end: right.end, line: left.line, @@ -65,7 +177,11 @@ impl Parser { }; } else { left = AST { - ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Sub, Box::new(right.clone())), + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Sub, + Box::new(right.clone()), + ), start: left.start, end: right.end, line: left.line, @@ -76,16 +192,22 @@ impl Parser { Ok(left) } pub fn factor(&mut self) -> Result { - let mut left = self.primary()?; + let mut left = self.unary()?; - while self.current < self.input.len() && - (self.peek()?.token_type == TokenType::Star || self.peek()?.token_type == TokenType::Slash) { + while self.current < self.input.len() + && (self.peek()?.token_type == TokenType::Star + || self.peek()?.token_type == TokenType::Slash) + { let binop = self.read_token()?; - let right = self.primary()?; + let right = self.unary()?; if binop.token_type == TokenType::Star { left = AST { - ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Mul, Box::new(right.clone())), + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Mul, + Box::new(right.clone()), + ), start: left.start, end: right.end, line: left.line, @@ -93,7 +215,11 @@ impl Parser { }; } else { left = AST { - ast_type: ASTType::Binary(Box::new(left.clone()), BinOp::Div, Box::new(right.clone())), + ast_type: ASTType::Binary( + Box::new(left.clone()), + BinOp::Div, + Box::new(right.clone()), + ), start: left.start, end: right.end, line: left.line, @@ -103,6 +229,31 @@ impl Parser { } Ok(left) } + pub fn unary(&mut self) -> Result { + if let TokenType::Bang | TokenType::Minus = self.peek()?.token_type { + let token = self.read_token()?; + let unary = self.unary()?; + if token.token_type == TokenType::Bang { + return Ok(AST { + ast_type: ASTType::Unary(UnOp::Not, Box::new(unary.clone())), + start: token.start, + end: unary.end, + line: token.line, + column: token.column, + }); + } else { + return Ok(AST { + ast_type: ASTType::Unary(UnOp::Neg, Box::new(unary.clone())), + start: token.start, + end: unary.end, + line: token.line, + column: token.column, + }); + } + } + + Ok(self.primary()?) + } pub fn primary(&mut self) -> Result { let token = self.read_token()?; @@ -138,11 +289,17 @@ impl Parser { return Ok(self.group(token)?); } _ => { - return Err(KabelError::new(ErrorKind::UnexpectedToken, - format!("Unexpected token {}", self.text[token.start..token.end].to_string()), - token.line, token.column, - self.text[token.line_start..token.end].to_string())); - }, + return Err(KabelError::new( + ErrorKind::UnexpectedToken, + format!( + "Unexpected token {}", + self.text[token.start..token.end].to_string() + ), + token.line, + token.column, + self.text[token.line_start..token.end].to_string(), + )); + } } } pub fn group(&mut self, left_paren: Token) -> Result { @@ -150,10 +307,13 @@ impl Parser { let right_paren = self.peek(); if let Ok(right_paren) = right_paren { if right_paren.token_type != TokenType::RightParen { - return Err(KabelError::new(ErrorKind::MissingDelimiter, - "Missing right parenthesis".to_string(), - right_paren.line, right_paren.column, - self.text[left_paren.start..right_paren.end].to_string())); + return Err(KabelError::new( + ErrorKind::MissingDelimiter, + "Missing right parenthesis".to_string(), + right_paren.line, + right_paren.column, + self.text[left_paren.start..right_paren.end].to_string(), + )); } self.read_token()?; return Ok(AST { @@ -165,21 +325,27 @@ impl Parser { }); } if let Err(e) = right_paren { - return Err(KabelError::new(ErrorKind::MissingDelimiter, - "Missing right parenthesis".to_string(), - e.line, e.column, - self.text[left_paren.line_start..expr.end].to_string())); + return Err(KabelError::new( + ErrorKind::MissingDelimiter, + "Missing right parenthesis".to_string(), + e.line, + e.column, + self.text[left_paren.line_start..expr.end].to_string(), + )); } unreachable!(); } - pub fn read_token(&mut self) -> Result { if self.current >= self.input.len() { - let last_token = self.input[self.input.len()-1].clone(); - return Err(KabelError::new(ErrorKind::UnexpectedEof, - "Unexpected end of file".to_string(), last_token.line, last_token.column, - self.text[last_token.line_start..last_token.end].to_string())); + let last_token = self.input[self.input.len() - 1].clone(); + return Err(KabelError::new( + ErrorKind::UnexpectedEof, + "Unexpected end of file".to_string(), + last_token.line, + last_token.column, + self.text[last_token.line_start..last_token.end].to_string(), + )); } self.token = self.input[self.current].clone(); self.current += 1; @@ -187,10 +353,14 @@ impl Parser { } pub fn peek(&mut self) -> Result { if self.current >= self.input.len() { - let last_token = self.input[self.input.len()-1].clone(); - return Err(KabelError::new(ErrorKind::UnexpectedEof, - "Unexpected end of file".to_string(), last_token.line, last_token.column, - self.text[last_token.line_start..last_token.end].to_string())); + let last_token = self.input[self.input.len() - 1].clone(); + return Err(KabelError::new( + ErrorKind::UnexpectedEof, + "Unexpected end of file".to_string(), + last_token.line, + last_token.column, + self.text[last_token.line_start..last_token.end].to_string(), + )); } return Ok(self.input[self.current].clone()); } @@ -210,6 +380,7 @@ pub enum ASTType { Program(Vec), Binary(Box, BinOp, Box), + Unary(UnOp, Box), Group(Box), Ident(String), @@ -223,4 +394,16 @@ pub enum BinOp { Sub, Mul, Div, + Eq, + Ne, + Gr, + Ge, + Ls, + Le, +} + +#[derive(Debug, Clone, Copy)] +pub enum UnOp { + Not, + Neg, } diff --git a/starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs b/starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs index 4e1007b478aa25042b825615d80068891527efb8..4a1eedc2e2e739c2dd63e2f2ffa3783c46cce8c7 100644 --- a/starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs +++ b/starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs @@ -4,13 +4,17 @@ import { resolve } from "path"; import * as child from "child_process"; import autoprefixer from "file:///home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client/node_modules/autoprefixer/lib/autoprefixer.js"; import { svelte } from "file:///home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client/node_modules/@sveltejs/vite-plugin-svelte/src/index.js"; -var __vite_injected_original_dirname = "/home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client"; -var commitHash = child.execSync("git describe --no-match --always --abbrev=8 --dirty").toString().trim(); +var __vite_injected_original_dirname = + "/home/ghostlyzsh/dev/starkingdoms.tk/starkingdoms-client"; +var commitHash = child + .execSync("git describe --no-match --always --abbrev=8 --dirty") + .toString() + .trim(); var vite_config_default = defineConfig({ plugins: [svelte()], define: { APP_VERSION: JSON.stringify(process.env.npm_package_version), - COMMIT_HASH: JSON.stringify(commitHash) + COMMIT_HASH: JSON.stringify(commitHash), }, build: { target: ["chrome89", "edge89", "firefox89", "safari15"], @@ -21,19 +25,20 @@ var vite_config_default = defineConfig({ play: resolve(__vite_injected_original_dirname, "play/index.html"), signup: resolve(__vite_injected_original_dirname, "signup/index.html"), login: resolve(__vite_injected_original_dirname, "login/index.html"), - shipeditor: resolve(__vite_injected_original_dirname, "shipeditor/index.html"), - uikit: resolve(__vite_injected_original_dirname, "uikit/index.html") - } - } + shipeditor: resolve( + __vite_injected_original_dirname, + "shipeditor/index.html", + ), + uikit: resolve(__vite_injected_original_dirname, "uikit/index.html"), + }, + }, }, appType: "mpa", css: { postcss: { - plugins: [autoprefixer({})] - } - } + plugins: [autoprefixer({})], + }, + }, }); -export { - vite_config_default as default -}; +export { vite_config_default as default }; //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS9naG9zdGx5enNoL2Rldi9zdGFya2luZ2RvbXMudGsvc3Rhcmtpbmdkb21zLWNsaWVudFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL2hvbWUvZ2hvc3RseXpzaC9kZXYvc3Rhcmtpbmdkb21zLnRrL3N0YXJraW5nZG9tcy1jbGllbnQvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL2hvbWUvZ2hvc3RseXpzaC9kZXYvc3Rhcmtpbmdkb21zLnRrL3N0YXJraW5nZG9tcy1jbGllbnQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tIFwidml0ZVwiO1xuaW1wb3J0IHsgcmVzb2x2ZSB9IGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgKiBhcyBjaGlsZCBmcm9tIFwiY2hpbGRfcHJvY2Vzc1wiO1xuLy9AdHMtaWdub3JlXG5pbXBvcnQgYXV0b3ByZWZpeGVyIGZyb20gXCJhdXRvcHJlZml4ZXJcIjtcbmltcG9ydCB7IHN2ZWx0ZSB9IGZyb20gXCJAc3ZlbHRlanMvdml0ZS1wbHVnaW4tc3ZlbHRlXCI7XG5cbmNvbnN0IGNvbW1pdEhhc2ggPSBjaGlsZFxuICAuZXhlY1N5bmMoXCJnaXQgZGVzY3JpYmUgLS1uby1tYXRjaCAtLWFsd2F5cyAtLWFiYnJldj04IC0tZGlydHlcIilcbiAgLnRvU3RyaW5nKClcbiAgLnRyaW0oKTtcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW3N2ZWx0ZSgpXSxcbiAgZGVmaW5lOiB7XG4gICAgQVBQX1ZFUlNJT046IEpTT04uc3RyaW5naWZ5KHByb2Nlc3MuZW52Lm5wbV9wYWNrYWdlX3ZlcnNpb24pLFxuICAgIENPTU1JVF9IQVNIOiBKU09OLnN0cmluZ2lmeShjb21taXRIYXNoKSxcbiAgfSxcbiAgYnVpbGQ6IHtcbiAgICB0YXJnZXQ6IFtcImNocm9tZTg5XCIsIFwiZWRnZTg5XCIsIFwiZmlyZWZveDg5XCIsIFwic2FmYXJpMTVcIl0sXG4gICAgY3NzQ29kZVNwbGl0OiBmYWxzZSxcbiAgICByb2xsdXBPcHRpb25zOiB7XG4gICAgICBpbnB1dDoge1xuICAgICAgICBtYWluOiByZXNvbHZlKF9fZGlybmFtZSwgXCJpbmRleC5odG1sXCIpLFxuICAgICAgICBwbGF5OiByZXNvbHZlKF9fZGlybmFtZSwgXCJwbGF5L2luZGV4Lmh0bWxcIiksXG4gICAgICAgIHNpZ251cDogcmVzb2x2ZShfX2Rpcm5hbWUsIFwic2lnbnVwL2luZGV4Lmh0bWxcIiksXG4gICAgICAgIGxvZ2luOiByZXNvbHZlKF9fZGlybmFtZSwgXCJsb2dpbi9pbmRleC5odG1sXCIpLFxuICAgICAgICBzaGlwZWRpdG9yOiByZXNvbHZlKF9fZGlybmFtZSwgXCJzaGlwZWRpdG9yL2luZGV4Lmh0bWxcIiksXG4gICAgICAgIHVpa2l0OiByZXNvbHZlKF9fZGlybmFtZSwgXCJ1aWtpdC9pbmRleC5odG1sXCIpLFxuICAgICAgfSxcbiAgICB9LFxuICB9LFxuICBhcHBUeXBlOiBcIm1wYVwiLFxuICBjc3M6IHtcbiAgICBwb3N0Y3NzOiB7XG4gICAgICBwbHVnaW5zOiBbYXV0b3ByZWZpeGVyKHt9KV0sXG4gICAgfSxcbiAgfSxcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUEwVixTQUFTLG9CQUFvQjtBQUN2WCxTQUFTLGVBQWU7QUFDeEIsWUFBWSxXQUFXO0FBRXZCLE9BQU8sa0JBQWtCO0FBQ3pCLFNBQVMsY0FBYztBQUx2QixJQUFNLG1DQUFtQztBQU96QyxJQUFNLGFBQ0gsZUFBUyxxREFBcUQsRUFDOUQsU0FBUyxFQUNULEtBQUs7QUFFUixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTLENBQUMsT0FBTyxDQUFDO0FBQUEsRUFDbEIsUUFBUTtBQUFBLElBQ04sYUFBYSxLQUFLLFVBQVUsUUFBUSxJQUFJLG1CQUFtQjtBQUFBLElBQzNELGFBQWEsS0FBSyxVQUFVLFVBQVU7QUFBQSxFQUN4QztBQUFBLEVBQ0EsT0FBTztBQUFBLElBQ0wsUUFBUSxDQUFDLFlBQVksVUFBVSxhQUFhLFVBQVU7QUFBQSxJQUN0RCxjQUFjO0FBQUEsSUFDZCxlQUFlO0FBQUEsTUFDYixPQUFPO0FBQUEsUUFDTCxNQUFNLFFBQVEsa0NBQVcsWUFBWTtBQUFBLFFBQ3JDLE1BQU0sUUFBUSxrQ0FBVyxpQkFBaUI7QUFBQSxRQUMxQyxRQUFRLFFBQVEsa0NBQVcsbUJBQW1CO0FBQUEsUUFDOUMsT0FBTyxRQUFRLGtDQUFXLGtCQUFrQjtBQUFBLFFBQzVDLFlBQVksUUFBUSxrQ0FBVyx1QkFBdUI7QUFBQSxRQUN0RCxPQUFPLFFBQVEsa0NBQVcsa0JBQWtCO0FBQUEsTUFDOUM7QUFBQSxJQUNGO0FBQUEsRUFDRjtBQUFBLEVBQ0EsU0FBUztBQUFBLEVBQ1QsS0FBSztBQUFBLElBQ0gsU0FBUztBQUFBLE1BQ1AsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7QUFBQSxJQUM1QjtBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=