aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-03-13 13:16:53 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-03-13 13:16:53 -0300
commitcf71a5ddc742692fad813f89f1c9ef53e1ffde0f (patch)
treedf02305ff3cf05908f21829384e3a7f8699d2331
parent2c32bff60987d38a60a58d4f0123f3783da60a63 (diff)
downloadlua-cf71a5ddc742692fad813f89f1c9ef53e1ffde0f.tar.gz
lua-cf71a5ddc742692fad813f89f1c9ef53e1ffde0f.tar.bz2
lua-cf71a5ddc742692fad813f89f1c9ef53e1ffde0f.zip
Details
Several small improvements (code style, warnings, comments, more tests), in particular: - 'lua_topointer' extended to handle strings - raises an error in 'string.format("%10q")' ('%q' with modifiers) - in the manual for 'string.format', the term "option" replaced by "conversion specifier" (the term used by the C standard)
-rw-r--r--lapi.c31
-rw-r--r--lfunc.c3
-rw-r--r--lopcodes.h15
-rw-r--r--lstrlib.c6
-rw-r--r--ltests.c7
-rw-r--r--manual/manual.of54
-rw-r--r--testes/api.lua21
-rw-r--r--testes/strings.lua1
8 files changed, 87 insertions, 51 deletions
diff --git a/lapi.c b/lapi.c
index 4026497e..66d75649 100644
--- a/lapi.c
+++ b/lapi.c
@@ -414,8 +414,7 @@ LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
414} 414}
415 415
416 416
417LUA_API void *lua_touserdata (lua_State *L, int idx) { 417static void *touserdata (const TValue *o) {
418 const TValue *o = index2value(L, idx);
419 switch (ttype(o)) { 418 switch (ttype(o)) {
420 case LUA_TUSERDATA: return getudatamem(uvalue(o)); 419 case LUA_TUSERDATA: return getudatamem(uvalue(o));
421 case LUA_TLIGHTUSERDATA: return pvalue(o); 420 case LUA_TLIGHTUSERDATA: return pvalue(o);
@@ -424,23 +423,37 @@ LUA_API void *lua_touserdata (lua_State *L, int idx) {
424} 423}
425 424
426 425
426LUA_API void *lua_touserdata (lua_State *L, int idx) {
427 const TValue *o = index2value(L, idx);
428 return touserdata(o);
429}
430
431
427LUA_API lua_State *lua_tothread (lua_State *L, int idx) { 432LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
428 const TValue *o = index2value(L, idx); 433 const TValue *o = index2value(L, idx);
429 return (!ttisthread(o)) ? NULL : thvalue(o); 434 return (!ttisthread(o)) ? NULL : thvalue(o);
430} 435}
431 436
432 437
438/*
439** Returns a pointer to the internal representation of an object.
440** Note that ANSI C does not allow the conversion of a pointer to
441** function to a 'void*', so the conversion here goes through
442** a 'size_t'. (As the returned pointer is only informative, this
443** conversion should not be a problem.)
444*/
433LUA_API const void *lua_topointer (lua_State *L, int idx) { 445LUA_API const void *lua_topointer (lua_State *L, int idx) {
434 const TValue *o = index2value(L, idx); 446 const TValue *o = index2value(L, idx);
435 switch (ttypetag(o)) { 447 switch (ttypetag(o)) {
436 case LUA_TTABLE: return hvalue(o);
437 case LUA_TLCL: return clLvalue(o);
438 case LUA_TCCL: return clCvalue(o);
439 case LUA_TLCF: return cast_voidp(cast_sizet(fvalue(o))); 448 case LUA_TLCF: return cast_voidp(cast_sizet(fvalue(o)));
440 case LUA_TTHREAD: return thvalue(o); 449 case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA:
441 case LUA_TUSERDATA: return getudatamem(uvalue(o)); 450 return touserdata(o);
442 case LUA_TLIGHTUSERDATA: return pvalue(o); 451 default: {
443 default: return NULL; 452 if (iscollectable(o))
453 return gcvalue(o);
454 else
455 return NULL;
456 }
444 } 457 }
445} 458}
446 459
diff --git a/lfunc.c b/lfunc.c
index 362b798c..3e044b65 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -138,7 +138,8 @@ static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
138 if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ 138 if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
139 callclose(L, NULL); /* call closing method */ 139 callclose(L, NULL); /* call closing method */
140 else if (!ttisnil(uv)) { /* non-closable non-nil value? */ 140 else if (!ttisnil(uv)) { /* non-closable non-nil value? */
141 const char *vname = luaG_findlocal(L, L->ci, level - L->ci->func, NULL); 141 int idx = cast_int(level - L->ci->func);
142 const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
142 if (vname == NULL) vname = "?"; 143 if (vname == NULL) vname = "?";
143 luaG_runerror(L, "attempt to close non-closable variable '%s'", vname); 144 luaG_runerror(L, "attempt to close non-closable variable '%s'", vname);
144 } 145 }
diff --git a/lopcodes.h b/lopcodes.h
index d7403caf..3e100259 100644
--- a/lopcodes.h
+++ b/lopcodes.h
@@ -90,7 +90,6 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
90#define MAXARG_B ((1<<SIZE_B)-1) 90#define MAXARG_B ((1<<SIZE_B)-1)
91#define MAXARG_C ((1<<SIZE_C)-1) 91#define MAXARG_C ((1<<SIZE_C)-1)
92#define OFFSET_sC (MAXARG_C >> 1) 92#define OFFSET_sC (MAXARG_C >> 1)
93#define MAXARG_Cx ((1<<(SIZE_C + 1))-1)
94 93
95 94
96/* creates a mask with 'n' 1 bits at position 'p' */ 95/* creates a mask with 'n' 1 bits at position 'p' */
@@ -233,8 +232,8 @@ OP_BANDK,/* A B C R(A) := R(B) & K(C):integer */
233OP_BORK,/* A B C R(A) := R(B) | K(C):integer */ 232OP_BORK,/* A B C R(A) := R(B) | K(C):integer */
234OP_BXORK,/* A B C R(A) := R(B) ~ K(C):integer */ 233OP_BXORK,/* A B C R(A) := R(B) ~ K(C):integer */
235 234
236OP_SHRI,/* A B C R(A) := R(B) >> C */ 235OP_SHRI,/* A B sC R(A) := R(B) >> C */
237OP_SHLI,/* A B C R(A) := C << R(B) */ 236OP_SHLI,/* A B sC R(A) := C << R(B) */
238 237
239OP_ADD,/* A B C R(A) := R(B) + R(C) */ 238OP_ADD,/* A B C R(A) := R(B) + R(C) */
240OP_SUB,/* A B C R(A) := R(B) - R(C) */ 239OP_SUB,/* A B C R(A) := R(B) - R(C) */
@@ -272,7 +271,7 @@ OP_GTI,/* A sB if ((R(A) > sB) ~= k) then pc++ */
272OP_GEI,/* A sB if ((R(A) >= sB) ~= k) then pc++ */ 271OP_GEI,/* A sB if ((R(A) >= sB) ~= k) then pc++ */
273 272
274OP_TEST,/* A if (not R(A) == k) then pc++ */ 273OP_TEST,/* A if (not R(A) == k) then pc++ */
275OP_TESTSET,/* A B if (not R(B) == k) then R(A) := R(B) else pc++ */ 274OP_TESTSET,/* A B if (not R(B) == k) then pc++ else R(A) := R(B) */
276 275
277OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ 276OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
278OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ 277OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
@@ -305,15 +304,15 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
305} OpCode; 304} OpCode;
306 305
307 306
308#define NUM_OPCODES (cast_int(OP_EXTRAARG) + 1) 307#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1)
309 308
310 309
311 310
312/*=========================================================================== 311/*===========================================================================
313 Notes: 312 Notes:
314 (*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then 'top' is 313 (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then
315 set to last_result+1, so next open instruction (OP_CALL, OP_RETURN*, 314 'top' is set to last_result+1, so next open instruction (OP_CALL,
316 OP_SETLIST) may use 'top'. 315 OP_RETURN*, OP_SETLIST) may use 'top'.
317 316
318 (*) In OP_VARARG, if (C == 0) then use actual number of varargs and 317 (*) In OP_VARARG, if (C == 0) then use actual number of varargs and
319 set top (like in OP_CALL with C == 0). 318 set top (like in OP_CALL with C == 0).
diff --git a/lstrlib.c b/lstrlib.c
index 41ebc523..ab4258e5 100644
--- a/lstrlib.c
+++ b/lstrlib.c
@@ -181,7 +181,7 @@ static int str_byte (lua_State *L) {
181 size_t pose = getendpos(L, 3, pi, l); 181 size_t pose = getendpos(L, 3, pi, l);
182 int n, i; 182 int n, i;
183 if (posi > pose) return 0; /* empty interval; return no values */ 183 if (posi > pose) return 0; /* empty interval; return no values */
184 if (pose - posi >= INT_MAX) /* arithmetic overflow? */ 184 if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */
185 return luaL_error(L, "string slice too long"); 185 return luaL_error(L, "string slice too long");
186 n = (int)(pose - posi) + 1; 186 n = (int)(pose - posi) + 1;
187 luaL_checkstack(L, n, "string slice too long"); 187 luaL_checkstack(L, n, "string slice too long");
@@ -1159,7 +1159,7 @@ static int str_format (lua_State *L) {
1159 char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ 1159 char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */
1160 int nb = 0; /* number of bytes in added item */ 1160 int nb = 0; /* number of bytes in added item */
1161 if (++arg > top) 1161 if (++arg > top)
1162 luaL_argerror(L, arg, "no value"); 1162 return luaL_argerror(L, arg, "no value");
1163 strfrmt = scanformat(L, strfrmt, form); 1163 strfrmt = scanformat(L, strfrmt, form);
1164 switch (*strfrmt++) { 1164 switch (*strfrmt++) {
1165 case 'c': { 1165 case 'c': {
@@ -1186,6 +1186,8 @@ static int str_format (lua_State *L) {
1186 break; 1186 break;
1187 } 1187 }
1188 case 'q': { 1188 case 'q': {
1189 if (form[2] != '\0') /* modifiers? */
1190 return luaL_error(L, "specifier '%%q' cannot have modifiers");
1189 addliteral(L, &b, arg); 1191 addliteral(L, &b, arg);
1190 break; 1192 break;
1191 } 1193 }
diff --git a/ltests.c b/ltests.c
index 36a974ae..23375382 100644
--- a/ltests.c
+++ b/ltests.c
@@ -164,7 +164,7 @@ typedef union Header {
164 164
165 165
166Memcontrol l_memcontrol = 166Memcontrol l_memcontrol =
167 {0L, 0L, 0L, 0L, (~0L), {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}}; 167 {0UL, 0UL, 0UL, 0UL, (~0UL), {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}};
168 168
169 169
170static void freeblock (Memcontrol *mc, Header *block) { 170static void freeblock (Memcontrol *mc, Header *block) {
@@ -1596,7 +1596,10 @@ static struct X { int x; } x;
1596 lua_pushnumber(L1, lua_tonumber(L1, getindex)); 1596 lua_pushnumber(L1, lua_tonumber(L1, getindex));
1597 } 1597 }
1598 else if EQ("topointer") { 1598 else if EQ("topointer") {
1599 lua_pushnumber(L1, cast_sizet(lua_topointer(L1, getindex))); 1599 lua_pushlightuserdata(L1, cast_voidp(lua_topointer(L1, getindex)));
1600 }
1601 else if EQ("touserdata") {
1602 lua_pushlightuserdata(L1, lua_touserdata(L1, getindex));
1600 } 1603 }
1601 else if EQ("tostring") { 1604 else if EQ("tostring") {
1602 const char *s = lua_tostring(L1, getindex); 1605 const char *s = lua_tostring(L1, getindex);
diff --git a/manual/manual.of b/manual/manual.of
index 421d04de..9c8ab033 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -143,7 +143,7 @@ that is, @x{arrays} that can have as indices not only numbers,
143but any Lua value except @nil and @x{NaN}. 143but any Lua value except @nil and @x{NaN}.
144(@emphx{Not a Number} is a special floating-point value 144(@emphx{Not a Number} is a special floating-point value
145used by the @x{IEEE 754} standard to represent 145used by the @x{IEEE 754} standard to represent
146undefined or unrepresentable numerical results, such as @T{0/0}.) 146undefined numerical results, such as @T{0/0}.)
147Tables can be @emph{heterogeneous}; 147Tables can be @emph{heterogeneous};
148that is, they can contain values of all types (except @nil). 148that is, they can contain values of all types (except @nil).
149Any key with value @nil is not considered part of the table. 149Any key with value @nil is not considered part of the table.
@@ -670,8 +670,8 @@ are called when the garbage collector detects that the
670corresponding table or userdata is unreachable. 670corresponding table or userdata is unreachable.
671Finalizers allow you to coordinate Lua's garbage collection 671Finalizers allow you to coordinate Lua's garbage collection
672with external resource management 672with external resource management
673(such as closing files, network or database connections, 673such as closing files, network or database connections,
674or freeing your own memory). 674or freeing your own memory.
675 675
676For an object (table or userdata) to be finalized when collected, 676For an object (table or userdata) to be finalized when collected,
677you must @emph{mark} it for finalization. 677you must @emph{mark} it for finalization.
@@ -1323,11 +1323,12 @@ labels in Lua are considered statements too:
1323} 1323}
1324 1324
1325A label is visible in the entire block where it is defined, 1325A label is visible in the entire block where it is defined,
1326except 1326except inside nested functions.
1327inside nested blocks where a label with the same name is defined and
1328inside nested functions.
1329A goto may jump to any visible label as long as it does not 1327A goto may jump to any visible label as long as it does not
1330enter into the scope of a local variable. 1328enter into the scope of a local variable.
1329A label should not be declared
1330where a label with the same name is visible,
1331even if this other label has been declared in an enclosing block.
1331 1332
1332Labels and empty statements are called @def{void statements}, 1333Labels and empty statements are called @def{void statements},
1333as they perform no actions. 1334as they perform no actions.
@@ -1537,7 +1538,7 @@ goes out of scope, including normal block termination,
1537exiting its block by @Rw{break}/@Rw{goto}/@Rw{return}, 1538exiting its block by @Rw{break}/@Rw{goto}/@Rw{return},
1538or exiting by an error. 1539or exiting by an error.
1539 1540
1540Here, to \emph{close} a value means 1541Here, to @emph{close} a value means
1541to call its @idx{__close} metamethod. 1542to call its @idx{__close} metamethod.
1542If the value is @nil, it is ignored; 1543If the value is @nil, it is ignored;
1543otherwise, 1544otherwise,
@@ -4236,7 +4237,7 @@ indicates whether the operation succeeded.
4236 4237
4237Converts the value at the given index to a generic 4238Converts the value at the given index to a generic
4238@N{C pointer} (@T{void*}). 4239@N{C pointer} (@T{void*}).
4239The value can be a userdata, a table, a thread, or a function; 4240The value can be a userdata, a table, a thread, a string, or a function;
4240otherwise, @id{lua_topointer} returns @id{NULL}. 4241otherwise, @id{lua_topointer} returns @id{NULL}.
4241Different objects will give different pointers. 4242Different objects will give different pointers.
4242There is no way to convert the pointer back to its original value. 4243There is no way to convert the pointer back to its original value.
@@ -6712,8 +6713,10 @@ to save space.
6712 6713
6713Functions with upvalues have only their number of upvalues saved. 6714Functions with upvalues have only their number of upvalues saved.
6714When (re)loaded, 6715When (re)loaded,
6715those upvalues receive fresh instances containing @nil. 6716those upvalues receive fresh instances.
6716(You can use the debug library to serialize 6717(See the @Lid{load} function for details about
6718how these upvalues are initialized.
6719You can use the debug library to serialize
6717and reload the upvalues of a function 6720and reload the upvalues of a function
6718in a way adequate to your needs.) 6721in a way adequate to your needs.)
6719 6722
@@ -6747,12 +6750,12 @@ after the two indices.
6747Returns a formatted version of its variable number of arguments 6750Returns a formatted version of its variable number of arguments
6748following the description given in its first argument (which must be a string). 6751following the description given in its first argument (which must be a string).
6749The format string follows the same rules as the @ANSI{sprintf}. 6752The format string follows the same rules as the @ANSI{sprintf}.
6750The only differences are that the options/modifiers 6753The only differences are that the conversion specifiers and modifiers
6751@T{*}, @id{h}, @id{L}, @id{l}, @id{n}, 6754@T{*}, @id{h}, @id{L}, @id{l}, @id{n},
6752and @id{p} are not supported 6755and @id{p} are not supported
6753and that there is an extra option, @id{q}. 6756and that there is an extra specifier, @id{q}.
6754 6757
6755The @id{q} option formats booleans, nil, numbers, and strings 6758The specifier @id{q} formats booleans, nil, numbers, and strings
6756in a way that the result is a valid constant in Lua source code. 6759in a way that the result is a valid constant in Lua source code.
6757Booleans and nil are written in the obvious way 6760Booleans and nil are written in the obvious way
6758(@id{true}, @id{false}, @id{nil}). 6761(@id{true}, @id{false}, @id{nil}).
@@ -6770,22 +6773,23 @@ may produce the string:
6770"a string with \"quotes\" and \ 6773"a string with \"quotes\" and \
6771 new line" 6774 new line"
6772} 6775}
6776This specifier does not support modifiers (flags, width, length).
6773 6777
6774Options 6778The conversion specifiers
6775@id{A}, @id{a}, @id{E}, @id{e}, @id{f}, 6779@id{A}, @id{a}, @id{E}, @id{e}, @id{f},
6776@id{G}, and @id{g} all expect a number as argument. 6780@id{G}, and @id{g} all expect a number as argument.
6777Options @id{c}, @id{d}, 6781The specifiers @id{c}, @id{d},
6778@id{i}, @id{o}, @id{u}, @id{X}, and @id{x} 6782@id{i}, @id{o}, @id{u}, @id{X}, and @id{x}
6779expect an integer. 6783expect an integer.
6780When Lua is compiled with a C89 compiler, 6784When Lua is compiled with a C89 compiler,
6781options @id{A} and @id{a} (hexadecimal floats) 6785the specifiers @id{A} and @id{a} (hexadecimal floats)
6782do not support any modifier (flags, width, length). 6786do not support modifiers.
6783 6787
6784Option @id{s} expects a string; 6788The specifier @id{s} expects a string;
6785if its argument is not a string, 6789if its argument is not a string,
6786it is converted to one following the same rules of @Lid{tostring}. 6790it is converted to one following the same rules of @Lid{tostring}.
6787If the option has any modifier (flags, width, length), 6791If the specifier has any modifier,
6788the string argument should not contain @x{embedded zeros}. 6792the corresponding string argument should not contain @x{embedded zeros}.
6789 6793
6790} 6794}
6791 6795
@@ -8009,8 +8013,8 @@ or there is any input from some special files
8009} 8013}
8010 8014
8011} 8015}
8012For the last two cases, @id{size} 8016For the last two cases,
8013specifies the size of the buffer, in bytes. 8017@id{size} is a hint for the size of the buffer, in bytes.
8014The default is an appropriate size. 8018The default is an appropriate size.
8015 8019
8016} 8020}
@@ -8698,6 +8702,12 @@ When a coroutine finishes with an error,
8698its stack is unwound (to run any pending closing methods). 8702its stack is unwound (to run any pending closing methods).
8699} 8703}
8700 8704
8705@item{
8706A label for a @Rw{goto} cannot be declared where a label with the same
8707name is visible, even if this other label is declared in an enclosing
8708block.
8709}
8710
8701} 8711}
8702 8712
8703} 8713}
diff --git a/testes/api.lua b/testes/api.lua
index 9904dadf..d034ea80 100644
--- a/testes/api.lua
+++ b/testes/api.lua
@@ -332,6 +332,7 @@ function to (s, x, n)
332 return T.testC(string.format("%s %d; return 1", s, n), x) 332 return T.testC(string.format("%s %d; return 1", s, n), x)
333end 333end
334 334
335local null = T.pushuserdata(0)
335local hfunc = string.gmatch("", "") -- a "heavy C function" (with upvalues) 336local hfunc = string.gmatch("", "") -- a "heavy C function" (with upvalues)
336assert(debug.getupvalue(hfunc, 1)) 337assert(debug.getupvalue(hfunc, 1))
337assert(to("tostring", {}) == nil) 338assert(to("tostring", {}) == nil)
@@ -349,13 +350,19 @@ assert(to("tonumber", {}) == 0)
349assert(to("tonumber", "12") == 12) 350assert(to("tonumber", "12") == 12)
350assert(to("tonumber", "s2") == 0) 351assert(to("tonumber", "s2") == 0)
351assert(to("tonumber", 1, 20) == 0) 352assert(to("tonumber", 1, 20) == 0)
352assert(to("topointer", 10) == 0) 353assert(to("topointer", 10) == null)
353assert(to("topointer", true) == 0) 354assert(to("topointer", true) == null)
354assert(to("topointer", T.pushuserdata(20)) == 20) 355assert(to("topointer", nil) == null)
355assert(to("topointer", io.read) ~= 0) -- light C function 356assert(to("topointer", "abc") ~= null)
356assert(to("topointer", hfunc) ~= 0) -- "heavy" C function 357assert(to("topointer", string.rep("x", 10)) ==
357assert(to("topointer", function () end) ~= 0) -- Lua function 358 to("topointer", string.rep("x", 10))) -- short strings
358assert(to("topointer", io.stdin) ~= 0) -- full userdata 359assert(to("topointer", string.rep("x", 300)) ~=
360 to("topointer", string.rep("x", 300))) -- long strings
361assert(to("topointer", T.pushuserdata(20)) ~= null)
362assert(to("topointer", io.read) ~= null) -- light C function
363assert(to("topointer", hfunc) ~= null) -- "heavy" C function
364assert(to("topointer", function () end) ~= null) -- Lua function
365assert(to("topointer", io.stdin) ~= null) -- full userdata
359assert(to("func2num", 20) == 0) 366assert(to("func2num", 20) == 0)
360assert(to("func2num", T.pushuserdata(10)) == 0) 367assert(to("func2num", T.pushuserdata(10)) == 0)
361assert(to("func2num", io.read) ~= 0) -- light C function 368assert(to("func2num", io.read) ~= 0) -- light C function
diff --git a/testes/strings.lua b/testes/strings.lua
index 88480924..da53a87e 100644
--- a/testes/strings.lua
+++ b/testes/strings.lua
@@ -199,6 +199,7 @@ end
199 199
200assert(string.format("\0%s\0", "\0\0\1") == "\0\0\0\1\0") 200assert(string.format("\0%s\0", "\0\0\1") == "\0\0\0\1\0")
201checkerror("contains zeros", string.format, "%10s", "\0") 201checkerror("contains zeros", string.format, "%10s", "\0")
202checkerror("cannot have modifiers", string.format, "%10q", "1")
202 203
203-- format x tostring 204-- format x tostring
204assert(string.format("%s %s", nil, true) == "nil true") 205assert(string.format("%s %s", nil, true) == "nil true")