M kabel/grammar.ebnf => kabel/grammar.ebnf +4 -2
@@ 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 ;
M kabel/src/error.rs => kabel/src/error.rs +11 -8
@@ 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,
M kabel/src/lexer.rs => kabel/src/lexer.rs +108 -20
@@ 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<u8>,
@@ 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),
}
M kabel/src/lib.rs => kabel/src/lib.rs +3 -3
@@ 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<Token>) -> (AST, Parser) {
+pub fn run_parser(text: String, input: Vec<Token>) -> (AST, Parser) {
let mut parser = Parser::new(text, input);
(parser.program(), parser)
}
M kabel/src/parser.rs => kabel/src/parser.rs +219 -36
@@ 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<Token>,
@@ 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<AST, KabelError> {
- let term = self.term()?;
- Ok(term)
+ let equality = self.equality()?;
+ Ok(equality)
+ }
+
+ pub fn equality(&mut self) -> Result<AST, KabelError> {
+ 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<AST, KabelError> {
+ 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<AST, KabelError> {
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<AST, KabelError> {
- 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<AST, KabelError> {
+ 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<AST, KabelError> {
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<AST, KabelError> {
@@ 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<Token, KabelError> {
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<Token, KabelError> {
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<AST>),
Binary(Box<AST>, BinOp, Box<AST>),
+ Unary(UnOp, Box<AST>),
Group(Box<AST>),
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,
}
M starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs => starkingdoms-client/vite.config.ts.timestamp-1722468930578-9a55b81119f46.mjs +18 -13
@@ 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=