Berry added f-strings as an alternative to string formatting (#18937)

This commit is contained in:
s-hadinger 2023-06-22 22:50:05 +02:00 committed by GitHub
parent 558f812ec2
commit 85f357096a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 817 additions and 609 deletions

View File

@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file.
- Berry `string.format()` now automatically converts type according to format
- Matter add friendly-name (NodeLabel) to each endpoint
- Berry add global function `format` as a simpler syntax to `string.format`
- Berry added f-strings as an alternative to string formatting
### Breaking Changed

View File

@ -137,7 +137,7 @@ void be_print_inst(binstruction ins, int pc, void* fout)
logbuf("%s", opc2str(op));
break;
}
memcpy(__lbuf_tmp, __lbuf, strlen(__lbuf)+1);
memcpy(__lbuf_tmp, __lbuf, strlen(__lbuf) + 1);
logbuf("%s\n", __lbuf_tmp);
if (fout) {
be_fwrite(fout, __lbuf, strlen(__lbuf));
@ -184,7 +184,7 @@ static void sourceinfo(bproto *proto, binstruction *ip)
blineinfo *end = it + proto->nlineinfo;
int pc = cast_int(ip - proto->code - 1); /* now vm->ip has been increased */
for (; it < end && pc > it->endpc; ++it);
sprintf(buf, ":%d:", it->linenumber);
snprintf(buf, sizeof(buf), ":%d:", it->linenumber);
be_writestring(str(proto->source));
be_writestring(buf);
} else {

View File

@ -183,8 +183,9 @@ int be_protectedparser(bvm *vm,
return res;
}
static const char* _sgets(void *data, size_t *size)
static const char* _sgets(struct blexer* lexer, void *data, size_t *size)
{
(void)lexer;
struct strbuf *sb = data;
*size = sb->len;
if (sb->len) {
@ -194,8 +195,9 @@ static const char* _sgets(void *data, size_t *size)
return NULL;
}
static const char* _fgets(void *data, size_t *size)
static const char* _fgets(struct blexer* lexer, void *data, size_t *size)
{
(void)lexer;
struct filebuf *fb = data;
*size = be_fread(fb->fp, fb->buf, sizeof(fb->buf));
if (*size) {

View File

@ -13,14 +13,15 @@
#include "be_map.h"
#include "be_vm.h"
#include "be_strlib.h"
#include <string.h>
#define SHORT_STR_LEN 32
#define EOS '\0' /* end of source */
#define type_count() (int)array_count(kwords_tab)
#define lexbuf(lex) ((lex)->buf.s)
#define isvalid(lex) ((lex)->cursor < (lex)->endbuf)
#define lgetc(lex) ((lex)->cursor)
#define isvalid(lex) ((lex)->reader.cursor < (lex)->endbuf)
#define lgetc(lex) ((lex)->reader.cursor)
#define setstr(lex, v) ((lex)->token.u.s = (v))
#define setint(lex, v) ((lex)->token.u.i = (v))
#define setreal(lex, v) ((lex)->token.u.r = (v))
@ -39,7 +40,8 @@ static const char* const kwords_tab[] = {
":", "?", "->", "if", "elif", "else", "while",
"for", "def", "end", "class", "break", "continue",
"return", "true", "false", "nil", "var", "do",
"import", "as", "try", "except", "raise", "static"
"import", "as", "try", "except", "raise", "static",
// ".f"
};
void be_lexerror(blexer *lexer, const char *msg)
@ -102,12 +104,12 @@ static int next(blexer *lexer)
struct blexerreader *lr = &lexer->reader;
if (!(lr->len--)) {
static const char eos = EOS;
const char *s = lr->readf(lr->data, &lr->len);
const char *s = lr->readf(lexer, lr->data, &lr->len);
lr->s = s ? s : &eos;
--lr->len;
}
lexer->cursor = *lr->s++;
return lexer->cursor;
lexer->reader.cursor = *lr->s++;
return lexer->reader.cursor;
}
static void clear_buf(blexer *lexer)
@ -115,10 +117,7 @@ static void clear_buf(blexer *lexer)
lexer->buf.len = 0;
}
/* save and next */
static int save(blexer *lexer)
{
int ch = lgetc(lexer);
static void save_char(blexer *lexer, int ch) {
struct blexerbuf *buf = &lexer->buf;
if (buf->len >= buf->size) {
size_t size = buf->size << 1;
@ -126,6 +125,13 @@ static int save(blexer *lexer)
buf->size = size;
}
buf->s[buf->len++] = (char)ch;
}
/* save and next */
static int save(blexer *lexer)
{
int ch = lgetc(lexer);
save_char(lexer, ch);
return next(lexer);
}
@ -247,7 +253,7 @@ static int skip_newline(blexer *lexer)
next(lexer); /* skip "\n\r" or "\r\n" */
}
lexer->linenumber++;
return lexer->cursor;
return lexer->reader.cursor;
}
static void skip_comment(blexer *lexer)
@ -373,12 +379,217 @@ static btokentype scan_numeral(blexer *lexer)
return type;
}
/* structure for a temporary reader used by transpiler, with attributes for an allocated buffer and size */
struct blexerreader_save {
struct blexerreader reader;
char* s;
size_t size;
char cursor;
};
/* buf reader for transpiled code from f-strings */
/* it restores the original reader when the transpiler buffer is empty */
/* the first pass returns a single byte buffer with the saved cursor */
/* the second pass restores the original reader */
static const char* _bufgets(struct blexer* lexer, void *data, size_t *size)
{
/* this is called once the temporaty transpiler buffer is empty */
struct blexerreader *reader = &lexer->reader; /* current reader which is temporary only for the transpiler */
struct blexerreader_save *reader_save = data; /* original reader that needs to be restored once the buffer is empty */
/* first case, we saved the cursor (fist char), we server it now */
if (reader_save->reader.cursor >= 0) { /* serve the previously saved cursor */
/* copy cursor to a 'char' type */
reader_save->cursor = reader_save->reader.cursor;
reader_save->reader.cursor = -1; /* no more cursor saved */
*size = 1;
return &reader_save->cursor;
}
/* second case, the saved cursor was returned, now restore the normal flow of the original reader */
/* restore the original reader */
*reader = reader_save->reader;
/* free the memory from the structure */
be_free(lexer->vm, reader_save->s, reader_save->size); /* free the buffer */
be_free(lexer->vm, reader_save, sizeof(struct blexerreader_save)); /* free the structure */
if (!reader->len) { /* just in case the original buffer was also */
return reader->readf(lexer, reader->data, size);
}
/* the following is not necessary, but safer */
*size = reader->len;
return reader->s;
}
static btokentype scan_string(blexer *lexer); /* forward declaration */
/* scan f-string and transpile it to `format(...)` syntax then feeding the normal lexer and parser */
static void scan_f_string(blexer *lexer)
{
char ch;
clear_buf(lexer);
scan_string(lexer); /* first scan the entire string in lexer->buf */
/* save original reader until the transpiled is processed */
/* reader will be restored by the reader function once the transpiled buffer is empty */
struct blexerreader_save *reader_save = (struct blexerreader_save *) be_malloc(lexer->vm, sizeof(struct blexerreader_save)); /* temporary reader */
reader_save->reader = lexer->reader;
/* save blexerbuf which contains the unparsed_fstring */
struct blexerbuf buf_unparsed_fstr = lexer->buf;
/* prepare and allocated a temporary buffer to save parsed f_string */
lexer->buf.size = lexer->buf.size + 20;
lexer->buf.s = be_malloc(lexer->vm, lexer->buf.size);
lexer->buf.len = 0;
/* parse f_string */
/* First pass, check syntax and extract string literals, and format */
save_char(lexer, '(');
save_char(lexer, '"');
for (size_t i = 0; i < buf_unparsed_fstr.len; i++) {
ch = buf_unparsed_fstr.s[i];
switch (ch) {
case '%': /* % needs to be encoded as %% */
save_char(lexer, '%');
save_char(lexer, '%');
break;
case '\\': /* \ needs to be encoded as \\ */
save_char(lexer, '\\');
save_char(lexer, '\\');
break;
case '"': /* " needs to be encoded as \" */
save_char(lexer, '\\');
save_char(lexer, '"');
break;
case '}': /* }} is converted as } yet we tolerate a single } */
if ((i+1 < buf_unparsed_fstr.len) && (buf_unparsed_fstr.s[i+1] == '}')) { i++; } /* if '}}' replace with '}' */
save_char(lexer, '}');
break;
default: /* copy any other character */
save_char(lexer, ch);
break;
case '{': /* special case for { */
i++; /* in all cases skip to next char */
if ((i < buf_unparsed_fstr.len) && (buf_unparsed_fstr.s[i] == '{')) {
save_char(lexer, '{'); /* {{ is simply encoded as { and continue parsing */
} else {
/* we still don't know if '=' is present, so we copy the expression each time, and rollback if we find out the '=' is not present */
size_t rollback = lexer->buf.len; /* mark the end of string for later rollback if '=' is not present */
/* parse inner part */
/* skip everything until either ':' or '}' or '=' */
/* if end of string is reached before '}' raise en error */
for (; i < buf_unparsed_fstr.len; i++) {
ch = buf_unparsed_fstr.s[i];
if (ch == ':' || ch == '}') { break; }
save_char(lexer, ch); /* copy any character unless it's ':' or '}' */
if (ch == '=') { break; } /* '=' is copied but breaks parsing as well */
}
/* safe check if we reached the end of the string */
if (i >= buf_unparsed_fstr.len) { be_raise(lexer->vm, "syntax_error", "'}' expected"); }
/* if '=' detected then do some additional checks */
if (ch == '=') {
i++; /* skip '=' and check we haven't reached the end */
if (i >= buf_unparsed_fstr.len) { be_raise(lexer->vm, "syntax_error", "'}' expected"); }
ch = buf_unparsed_fstr.s[i];
if ((ch != ':') && (ch != '}')) { /* '=' must be immediately followed by ':' or '}' */
be_raise(lexer->vm, "syntax_error", "':' or '}' expected after '='");
}
} else {
/* no '=' present, rollback the text of the expression */
lexer->buf.len = rollback;
}
save_char(lexer, '%'); /* start format encoding */
if (ch == ':') {
/* copy format */
i++;
if ((i < buf_unparsed_fstr.len) && (buf_unparsed_fstr.s[i] == '%')) { i++; } /* skip '%' following ':' */
for (; i < buf_unparsed_fstr.len; i++) {
ch = buf_unparsed_fstr.s[i];
if (ch == '}') { break; }
save_char(lexer, ch);
}
if (i >= buf_unparsed_fstr.len) { be_raise(lexer->vm, "syntax_error", "'}' expected"); }
} else {
/* if no formatting, output '%s' */
save_char(lexer, 's');
}
}
break;
}
}
save_char(lexer, '"'); /* finish format string */
/* Second pass - add arguments if any */
for (size_t i = 0; i < buf_unparsed_fstr.len; i++) {
/* skip any character that is not '{' followed by '{' */
if (buf_unparsed_fstr.s[i] == '{') {
i++; /* in all cases skip to next char */
if ((i < buf_unparsed_fstr.len) && (buf_unparsed_fstr.s[i] == '{')) { continue; }
/* extract argument */
save_char(lexer, ','); /* add ',' to start next argument to `format()` */
for (; i < buf_unparsed_fstr.len; i++) {
ch = buf_unparsed_fstr.s[i];
if (ch == '=' || ch == ':' || ch == '}') { break; }
save_char(lexer, ch); /* copy expression until we reach ':', '=' or '}' */
}
/* no need to check for end of string here, it was done already in first pass */
if (ch == ':' || ch == '=') { /* if '=' or ':', skip everyting until '}' */
i++;
for (; i < buf_unparsed_fstr.len; i++) {
ch = buf_unparsed_fstr.s[i];
if (ch == '}') { break; }
}
}
}
}
save_char(lexer, ')'); /* add final ')' */
/* Situation now: */
/* `buf_unparsed_fstr` contains the buffer of the input unparsed f-string, ex: "age: {age:2i}" */
/* `lexer->buf` contains the buffer of the transpiled f-string without call to format(), ex: '("age: %2i", age)' */
/* `reader_save` contains the original reader to continue parsing after f-string */
/* `lexer->reader` will contain a temporary reader from the parsed f-string */
/* extract the parsed f-string from the temporary buffer (needs later deallocation) */
char * parsed_fstr_s = lexer->buf.s; /* needs later deallocation with parsed_fstr_size */
size_t parsed_fstr_len = lexer->buf.len;
size_t parsed_fstr_size = lexer->buf.size;
/* restore buf to lexer */
lexer->buf = buf_unparsed_fstr;
/* change the temporary reader to the parsed f-string */
lexer->reader.len = parsed_fstr_len;
lexer->reader.data = (void*) reader_save; /* link to the saved context */
lexer->reader.s = parsed_fstr_s; /* reader is responisble to deallocate later this buffer */
lexer->reader.readf = _bufgets;
/* add information needed for `_bufgets` to later deallocate the buffer */
reader_save->size = parsed_fstr_size;
reader_save->s = parsed_fstr_s;
/* start parsing the parsed f-string, which is btw always '(' */
next(lexer);
/* remember that we are still in `scan_identifier()`, we replace the 'f' identifier to 'format' which is the global function to call */
static const char FORMAT[] = "format";
lexer->buf.len = sizeof(FORMAT) - 1; /* we now that buf size is at least SHORT_STR_LEN (32) */
memmove(lexer->buf.s, FORMAT, lexer->buf.len);
}
static btokentype scan_identifier(blexer *lexer)
{
int type;
bstring *s;
save(lexer);
match(lexer, is_word);
/* check if the form is f"aaaa" or f'aaa' */
char ch = lgetc(lexer);
if ((lexer->buf.len == 1) && (lexer->buf.s[0] == 'f') && (ch == '"' || ch == '\'')) {
scan_f_string(lexer);
}
s = buf_tostr(lexer);
type = str_extra(s);
if (type >= KeyIf && type < type_count()) {

View File

@ -97,6 +97,7 @@ struct blexerreader {
size_t len;
void *data;
breader readf;
int cursor;
};
struct blexerbuf {
@ -123,7 +124,6 @@ typedef struct blexer {
struct blexerreader reader;
bmap *strtab;
bvm *vm;
int cursor;
} blexer;
void be_lexer_init(blexer *lexer, bvm *vm,

View File

@ -195,7 +195,8 @@ typedef struct {
bntvfunc destroy;
} bcommomobj;
typedef const char* (*breader)(void*, size_t*);
struct blexer;
typedef const char* (*breader)(struct blexer*, void*, size_t*);
#define cast(_T, _v) ((_T)(_v))
#define cast_int(_v) cast(int, _v)

View File

@ -52,22 +52,22 @@ bstring* be_num2str(bvm *vm, bvalue *v)
{
char buf[25];
if (var_isint(v)) {
sprintf(buf, BE_INT_FORMAT, var_toint(v));
snprintf(buf, sizeof(buf),BE_INT_FORMAT, var_toint(v));
} else if (var_isreal(v)) {
sprintf(buf, "%g", var_toreal(v));
snprintf(buf, sizeof(buf), "%g", var_toreal(v));
} else {
sprintf(buf, "(nan)");
snprintf(buf, sizeof(buf), "(nan)");
}
return be_newstr(vm, buf);
}
static void module2str(char *buf, bvalue *v)
static void module2str(char *buf, size_t buf_len, bvalue *v)
{
const char *name = be_module_name(cast(bmodule*, var_toobj(v)));
if (name) {
sprintf(buf, "<module: %s>", name);
snprintf(buf, buf_len, "<module: %s>", name);
} else {
sprintf(buf, "<module: %p>", var_toobj(v));
snprintf(buf, buf_len, "<module: %p>", var_toobj(v));
}
}
@ -83,26 +83,26 @@ static bstring* sim2str(bvm *vm, bvalue *v)
break;
case BE_INDEX:
case BE_INT:
sprintf(sbuf, BE_INT_FORMAT, var_toint(v));
snprintf(sbuf, sizeof(sbuf), BE_INT_FORMAT, var_toint(v));
break;
case BE_REAL:
sprintf(sbuf, "%g", var_toreal(v));
snprintf(sbuf, sizeof(sbuf), "%g", var_toreal(v));
break;
case BE_CLOSURE: case BE_NTVCLOS: case BE_NTVFUNC: case BE_CTYPE_FUNC:
sprintf(sbuf, "<function: %p>", var_toobj(v));
snprintf(sbuf, sizeof(sbuf), "<function: %p>", var_toobj(v));
break;
case BE_CLASS:
sprintf(sbuf, "<class: %s>",
snprintf(sbuf, sizeof(sbuf), "<class: %s>",
str(be_class_name(cast(bclass*, var_toobj(v)))));
break;
case BE_MODULE:
module2str(sbuf, v);
module2str(sbuf, sizeof(sbuf), v);
break;
case BE_COMPTR:
sprintf(sbuf, "<ptr: %p>", var_toobj(v));
snprintf(sbuf, sizeof(sbuf), "<ptr: %p>", var_toobj(v));
break;
default:
strcpy(sbuf, "(unknown value)");
strncpy(sbuf, "(unknown value)", sizeof(sbuf));
break;
}
return be_newstr(vm, sbuf);
@ -117,8 +117,10 @@ static bstring* ins2str(bvm *vm, int idx)
be_incrtop(vm); /* push the obj::tostring to stack */
if (basetype(type) != BE_FUNCTION) {
bstring *name = be_class_name(be_instance_class(obj));
char *sbuf = be_malloc(vm, (size_t)str_len(name) + 16);
sprintf(sbuf, "<instance: %s()>", str(name));
size_t buf_len = (size_t) str_len(name) + 16;
char *sbuf = be_malloc(vm, buf_len);
/* TODO what if sbuf cannot be allocated */
snprintf(sbuf, buf_len, "<instance: %s()>", str(name));
be_stackpop(vm, 1); /* pop the obj::tostring */
s = be_newstr(vm, sbuf);
be_free(vm, sbuf, (size_t)str_len(name) + 16);
@ -217,7 +219,7 @@ const char* be_pushvfstr(bvm *vm, const char *format, va_list arg)
}
case 'p': {
char buf[2 * sizeof(void*) + 4];
sprintf(buf, "%p", va_arg(arg, void*));
snprintf(buf, sizeof(buf), "%p", va_arg(arg, void*));
pushstr(vm, buf, strlen(buf));
break;
}
@ -632,7 +634,7 @@ int be_str_format(bvm *vm)
bint val;
if (convert_to_int(vm, index, &val)) {
mode_fixlen(mode, BE_INT_FMTLEN);
sprintf(buf, mode, val);
snprintf(buf, sizeof(buf), mode, val);
}
be_pushstring(vm, buf);
break;
@ -642,7 +644,7 @@ int be_str_format(bvm *vm)
{
breal val;
if (convert_to_real(vm, index, &val)) {
sprintf(buf, mode, val);
snprintf(buf, sizeof(buf), mode, val);
}
be_pushstring(vm, buf);
break;
@ -651,7 +653,7 @@ int be_str_format(bvm *vm)
{
bint val;
if (convert_to_int(vm, index, &val)) {
sprintf(buf, "%c", (int)val);
snprintf(buf, sizeof(buf), "%c", (int)val);
}
be_pushstring(vm, buf);
break;
@ -662,7 +664,7 @@ int be_str_format(bvm *vm)
if (len > 100 && strlen(mode) == 2) {
be_pushvalue(vm, index);
} else {
sprintf(buf, mode, s);
snprintf(buf, sizeof(buf), mode, s);
be_pushstring(vm, buf);
}
break;
@ -810,10 +812,10 @@ static int str_i2hex(bvm *vm)
if (top >= 2 && be_isint(vm, 2)) {
bint num = be_toint(vm, 2);
if (num > 0 && num <= 16) {
sprintf(fmt, "%%.%d" BE_INT_FMTLEN "X", (int)num);
snprintf(fmt, sizeof(fmt), "%%.%d" BE_INT_FMTLEN "X", (int)num);
}
}
sprintf(buf, fmt, value);
snprintf(buf, sizeof(buf), fmt, value);
be_pushstring(vm, buf);
be_return(vm);
}

View File

@ -89,7 +89,6 @@ class Matter_UI
#- ---------------------------------------------------------------------- -#
def show_enable()
import webserver
import string
var matter_enabled = self.matter_enabled
webserver.content_send("<fieldset><legend><b>&nbsp;Matter &nbsp;</b></legend>"
@ -97,12 +96,14 @@ class Matter_UI
"<form action='/matterc' method='post'>")
# checkbox for Matter enable
webserver.content_send(string.format("<p><input id='menable' type='checkbox' name='menable' %s>", self.matter_enabled() ? "checked" : ""))
var matter_enabled_checked = self.matter_enabled() ? 'checked' : ''
webserver.content_send(f"<p><input id='menable' type='checkbox' name='menable' {matter_enabled_checked}>")
webserver.content_send("<label for='menable'><b>Matter enable</b></label></p>")
if self.matter_enabled()
# checkbox for Matter commissioning
webserver.content_send(string.format("<p><input id='comm' type='checkbox' name='comm' %s>", self.device.commissioning_open != nil ? "checked" : ""))
var commissioning_open_checked = self.device.commissioning_open != nil ? "checked" : ""
webserver.content_send(f"<p><input id='comm' type='checkbox' name='comm' {commissioning_open_checked}>")
webserver.content_send("<label for='comm'><b>Commissioning open</b></label></p>")
end
@ -163,21 +164,20 @@ class Matter_UI
#- ---------------------------------------------------------------------- -#
def show_commissioning_info()
import webserver
import string
var seconds_left = (self.device.commissioning_open - tasmota.millis()) / 1000
if seconds_left < 0 seconds_left = 0 end
var min_left = (seconds_left + 30) / 60
webserver.content_send(string.format("<fieldset><legend><b>&nbsp;Commissioning open for %i min&nbsp;</b></legend><p></p>", min_left))
webserver.content_send(f"<fieldset><legend><b>&nbsp;Commissioning open for {min_left:i} min&nbsp;</b></legend><p></p>")
var pairing_code = self.device.compute_manual_pairing_code()
webserver.content_send(string.format("<p>Manual pairing code:<br><b>%s-%s-%s</b></p><hr>", pairing_code[0..3], pairing_code[4..6], pairing_code[7..]))
webserver.content_send(f"<p>Manual pairing code:<br><b>{pairing_code[0..3]}-{pairing_code[4..6]}-{pairing_code[7..]}</b></p><hr>")
webserver.content_send("<div><center>")
var qr_text = self.device.compute_qrcode_content()
self.show_qrcode(qr_text)
webserver.content_send(string.format("<p> %s</p>", qr_text))
webserver.content_send(f"<p> {qr_text}</p>")
webserver.content_send("</div><p></p></fieldset><p></p>")
end
@ -187,16 +187,16 @@ class Matter_UI
#- ---------------------------------------------------------------------- -#
def show_passcode_form()
import webserver
import string
webserver.content_send("<fieldset><legend><b>&nbsp;Matter Advanced Configuration&nbsp;</b></legend><p></p>")
#
webserver.content_send("<form action='/matterc' method='post' onsubmit='return confirm(\"This will cause a restart.\");'>"
"<p>Passcode:</p>")
webserver.content_send(string.format("<input type='number' min='1' max='99999998' name='passcode' value='%i'>", self.device.root_passcode))
webserver.content_send(f"<input type='number' min='1' max='99999998' name='passcode' value='{self.device.root_passcode:i}'>")
webserver.content_send("<p>Distinguish id:</p>")
webserver.content_send(string.format("<input type='number' min='0' max='4095' name='discriminator' value='%i'>", self.device.root_discriminator))
webserver.content_send(string.format("<p><input type='checkbox' name='ipv4'%s>IPv4 only</p>", self.device.ipv4only ? " checked" : ""))
webserver.content_send(f"<input type='number' min='0' max='4095' name='discriminator' value='{self.device.root_discriminator:i}'>")
var ipv4only_checked = self.device.ipv4only ? " checked" : ""
webserver.content_send(f"<p><input type='checkbox' name='ipv4'{ipv4only_checked}>IPv4 only</p>")
webserver.content_send("<p></p><button name='passcode' class='button bgrn'>Change</button></form></p>"
"<p></p></fieldset><p></p>")
@ -224,15 +224,15 @@ class Matter_UI
if !label label = "<No label>" end
label = webserver.html_escape(label) # protect against HTML injection
webserver.content_send(string.format("<fieldset><legend><b>&nbsp;#%i %s</b> (%s)&nbsp;</legend><p></p>", f.get_fabric_index(), label, f.get_admin_vendor_name()))
webserver.content_send(f"<fieldset><legend><b>&nbsp;#{f.get_fabric_index():i} {label}</b> ({f.get_admin_vendor_name()})&nbsp;</legend><p></p>")
var fabric_rev = f.get_fabric_id().copy().reverse()
var deviceid_rev = f.get_device_id().copy().reverse()
webserver.content_send(string.format("Fabric: %s<br>", fabric_rev.tohex()))
webserver.content_send(string.format("Device: %s<br>&nbsp;", deviceid_rev.tohex()))
webserver.content_send(f"Fabric: {fabric_rev.tohex()}<br>")
webserver.content_send(f"Device: {deviceid_rev.tohex()}<br>&nbsp;")
webserver.content_send("<form action='/matterc' method='post' onsubmit='return confirm(\"Are you sure?\");'>")
webserver.content_send(string.format("<input name='del_fabric' type='hidden' value='%i'>", f.get_fabric_index()))
webserver.content_send(f"<input name='del_fabric' type='hidden' value='{f.get_fabric_index():i}'>")
webserver.content_send("<button name='del' class='button bgrn'>Delete Fabric</button></form></p>")
webserver.content_send("<p></p></fieldset><p></p>")
@ -252,8 +252,8 @@ class Matter_UI
# var hl = ["Enter Relay number","Not used","Enter Filter pattern","Enter Switch number"];
def show_plugins_hints_js(*class_list)
import webserver
import string
import json
import string
var class_types = []
for cl: class_list
@ -279,11 +279,11 @@ class Matter_UI
end
end
webserver.content_send(string.format(
webserver.content_send(f""
"<script type='text/javascript'>"
"var hm=%s;"
"var hl=%s;"
"</script>", json.dump(hm), json.dump(hl)))
"var hm={json.dump(hm)};"
"var hl={json.dump(hl)};"
"</script>")
webserver.content_send(matter._ADD_ENDPOINT_JS)
@ -332,17 +332,17 @@ class Matter_UI
end
found = true
webserver.content_send(string.format("<tr><td style='font-size:smaller;'><b>%i</b></td>", ep))
webserver.content_send(string.format("<td style='font-size:smaller;'><input type='text' name='nam%i' size='1' value='%s'></td>",
webserver.content_send(f"<tr><td style='font-size:smaller;'><b>{ep:i}</b></td>")
webserver.content_send(format("<td style='font-size:smaller;'><input type='text' name='nam%i' size='1' value='%s'></td>",
ep, webserver.html_escape(conf.find('name', ''))))
webserver.content_send(string.format("<td style='font-size:smaller;'><b>%s</b></td>", self.plugin_name(conf.find('type', ''))))
webserver.content_send(string.format("<td style='font-size:smaller;'><input type='text' name='arg%i' size='1' value='%s' placeholder='%s'></td>",
webserver.content_send(f"<td style='font-size:smaller;'><b>{self.plugin_name(conf.find('type', ''))}</b></td>")
webserver.content_send(format("<td style='font-size:smaller;'><input type='text' name='arg%i' size='1' value='%s' placeholder='%s'></td>",
ep, webserver.html_escape(arg), cl ? webserver.html_escape(cl.ARG_HINT) : ''))
webserver.content_send(string.format("<td style='text-align:center;'><button name='del%i' "
webserver.content_send(f"<td style='text-align:center;'><button name='del{ep:i}' "
"style='background:none;border:none;line-height:1;'"
" onclick=\"return confirm('Confirm removing endpoint')\""
">"
"&#128293;</button></td></tr>", ep))
"&#128293;</button></td></tr>")
i += 1
end
webserver.content_send("</table>")
@ -366,7 +366,8 @@ class Matter_UI
for remote: remotes
webserver.content_send(string.format("&#x1F517; <a target='_blank' href=\"http://%s/?\">%s</a>", webserver.html_escape(remote), webserver.html_escape(remote)))
var remote_html = webserver.html_escape(remote)
webserver.content_send(f"&#x1F517; <a target='_blank' href=\"http://{remote_html}/?\">{remote_html}</a>")
webserver.content_send("<table style='width:100%'>")
webserver.content_send("<tr>"
"<td width='25'></td>"
@ -396,18 +397,18 @@ class Matter_UI
end
found = true
webserver.content_send(string.format("<tr><td width='22' style='font-size:smaller;'><b>%i</b></td>", ep))
webserver.content_send(string.format("<td width='78' style='font-size:smaller;'><input type='text' name='nam%i' size='1' value='%s' placeholder='(optional)'></td>",
webserver.content_send(f"<tr><td width='22' style='font-size:smaller;'><b>{ep:i}</b></td>")
webserver.content_send(format("<td width='78' style='font-size:smaller;'><input type='text' name='nam%i' size='1' value='%s' placeholder='(optional)'></td>",
ep, webserver.html_escape(conf.find('name', ''))))
webserver.content_send(string.format("<td width='115' style='font-size:smaller;'><b>%s</b></select></td>", self.plugin_name(conf.find('type', ''))))
webserver.content_send(string.format("<td style='font-size:smaller;'><input type='text' name='arg%i' size='8' value='%s'></td>",
webserver.content_send(format("<td width='115' style='font-size:smaller;'><b>%s</b></select></td>", self.plugin_name(conf.find('type', ''))))
webserver.content_send(format("<td style='font-size:smaller;'><input type='text' name='arg%i' size='8' value='%s'></td>",
ep, webserver.html_escape(arg)))
webserver.content_send(string.format("<td width='15' style='text-align:center;'><button name='del%i' "
webserver.content_send(f"<td width='15' style='text-align:center;'><button name='del{ep:i}' "
"style='background:none;border:none;line-height:1;'"
" onclick=\"return confirm('Confirm removing endpoint')\""
">"
"&#128293;</button></td></tr>", ep))
"&#128293;</button></td></tr>")
i += 1
end
webserver.content_send("</table><p></p>")

File diff suppressed because it is too large Load Diff