diff --git a/gradle.properties b/gradle.properties index bcaaf2947..bbeffee42 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ kotlin.stdlib.default.dependency=false kotlin.jvm.target.validation.mode=error # Mod properties -modVersion=1.101.2 +modVersion=1.101.3 # Minecraft properties: We want to configure this here so we can read it in settings.gradle mcVersion=1.18.2 diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java index b10db8e30..87ee82037 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java @@ -8,9 +8,13 @@ import com.google.common.net.InetAddresses; import dan200.computercraft.ComputerCraft; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * A predicate on an address. Matches against a domain and an ip address. @@ -135,13 +139,36 @@ final class PrivatePattern implements AddressPredicate { static final PrivatePattern INSTANCE = new PrivatePattern(); + private static final Set additionalAddresses = Arrays.stream( new String[] { + // Block various cloud providers internal IPs. + "100.100.100.200", // Alibaba + "192.0.0.192", // Oracle + } ).map( InetAddresses::forString ).collect( Collectors.toSet() ); + @Override public boolean matches( InetAddress socketAddress ) { - return socketAddress.isAnyLocalAddress() - || socketAddress.isLoopbackAddress() - || socketAddress.isLinkLocalAddress() - || socketAddress.isSiteLocalAddress(); + return socketAddress.isAnyLocalAddress() // 0.0.0.0, ::0 + || socketAddress.isLoopbackAddress() // 127.0.0.0/8, ::1 + || socketAddress.isLinkLocalAddress() // 169.254.0.0/16, fe80::/10 + || socketAddress.isSiteLocalAddress() // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fec0::/10 + || socketAddress.isMulticastAddress() // 224.0.0.0/4, ff00::/8 + || isUniqueLocalAddress( socketAddress ) // fd00::/8 + || additionalAddresses.contains( socketAddress ); + } + + /** + * Determine if an IP address lives inside the ULA address range. + * + * @param address The IP address to test. + * @return Whether this address sits in the ULA address range. + * @see Unique local address on Wikipedia + */ + private boolean isUniqueLocalAddress( InetAddress address ) + { + // ULA is actually defined as fc00::/7 (so both fc00::/8 and fd00::/8). However, only the latter is actually + // defined right now, so let's be conservative. + return address instanceof Inet6Address && (address.getAddress()[0] & 0xff) == 0xfd; } } diff --git a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index af328d3dd..7fc993bef 100644 --- a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java @@ -7,10 +7,13 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.metrics.Metrics; +import dan200.computercraft.shared.command.arguments.ComputersArgumentType; import dan200.computercraft.shared.command.text.TableBuilder; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; @@ -200,7 +203,10 @@ else if( b.getLevel() == world ) .then( command( "queue" ) .requires( UserLevel.ANYONE ) - .arg( "computer", manyComputers() ) + .arg( + RequiredArgumentBuilder.argument( "computer", manyComputers() ) + .suggests( ( context, builder ) -> Suggestions.empty() ) + ) .argManyValue( "args", StringArgumentType.string(), Collections.emptyList() ) .executes( ( ctx, args ) -> { Collection computers = getComputersArgument( ctx, "computer" ); diff --git a/src/main/java/dan200/computercraft/shared/command/UserLevel.java b/src/main/java/dan200/computercraft/shared/command/UserLevel.java index 48e9aa365..ab6ebe5b5 100644 --- a/src/main/java/dan200/computercraft/shared/command/UserLevel.java +++ b/src/main/java/dan200/computercraft/shared/command/UserLevel.java @@ -61,12 +61,36 @@ public boolean test( CommandSourceStack source ) return source.hasPermission( toLevel() ); } + /** + * Take the union of two {@link UserLevel}s. + *

+ * This satisfies the property that for all sources {@code s}, {@code a.test(s) || b.test(s) == (a ∪ b).test(s)}. + * + * @param left The first user level to take the union of. + * @param right The second user level to take the union of. + * @return The union of two levels. + */ + public static UserLevel union( UserLevel left, UserLevel right ) + { + if( left == right ) return left; + + // x ∪ ANYONE = ANYONE + if( left == ANYONE || right == ANYONE ) return ANYONE; + + // x ∪ OWNER = OWNER + if( left == OWNER ) return right; + if( right == OWNER ) return left; + + // At this point, we have x != y and x, y ∈ { OP, OWNER_OP }. + return OWNER_OP; + } + private static boolean isOwner( CommandSourceStack source ) { MinecraftServer server = source.getServer(); Entity sender = source.getEntity(); return server.isDedicatedServer() ? source.getEntity() == null && source.hasPermission( 4 ) && source.getTextName().equals( "Server" ) - : sender instanceof Player player && player.getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ); + : sender instanceof Player player && server.isSingleplayerOwner( player.getGameProfile() ); } } diff --git a/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java b/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java index 2bbfb4af4..dfc3f756f 100644 --- a/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java +++ b/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java @@ -52,10 +52,15 @@ public CommandBuilder requires( Predicate predicate ) return this; } + public CommandBuilder arg( ArgumentBuilder arg ) + { + args.add( arg ); + return this; + } + public CommandBuilder arg( String name, ArgumentType type ) { - args.add( RequiredArgumentBuilder.argument( name, type ) ); - return this; + return arg( RequiredArgumentBuilder.argument( name, type ) ); } public CommandNodeBuilder>> argManyValue( String name, ArgumentType type, List empty ) @@ -84,7 +89,7 @@ private CommandNodeBuilder>> argMany( String nam return command -> { // The node for no arguments - ArgumentBuilder tail = tail( ctx -> command.run( ctx, empty.get() ) ); + ArgumentBuilder tail = setupTail( ctx -> command.run( ctx, empty.get() ) ); // The node for one or more arguments ArgumentBuilder moreArg = RequiredArgumentBuilder @@ -93,7 +98,7 @@ private CommandNodeBuilder>> argMany( String nam // Chain all of them together! tail.then( moreArg ); - return link( tail ); + return buildTail( tail ); }; } @@ -106,22 +111,18 @@ private static List getList( CommandContext context, String name ) @Override public CommandNode executes( Command command ) { - if( args.isEmpty() ) throw new IllegalStateException( "Cannot have empty arg chain builder" ); - - return link( tail( command ) ); + return buildTail( setupTail( command ) ); } - private ArgumentBuilder tail( Command command ) + private ArgumentBuilder setupTail( Command command ) { - ArgumentBuilder defaultTail = args.get( args.size() - 1 ); - defaultTail.executes( command ); - if( requires != null ) defaultTail.requires( requires ); - return defaultTail; + return args.get( args.size() - 1 ).executes( command ); } - private CommandNode link( ArgumentBuilder tail ) + private CommandNode buildTail( ArgumentBuilder tail ) { for( int i = args.size() - 2; i >= 0; i-- ) tail = args.get( i ).then( tail ); + if( requires != null ) tail.requires( requires ); return tail.build(); } } diff --git a/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java b/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java index 6f6f4847d..60027f04d 100644 --- a/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java +++ b/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java @@ -12,6 +12,7 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; +import dan200.computercraft.shared.command.UserLevel; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.ClickEvent; @@ -22,6 +23,10 @@ import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static dan200.computercraft.shared.command.text.ChatHelpers.coloured; import static dan200.computercraft.shared.command.text.ChatHelpers.translate; @@ -44,6 +49,33 @@ public static HelpingArgumentBuilder choice( String literal ) return new HelpingArgumentBuilder( literal ); } + + @Override + public LiteralArgumentBuilder requires( Predicate requirement ) + { + throw new IllegalStateException( "Cannot use requires on a HelpingArgumentBuilder" ); + } + + @Override + public Predicate getRequirement() + { + // The requirement of this node is the union of all child's requirements. + List> requirements = Stream.concat( + children.stream().map( ArgumentBuilder::getRequirement ), + getArguments().stream().map( CommandNode::getRequirement ) + ).collect( Collectors.toList() ); + + // If all requirements are a UserLevel, take the union of those instead. + UserLevel userLevel = UserLevel.OWNER; + for( Predicate requirement : requirements ) + { + if( !(requirement instanceof UserLevel level) ) return x -> requirements.stream().anyMatch( y -> y.test( x ) ); + userLevel = UserLevel.union( userLevel, level ); + } + + return userLevel; + } + @Override public LiteralArgumentBuilder executes( final Command command ) { @@ -99,9 +131,7 @@ private LiteralCommandNode buildImpl( String id, String comm helpCommand.node = node; // Set up a /... help command - LiteralArgumentBuilder helpNode = LiteralArgumentBuilder.literal( "help" ) - .requires( x -> getArguments().stream().anyMatch( y -> y.getRequirement().test( x ) ) ) - .executes( helpCommand ); + LiteralArgumentBuilder helpNode = LiteralArgumentBuilder.literal( "help" ).executes( helpCommand ); // Add all normal command children to this and the help node for( CommandNode child : getArguments() ) diff --git a/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java index 923207b58..df89ce8dd 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java @@ -167,7 +167,7 @@ public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull B @Override public float getExplosionResistance( BlockState state, BlockGetter world, BlockPos pos, Explosion explosion ) { - Entity exploder = explosion.getExploder(); + Entity exploder = explosion == null ? null : explosion.getExploder(); if( getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile ) { return 2000; diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java index 5cb13d6c2..57b3efca3 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java @@ -82,7 +82,7 @@ public TurtleModem( ResourceLocation id, ItemStack stack, boolean advanced ) } else { - leftOffModel = new ResourceLocation( ComputerCraft.MOD_ID, "turtle_modem_normal_off_left" ); + leftOffModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_off_left" ); rightOffModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_off_right" ); leftOnModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_on_left" ); rightOnModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_on_right" ); diff --git a/src/main/resources/data/computercraft/lua/rom/apis/gps.lua b/src/main/resources/data/computercraft/lua/rom/apis/gps.lua index 8a49cab1b..fb93f9855 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/gps.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/gps.lua @@ -153,12 +153,22 @@ function locate(_nTimeout, _bDebug) if tFix.nDistance == 0 then pos1, pos2 = tFix.vPosition, nil else - table.insert(tFixes, tFix) + -- Insert our new position in our table, with a maximum of three items. If this is close to a + -- previous position, replace that instead of inserting. + local insIndex = math.min(3, #tFixes + 1) + for i, older in pairs(tFixes) do + if (older.vPosition - tFix.vPosition):length() < 1 then + insIndex = i + break + end + end + tFixes[insIndex] = tFix + if #tFixes >= 3 then if not pos1 then - pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[#tFixes]) + pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[3]) else - pos1, pos2 = narrow(pos1, pos2, tFixes[#tFixes]) + pos1, pos2 = narrow(pos1, pos2, tFixes[3]) end end end diff --git a/src/main/resources/data/computercraft/lua/rom/apis/settings.lua b/src/main/resources/data/computercraft/lua/rom/apis/settings.lua index ce29eaf0c..1644ae34f 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/settings.lua @@ -74,8 +74,7 @@ function undefine(name) details[name] = nil end -local function set_value(name, value) - local new = reserialize(value) +local function set_value(name, new) local old = values[name] if old == nil then local opt = details[name] @@ -103,7 +102,7 @@ function set(name, value) local opt = details[name] if opt and opt.type then expect(2, value, opt.type) end - set_value(name, value) + set_value(name, reserialize(value)) end --- Get the value of a setting. @@ -214,7 +213,9 @@ function load(sPath) if type(k) == "string" and (ty_v == "string" or ty_v == "number" or ty_v == "boolean" or ty_v == "table") then local opt = details[k] if not opt or not opt.type or ty_v == opt.type then - set_value(k, v) + -- This may fail if the table is recursive (or otherwise cannot be serialized). + local ok, v = pcall(reserialize, v) + if ok then set_value(k, v) end end end end diff --git a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua index fd16f965f..40f95757b 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua @@ -292,6 +292,13 @@ local g_tLuaKeywords = { ["while"] = true, } +--- A version of the ipairs iterator which ignores metamethods +local function inext(tbl, i) + i = (i or 0) + 1 + local v = rawget(tbl, i) + if v == nil then return nil else return i, v end +end + local serialize_infinity = math.huge local function serialize_impl(t, tracking, indent, opts) local sType = type(t) @@ -318,11 +325,11 @@ local function serialize_impl(t, tracking, indent, opts) result = open local seen_keys = {} - for k, v in ipairs(t) do + for k, v in inext, t do seen_keys[k] = true result = result .. sub_indent .. serialize_impl(v, tracking, sub_indent, opts) .. comma end - for k, v in pairs(t) do + for k, v in next, t do if not seen_keys[k] then local sEntry if type(k) == "string" and not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then diff --git a/src/main/resources/data/computercraft/lua/rom/apis/window.lua b/src/main/resources/data/computercraft/lua/rom/apis/window.lua index fd57b79fa..549bbff77 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/window.lua @@ -127,11 +127,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) local sEmptyTextColor = tEmptyColorLines[nTextColor] local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] for y = 1, nHeight do - tLines[y] = { - text = sEmptyText, - textColor = sEmptyTextColor, - backgroundColor = sEmptyBackgroundColor, - } + tLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor } end for i = 0, 15 do @@ -161,7 +157,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) local function redrawLine(n) local tLine = tLines[n] parent.setCursorPos(nX, nY + n - 1) - parent.blit(tLine.text, tLine.textColor, tLine.backgroundColor) + parent.blit(tLine[1], tLine[2], tLine[3]) end local function redraw() @@ -184,9 +180,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) -- Modify line local tLine = tLines[nCursorY] if nStart == 1 and nEnd == nWidth then - tLine.text = sText - tLine.textColor = sTextColor - tLine.backgroundColor = sBackgroundColor + tLine[1] = sText + tLine[2] = sTextColor + tLine[3] = sBackgroundColor else local sClippedText, sClippedTextColor, sClippedBackgroundColor if nStart < 1 then @@ -206,9 +202,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) sClippedBackgroundColor = sBackgroundColor end - local sOldText = tLine.text - local sOldTextColor = tLine.textColor - local sOldBackgroundColor = tLine.backgroundColor + local sOldText = tLine[1] + local sOldTextColor = tLine[2] + local sOldBackgroundColor = tLine[3] local sNewText, sNewTextColor, sNewBackgroundColor if nStart > 1 then local nOldEnd = nStart - 1 @@ -227,9 +223,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) sNewBackgroundColor = sNewBackgroundColor .. string_sub(sOldBackgroundColor, nOldStart, nWidth) end - tLine.text = sNewText - tLine.textColor = sNewTextColor - tLine.backgroundColor = sNewBackgroundColor + tLine[1] = sNewText + tLine[2] = sNewTextColor + tLine[3] = sNewBackgroundColor end -- Redraw line @@ -276,11 +272,10 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) local sEmptyTextColor = tEmptyColorLines[nTextColor] local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] for y = 1, nHeight do - tLines[y] = { - text = sEmptyText, - textColor = sEmptyTextColor, - backgroundColor = sEmptyBackgroundColor, - } + local line = tLines[y] + line[1] = sEmptyText + line[2] = sEmptyTextColor + line[3] = sEmptyBackgroundColor end if bVisible then redraw() @@ -291,14 +286,10 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) function window.clearLine() if nCursorY >= 1 and nCursorY <= nHeight then - local sEmptyText = sEmptySpaceLine - local sEmptyTextColor = tEmptyColorLines[nTextColor] - local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] - tLines[nCursorY] = { - text = sEmptyText, - textColor = sEmptyTextColor, - backgroundColor = sEmptyBackgroundColor, - } + local line = tLines[nCursorY] + line[1] = sEmptySpaceLine + line[2] = tEmptyColorLines[nTextColor] + line[3] = tEmptyColorLines[nBackgroundColor] if bVisible then redrawLine(nCursorY) updateCursorColor() @@ -427,11 +418,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) if y >= 1 and y <= nHeight then tNewLines[newY] = tLines[y] else - tNewLines[newY] = { - text = sEmptyText, - textColor = sEmptyTextColor, - backgroundColor = sEmptyBackgroundColor, - } + tNewLines[newY] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor } end end tLines = tNewLines @@ -474,7 +461,8 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) error("Line is out of range.", 2) end - return tLines[y].text, tLines[y].textColor, tLines[y].backgroundColor + local line = tLines[y] + return line[1], line[2], line[3] end -- Other functions @@ -570,26 +558,22 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] for y = 1, new_height do if y > nHeight then - tNewLines[y] = { - text = sEmptyText, - textColor = sEmptyTextColor, - backgroundColor = sEmptyBackgroundColor, - } + tNewLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor } else local tOldLine = tLines[y] if new_width == nWidth then tNewLines[y] = tOldLine elseif new_width < nWidth then tNewLines[y] = { - text = string_sub(tOldLine.text, 1, new_width), - textColor = string_sub(tOldLine.textColor, 1, new_width), - backgroundColor = string_sub(tOldLine.backgroundColor, 1, new_width), + string_sub(tOldLine[1], 1, new_width), + string_sub(tOldLine[2], 1, new_width), + string_sub(tOldLine[3], 1, new_width), } else tNewLines[y] = { - text = tOldLine.text .. string_sub(sEmptyText, nWidth + 1, new_width), - textColor = tOldLine.textColor .. string_sub(sEmptyTextColor, nWidth + 1, new_width), - backgroundColor = tOldLine.backgroundColor .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width), + tOldLine[1] .. string_sub(sEmptyText, nWidth + 1, new_width), + tOldLine[2] .. string_sub(sEmptyTextColor, nWidth + 1, new_width), + tOldLine[3] .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width), } end end diff --git a/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/src/main/resources/data/computercraft/lua/rom/help/changelog.md index d916312ed..aa1dfdfeb 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/changelog.md +++ b/src/main/resources/data/computercraft/lua/rom/help/changelog.md @@ -1,3 +1,21 @@ +# New features in CC: Tweaked 1.101.3 + +* Improve syntax errors when missing commas in tables, and on trailing commas in parameter lists. +* `speaker` program now reports an error on common unsupported audio formats. +* Small optimisations to the `window` API. + +Several bug fixes: +* Fix the REPL syntax reporting crashing on valid parses. +* Ignore metatables in `textutils.serialize`. +* Fix `gps.locate` returning `nan` when receiving a duplicate location (Wojbie). +* Ignore metatables in `textutils.serialize`. +* Fix wireless turtles having an invalid model. +* Fix crash when turtles are exploded by a null explosion. +* Lua REPL no longer accepts `)(` as a valid expression. +* Fix several inconsistencies with `require`/`package.path` in the Lua REPL (Wojbie). +* Fix private several IP address ranges not being blocked by the `$private` rule. +* Improve permission checks in the `/computercraft` command. + # New features in CC: Tweaked 1.101.2 * Error messages in `edit` are now displayed in red on advanced computers. diff --git a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md index 1b028cf17..f0cc19c7e 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md +++ b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md @@ -1,16 +1,19 @@ -New features in CC: Tweaked 1.101.2 +New features in CC: Tweaked 1.101.3 -* Error messages in `edit` are now displayed in red on advanced computers. -* Improvements to the display of errors in the shell and REPL. +* Improve syntax errors when missing commas in tables, and on trailing commas in parameter lists. +* `speaker` program now reports an error on common unsupported audio formats. +* Small optimisations to the `window` API. Several bug fixes: -* Fix `import.lua` failing to upload a file. -* Fix several issues with sparse Lua tables (Shiranuit). -* Computer upgrades now accept normal computers, rather than uselessly allowing you to upgrade an advanced computer to an advanced computer! -* Correctly clamp speaker volume. -* Fix rednet queueing the wrong message when sending a message to the current computer. -* Fix the Lua VM crashing when a `__len` metamethod yields. -* Trim spaces from filesystem paths. -* Correctly format 12AM/PM with `%I`. +* Fix the REPL syntax reporting crashing on valid parses. +* Ignore metatables in `textutils.serialize`. +* Fix `gps.locate` returning `nan` when receiving a duplicate location (Wojbie). +* Ignore metatables in `textutils.serialize`. +* Fix wireless turtles having an invalid model. +* Fix crash when turtles are exploded by a null explosion. +* Lua REPL no longer accepts `)(` as a valid expression. +* Fix several inconsistencies with `require`/`package.path` in the Lua REPL (Wojbie). +* Fix private several IP address ranges not being blocked by the `$private` rule. +* Improve permission checks in the `/computercraft` command. Type "help changelog" to see the full version history. diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/errors.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/errors.lua index 3bb665a9a..81c0cef9f 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/errors.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/errors.lua @@ -364,6 +364,48 @@ function errors.table_key_equals(start_pos, end_pos) } end +--[[- There is a trailing comma in this list of function arguments. + +@tparam number token The token id. +@tparam number token_start The start position of the token. +@tparam number token_end The end position of the token. +@tparam number prev The start position of the previous entry. +@treturn table The resulting parse error. +]] +function errors.missing_table_comma(token, token_start, token_end, prev) + expect(1, token, "number") + expect(2, token_start, "number") + expect(3, token_end, "number") + expect(4, prev, "number") + + return { + "Unexpected " .. token_names[token] .. " in table.", + annotate(token_start, token_end), + annotate(prev + 1, prev + 1, "Are you missing a comma here?"), + } +end + +--[[- There is a trailing comma in this list of function arguments. + +@tparam number comma_start The start position of the `,` token. +@tparam number comma_end The end position of the `,` token. +@tparam number paren_start The start position of the `)` token. +@tparam number paren_end The end position of the `)` token. +@treturn table The resulting parse error. +]] +function errors.trailing_call_comma(comma_start, comma_end, paren_start, paren_end) + expect(1, comma_start, "number") + expect(2, comma_end, "number") + expect(3, paren_start, "number") + expect(4, paren_end, "number") + + return { + "Unexpected " .. code(")") .. " in function call.", + annotate(paren_start, paren_end), + annotate(comma_start, comma_end, "Tip: Try removing this " .. code(",") .. "."), + } +end + -------------------------------------------------------------------------------- -- Statement parsing errors -------------------------------------------------------------------------------- diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/init.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/init.lua index 6b52f1ce0..8166d06d1 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/init.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/init.lua @@ -17,6 +17,8 @@ local error_printer = require "cc.internal.error_printer" local error_sentinel = {} local function make_context(input) + expect(1, input, "string") + local context = {} local lines = { 1 } @@ -69,8 +71,9 @@ local function parse(input, start_symbol) expect(2, start_symbol, "number") local context = make_context(input) - function context.report(msg) - expect(1, msg, "table") + function context.report(msg, ...) + expect(1, msg, "table", "function") + if type(msg) == "function" then msg = msg(...) end error_printer(context, msg) error(error_sentinel) end @@ -106,8 +109,9 @@ local function parse_repl(input) local context = make_context(input) local last_error = nil - function context.report(msg) - expect(1, msg, "table") + function context.report(msg, ...) + expect(1, msg, "table", "function") + if type(msg) == "function" then msg = msg(...) end last_error = msg error(error_sentinel) end @@ -120,22 +124,35 @@ local function parse_repl(input) assert(coroutine.resume(parsers[i], context, coroutine.yield, start_code)) end + -- Run all parsers together in parallel, feeding them one token at a time. + -- Once all parsers have failed, report the last failure (corresponding to + -- the longest parse). local ok, err = pcall(function() local parsers_n = #parsers while true do local token, start, finish = lexer() - local stop = true + local all_failed = true for i = 1, parsers_n do local parser = parsers[i] - if coroutine.status(parser) ~= "dead" then - stop = false + if parser then local ok, err = coroutine.resume(parser, token, start, finish) - if not ok and err ~= error_sentinel then error(err, 0) end + if ok then + -- This parser accepted our input, succeed immediately. + if coroutine.status(parser) == "dead" then return end + + all_failed = false -- Otherwise continue parsing. + elseif err ~= error_sentinel then + -- An internal error occurred: propagate it. + error(err, 0) + else + -- The parser failed, stub it out so we don't try to continue using it. + parsers[i] = false + end end end - if stop then error(error_sentinel) end + if all_failed then error(error_sentinel) end end end) diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/lexer.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/lexer.lua index f4f5159c5..0f260c560 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/lexer.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/lexer.lua @@ -92,7 +92,7 @@ local function lex_number(context, str, start) local contents = sub(str, start, pos - 1) if not tonumber(contents) then -- TODO: Separate error for "2..3"? - context.report(errors.malformed_number(start, pos - 1)) + context.report(errors.malformed_number, start, pos - 1) end return tokens.NUMBER, pos - 1 @@ -114,14 +114,14 @@ local function lex_string(context, str, start_pos, quote) return tokens.STRING, pos elseif c == "\n" or c == "\r" or c == "" then -- We don't call newline here, as that's done for the next token. - context.report(errors.unfinished_string(start_pos, pos, quote)) + context.report(errors.unfinished_string, start_pos, pos, quote) return tokens.STRING, pos - 1 elseif c == "\\" then c = sub(str, pos + 1, pos + 1) if c == "\n" or c == "\r" then pos = newline(context, str, pos + 1, c) elseif c == "" then - context.report(errors.unfinished_string_escape(start_pos, pos, quote)) + context.report(errors.unfinished_string_escape, start_pos, pos, quote) return tokens.STRING, pos elseif c == "z" then pos = pos + 2 @@ -129,7 +129,7 @@ local function lex_string(context, str, start_pos, quote) local next_pos, _, c = find(str, "([%S\r\n])", pos) if not next_pos then - context.report(errors.unfinished_string(start_pos, #str, quote)) + context.report(errors.unfinished_string, start_pos, #str, quote) return tokens.STRING, #str end @@ -192,7 +192,7 @@ local function lex_long_str(context, str, start, len) elseif c == "[" then local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "[") if ok and boundary_pos - pos == len and len == 1 then - context.report(errors.nested_long_str(pos, boundary_pos)) + context.report(errors.nested_long_str, pos, boundary_pos) end pos = boundary_pos @@ -234,12 +234,12 @@ local function lex_token(context, str, pos) local end_pos = lex_long_str(context, str, boundary_pos + 1, boundary_pos - pos) if end_pos then return tokens.STRING, end_pos end - context.report(errors.unfinished_long_string(pos, boundary_pos, boundary_pos - pos)) + context.report(errors.unfinished_long_string, pos, boundary_pos, boundary_pos - pos) return tokens.ERROR, #str elseif pos + 1 == boundary_pos then -- Just a "[" return tokens.OSQUARE, pos else -- Malformed long string, for instance "[=" - context.report(errors.malformed_long_string(pos, boundary_pos, boundary_pos - pos)) + context.report(errors.malformed_long_string, pos, boundary_pos, boundary_pos - pos) return tokens.ERROR, boundary_pos end @@ -256,7 +256,7 @@ local function lex_token(context, str, pos) local end_pos = lex_long_str(context, str, boundary_pos + 1, boundary_pos - comment_pos) if end_pos then return tokens.COMMENT, end_pos end - context.report(errors.unfinished_long_comment(pos, boundary_pos, boundary_pos - comment_pos)) + context.report(errors.unfinished_long_comment, pos, boundary_pos, boundary_pos - comment_pos) return tokens.ERROR, #str end end @@ -313,18 +313,18 @@ local function lex_token(context, str, pos) if end_pos - pos <= 3 then local contents = sub(str, pos, end_pos) if contents == "&&" then - context.report(errors.wrong_and(pos, end_pos)) + context.report(errors.wrong_and, pos, end_pos) return tokens.AND, end_pos elseif contents == "||" then - context.report(errors.wrong_or(pos, end_pos)) + context.report(errors.wrong_or, pos, end_pos) return tokens.OR, end_pos elseif contents == "!=" or contents == "<>" then - context.report(errors.wrong_ne(pos, end_pos)) + context.report(errors.wrong_ne, pos, end_pos) return tokens.NE, end_pos end end - context.report(errors.unexpected_character(pos)) + context.report(errors.unexpected_character, pos) return tokens.ERROR, end_pos end end diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua index a78466880..66c5a1d9a 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua @@ -85,43 +85,61 @@ local function line_end_position(context, previous, token) end end +local expr_tokens = {} +for _, v in pairs { tokens.STRING, tokens.NUMBER, tokens.TRUE, tokens.FALSE, tokens.NIL } do + expr_tokens[v] = true +end + local error_messages = { function(context, stack, stack_n, regs, token) - -- parse_errors.mlyl, line 21 + -- parse_errors.mlyl, line 26 if token.v == tokens.EQUALS then return errors.table_key_equals(token.s, token.e) end end, function(context, stack, stack_n, regs, token) - -- parse_errors.mlyl, line 29 + -- parse_errors.mlyl, line 34 if token.v == tokens.EQUALS then return errors.use_double_equals(token.s, token.e) end end, + function(context, stack, stack_n, regs, token) + -- parse_errors.mlyl, line 42 + if expr_tokens[token.v] then + return errors.missing_table_comma(token.v, token.s, token.e, stack[stack_n + 2]) + end + end, + function(context, stack, stack_n, regs, token) + local comma = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } + -- parse_errors.mlyl, line 52 + if token.v == tokens.CPAREN then + return errors.trailing_call_comma(comma.s, comma.e, token.s, token.e) + end + end, function(context, stack, stack_n, regs, token) local lp = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } - -- parse_errors.mlyl, line 37 + -- parse_errors.mlyl, line 60 return errors.unclosed_brackets(lp.s, lp.e, token.v, token.s, token.e) end, function(context, stack, stack_n, regs, token) local lp = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } - -- parse_errors.mlyl, line 39 + -- parse_errors.mlyl, line 62 return errors.unclosed_brackets(lp.s, lp.e, token.v, token.s, token.e) end, function(context, stack, stack_n, regs, token) local lp = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } - -- parse_errors.mlyl, line 41 + -- parse_errors.mlyl, line 64 return errors.unclosed_brackets(lp.s, lp.e, token.v, token.s, token.e) end, function(context, stack, stack_n, regs, token) local loc = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } - -- parse_errors.mlyl, line 46 + -- parse_errors.mlyl, line 69 if token.v == tokens.DOT then return errors.local_function_dot(loc.s, loc.e, token.s, token.e) end end, function(context, stack, stack_n, regs, token) - -- parse_errors.mlyl, line 54 + -- parse_errors.mlyl, line 77 local end_pos = stack[stack_n + 2] -- Hack to get the last position if is_same_line(context, end_pos, token) then return errors.standalone_name(token.s) @@ -131,22 +149,22 @@ local error_messages = { end, function(context, stack, stack_n, regs, token) local start = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } - -- parse_errors.mlyl, line 65 + -- parse_errors.mlyl, line 88 return errors.expected_then(start.s, start.e, line_end_position(context, stack[stack_n + 2], token)) end, function(context, stack, stack_n, regs, token) local start = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } - -- parse_errors.mlyl, line 93 + -- parse_errors.mlyl, line 116 return errors.expected_end(start.s, start.e, token.v, token.s, token.e) end, function(context, stack, stack_n, regs, token) local func = { s = stack[regs[2] + 1], e = stack[regs[2] + 2] } local loc = { s = stack[regs[3] + 1], e = stack[regs[3] + 2] } - -- parse_errors.mlyl, line 97 + -- parse_errors.mlyl, line 120 return errors.expected_end(loc.s, func.e, token.v, token.s, token.e) end, function(context, stack, stack_n, regs, token) - -- parse_errors.mlyl, line 101 + -- parse_errors.mlyl, line 124 if token.v == tokens.END then return errors.unexpected_end(token.s, token.e) elseif token ~= tokens.EOF then @@ -154,20 +172,20 @@ local error_messages = { end end, function(context, stack, stack_n, regs, token) - -- parse_errors.mlyl, line 111 + -- parse_errors.mlyl, line 134 return errors.expected_function_args(token.v, token.s, token.e) end, function(context, stack, stack_n, regs, token) - -- parse_errors.mlyl, line 115 + -- parse_errors.mlyl, line 138 return errors.expected_expression(token.v, token.s, token.e) end, function(context, stack, stack_n, regs, token) - -- parse_errors.mlyl, line 119 + -- parse_errors.mlyl, line 142 return errors.expected_var(token.v, token.s, token.e) end, } -local error_program_start, error_program = 465, "\6\1\0\3\5\176\0\3\0060\0\3\6\30\0\3\6\22\0\3\6\14\0\3\6\3\0\3\5\245\0\3\5\235\0\3\5\218\0\3\5\205\0\3\5\185\0\1\0\3\5\176\0\3\5\168\0\3\5\160\0\3\5\160\0\3\5\160\0\3\5\160\0\3\5\160\0\3\5\136\0\3\5\136\0\3\5\160\0\3\5\152\0\3\5\144\0\3\5\144\0\3\5\144\0\3\5\136\0\3\5\128\0\3\5f\0\3\5^\0\3\5V\0\3\5N\0\3\5F\0\3\4\148\0\3\5<\0\3\0058\0\3\5-\0\3\4\161\0\3\4\157\0\3\4\149\0\3\1\220\0\3\4\148\0\3\4\142\0\3\4\136\0\3\4\131\0\3\4y\0\3\4q\0\3\4f\0\3\4E\0\3\4@\0\1\0\3\4;\0\1\0\3\0046\0\3\4/\0\3\3\146\0\3\3\138\0\3\3\134\0\3\3~\0\3\3n\0\3\3\130\0\3\3~\0\3\3~\0\3\3z\0\3\3v\0\3\2\240\0\3\3r\0\3\2\225\0\3\2\225\0\3\3n\0\3\3g\0\3\2\225\0\3\2\240\0\3\2\225\0\3\2\225\0\3\2\225\0\3\2\225\0\3\2\232\0\3\2\225\0\3\2\225\0\3\2\225\0\3\2\225\0\3\2X\0\3\2P\0\3\2D\0\3\2L\0\3\2H\0\3\2 \0\3\2\28\0\3\2D\0\3\2@\0\3\2<\0\3\0028\0\3\2\24\0\3\0024\0\3\2(\0\3\0020\0\3\2,\0\3\2(\0\3\2$\0\3\2 \0\3\2\28\0\3\2\24\0\3\2\20\0\3\2\16\0\3\2\12\0\3\2\8\0\3\2\8\0\3\2\4\0\3\2\0\0\3\1\252\0\3\1\252\0\3\1\243\0\3\1\229\0\3\1\235\0\3\1\229\0\3\1\220\0\5\0\0\3\4~\0\3\6;\0\5\0\14\1\0\3\0046\0\1\0\3\0046\0\3\6;\0\3\2\0\0\5\0\17\1\0\3\4;\0\3\1\243\0\3\1\243\0\3\2\0\0\3\6?\0\3\6C\0\3\4@\0\3\6G\0\3\0020\0\3\4f\0\3\2\28\0\3\2 \0\3\2\24\0\3\2P\0\3\2D\0\3\2@\0\3\0024\0\3\6K\0\3\2(\0\3\2,\0\3\6o\0\3\2H\0\3\6o\0\3\2\28\0\5\0\15\1\0\3\0046\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\179\0\3\6\179\0\3\6\179\0\3\6\171\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\3\2\225\0\3\2\225\0\3\2\225\0\3\3~\0\5\0\190\3\6v\0\3\3n\0\3\7\7\0\5\0\8\3\7\143\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\179\0\3\6\179\0\3\6\179\0\3\6\171\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\5\1\3\3\6v\0\3\3~\0\3\7\150\0\3\7\7\0\3\7\143\0\3\2\225\0\3\7\154\0\3\3\138\0\3\7\158\0\3\6\211\0\5\0\31\1\0\3\6\250\0\3\7\172\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\4@\0\1\0\3\0046\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\179\0\3\6\179\0\3\6\179\0\3\6\171\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\3\2\225\0\3\2\225\0\3\2\225\0\3\3~\0\3\7\162\0\3\2P\0\3\6C\0\3\6;\0\5\1f\3\6v\0\4\2\0\1\6\4\3\0\1\6\4\6\0\0\6\4\n\0\0\6\1\0\3\7\180\0\3\7\204\0\3\7\200\0\3\7\196\0\3\7\189\0\1\0\3\7\180\0\4\n\0\0\5\0\214\3\7\218\0\4\n\0\0\3\7\253\0\4\11\0\0\6\4\12\0\0\6\4\r\0\0\6\1\0\3\0046\0\1\0\3\4;\0\6\3\7\172\0\3\2P\0\5\0\18\6\3\7\162\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\4@\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\179\0\3\6\179\0\3\6\179\0\3\6\171\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\3\2\225\0\3\2\225\0\3\2\225\0\3\3~\0\3\6C\0\5\1\233\3\6v\0\3\7\196\0\5\0\30\6\3\7\200\0\1\0\3\8\4\0\4\1\0\0\5\0B\6\4\1\0\0\3\8\t\0\4\1\0\0\3\2\0\0\4\1\0\0\3\2D\0\4\1\0\0\3\8\r\0\1\0\3\0046\0\3\2,\0\3\2D\0\3\2\28\0\4\1\0\0\5\0T\6\4\1\0\0\3\8\17\0\4\1\0\0\3\8\21\0\4\1\0\0\3\8\25\0\4\1\0\0\3\8\29\0\4\1\0\0\3\8!\0\4\n\0\0\4\8\0\1\6\4\11\0\0\3\8%\0\3\7\218\0\3\7\204\0\3\7\189\0\5\1\19\1\0\3\7\180\0\3\2\0\0\4\1\0\0\4\0\0\0\5\0008\1\0\3\4;\0\4\1\0\0\1\0\3\0046\0\4\1\0\0\1\0\3\8+\0\3\7\204\0\4\n\0\0\5\0%\3\0080\0\4\n\0\0\3\7\204\0\4\n\0\0\3\0086\0\4\n\0\0\1\0\3\7\180\0\3\7\204\0\3\8<\0\4\n\0\0\5\0\224\3\8@\0\3\1\229\0\3\8D\0\3\2\16\0\3\8R\0\3\0028\0\1\0\3\5\176\0\3\6\30\0\3\6\22\0\3\6\14\0\3\6\3\0\1\0\3\5\176\0\3\4E\0\5\1L\3\0060\0\4\1\0\0\6\4\1\0\0\3\2\4\0\4\1\0\0\3\1\243\0\4\1\0\0\3\4f\0\4\1\0\0\3\2,\0\4\1\0\0\3\2P\0\4\1\0\0\3\8k\0\4\1\0\0\3\8\203\0\4\1\0\0\3\t6\0\4\1\0\0\3\tu\0\4\1\0\0\3\t\188\0\4\1\0\0\3\t\255\0\4\1\0\0\4\0\0\0\3\1\243\0\4\2\0\1\4\1\0\0\6\4\4\0\1\4\1\0\0\6\4\7\0\1\4\1\0\0\6\4\3\0\1\4\1\0\0\4\0\0\0\6\3\3z\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\179\0\3\6\179\0\3\6\179\0\3\6\171\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\3\2\225\0\3\2\225\0\3\2\225\0\3\3~\0\3\6o\0\5\2L\3\6v\0\3\2\240\0\3\n\142\0\3\5-\0\3\4\157\0\1\0\3\n\149\0\4\11\0\0\5\0P\6\4\8\0\1\6\3\7\204\0\5\0;\3\0080\0\3\7\204\0\3\0086\0\1\0\3\7\180\0\3\7\204\0\3\8<\0\5\0\230\3\8@\0\1\0\3\7\180\0\3\7\204\0\3\7\200\0\3\7\196\0\3\7\189\0\1\0\3\7\180\0\5\1U\3\7\218\0\4\7\0\1\6\3\2\4\0\3\8k\0\3\8\203\0\3\t6\0\3\tu\0\3\t\188\0\3\t\255\0\1\0\3\n\149\0\4\4\0\1\6\1\0\3\n\154\0\1\0\3\n\163\0\3\n\171\0\3\n\175\0\3\4y\0\1\0\3\5\176\0\3\n\179\0\5\0\190\3\6\30\0\1\0\3\0046\0\3\2,\0\3\2D\0\3\2\28\0\5\0\136\6\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\5\2\143\3\6v\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\171\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\5\2\212\3\6v\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\5\2c\3\6v\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\203\0\3\6\187\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\5\3\23\3\6v\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\187\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\5\3 \3\6v\0\1\0\3\6\250\0\1\0\3\6\241\0\1\0\3\6\232\0\1\0\3\6\223\0\3\6\211\0\3\6\203\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\195\0\3\6\171\0\3\6\171\0\3\6\195\0\3\6\187\0\3\6\179\0\3\6\179\0\3\6\179\0\3\6\171\0\3\6\163\0\3\6\155\0\3\6\147\0\3\6\147\0\3\6\139\0\3\6\131\0\3\6{\0\3\2\225\0\3\2\225\0\3\2\225\0\3\7\143\0\3\7\7\0\3\3~\0\5\3\163\3\6v\0\4\5\0\1\6\2\1\0\1\0\3\n\189\0\4\8\0\1\3\4\148\0\3\7\204\0\3\n\198\0\4\n\0\0\1\0\3\n\154\0\4\t\1\2\6\3\n\205\0\5\0u\3\n\209\0\3\7\204\0\3\n\213\0\3\n\205\0" -local error_tbl_ks, error_tbl_vs, error_tbl = 1, 2, "\0\0\193\1\0\141\2\0\0\3\0\189\0\0\0\5\1A\0\0\0\7\1=\0\0\0\t\0\201\0\0\0\11\0019\0\0\0\r\0015\0\0\0\15\0\213\16\0\181\17\0\207\18\1\205\19\0\161\20\1\201\21\0\0\22\1\197\23\0\173\24\1\153\25\1\193\26\0\11\27\0'\28\0011\29\1-\30\1)\31\0\145 \1%!\1!\"\1\29\21\1\216$\1\25%\1\21\0\0\0'\1\17(\1\r)\1\t*\0\27\0\0\0,\0i\0\0\0.\0e\0\0\0000\0a\0\0\0002\0]\0\0\0004\0Y\0\0\0006\0U\0\0\0008\0Q\0\0\0:\0M\0\0\0<\0I\0\0\0>\0E\0\0\0@\0A\0\0\0B\0=\0\0\0D\0009\0\0\0F\0005G\0\225H\0\207I\1E\0\0\0K\0qL\0\173M\1\5N\0mO\0\0P\0\221Q\0\0R\0\0S\1\1T\0\253U\0\249V\0\245W\0\241X\0\27Y\0\0R\2\236[\0}\\\0\217\0\0\0^\0y_\1\189`\1\185a\1\181b\1\177c\1\173d\0#e\0\169f\0\237g\0\31h\0\233i\0\229j\0\129k\0\7l\0'm\1\149n\1\145o\1\141p\1\137q\0\193r\0\141b\1\239t\0uu\0\0v\0\177w\0+x\0\23y\0'z\1\133{\0\153|\1\129n\2T~\1}\127\0\177\128\0\149b\3\142\130\0\129\131\0\19\132\0\141\133\0\133\134\1yu\4\153\136\0\129\137\0\15\138\0\137\139\0/\140\0'\141\1u\142\0\177\143\0\157\144\0\181\145\0\0\146\1\169\147\0\0\148\1\165\149\0\23\150\0'\151\1q\152\0\177\153\0\165b\5\214\155\0\129H\5n\157\0\129\158\0\7\159\0'\160\1m\131\0054\162\0\129\163\0\7\164\0'\165\1i\166\0\0\137\0054\168\0\0\169\0\7\170\0'\171\1e\172\0\1\173\0'\174\1a\175\1]\176\1Y\177\1U\178\1Q\179\0\0\180\0\197\144\5\255\182\1M\183\0\177\184\1\161\185\1\157\186\0\185\187\1I\188\0\0\189\0\0\190\0\0\191\0\0\192\0\0\3\0\0n\5|\129\5@\6\2\209u\7\166\8\2s\t\2a\n\2m\135\5@\12\2\213\144\7\185\14\2\217\0\0\0\16\8HZ\n\194H\8Y}\5x]\n\194" .. ("\0"):rep(42) .. "#\2m\0\0\0\0\0\0&\2\221\0\0\0\0\0\0\0\0\0\0\0\0+\2\177\0\0\0-\2\173\0\0\0/\2\169\0\0\0001\2\165\26\4P3\2\161}\n\1945\2\157\0\0\0007\2\153n\8g9\2\149\0\0\0;\2\145\0\0\0=\2\141\0\0\0?\2\137\0\0\0A\2\133\0\0\0C\2\129\0\0\0E\2}\0\0\0}\8cH\2s\0\0\0J\2\181\181\5t\0\0\0\8\3\t\t\2\247\n\3\3\0\0\0\154\n\194" .. ("\0"):rep(24) .. "Z\2\205\0\0\0\0\0\0]\2\201\0\0\0\0\0\0\0\0\0\0\0\0b\2y" .. ("\0"):rep(15) .. "#\3\3\0\0\0\0\0\0I\6,\181\n\194\0\0\0n\2\197\26\5\197+\3GI\7\214-\3Cs\2\193/\3?\0\0\0001\3;w\8N3\0037\0\0\0005\0033\0\0\0007\3/}\2\1899\3+\181\8_;\3'\129\2g=\3#\0\0\0?\3\31\0\0\0A\3\27\135\2gC\3\23q\0\0E\3\19\0\0\0j\6(H\3\t\0\6kJ\3Kx\4\\\0\0\0j\7\210" .. ("\0"):rep(24) .. "\131\4X\0\0\0\0\0\0\0\0\0Z\3c~\6,\137\4T]\3_\139\4`\0\0\0\0\0\0~\7\214b\3\15\26\6U\0\0\0\0\0\0\3\3\177\0\0\0\149\4\\\6\4\15\0\0\0\8\3\171\26\7\231\n\3\165n\3[\12\4\19\181\2\185\14\4\23\183\0\0s\3W\17\3\181\0\0\0\0\0\0\0\0\0\21\4+\0\0\0\0\0\0k\5\193\0\0\0}\3S\0\0\0\172\4J\0\0\0\129\2\253\0\0\0\0\0\0\0\0\0\168\6,#\3\165\135\2\253x\5\201&\4\27\0\0\0\168\7\214\0\0\0\0\0\0+\3\239\0\0\0-\3\235\0\0\0/\3\231\182\6,1\3\227\0\0\0003\3\223\0\0\0005\3\219\182\7\2147\3\215\190\6,9\3\211\0\0\0;\3\207\0\0\0=\3\203\190\7\214?\3\199\0\0\0A\3\195\149\5\201C\3\191\0\0\0E\3\187\0\0\0\0\0\0H\3\171\0\0\0J\3\243\158\5\193\0\0\0\0\0\0\0\0\0\0\0\0\163\5\193\0\0\0\181\3O\0\0\0\0\0\0\0\0\0\169\5\193q\6k\0\0\0\0\0\0Z\4\11\0\0\0\0\0\0]\4\7x\6a\0\0\0q" .. ("\0"):rep(20) .. "x\7\243\0\0\0\131\6]\0\0\0\0\0\0\0\0\0\0\0\0n\4\3\137\6Y\0\0\0\139\6e\131\7\239s\3\255\0\0\0u\4#v\3\155\0\0\0\137\7\235\0\0\0\139\7\247\149\6a\0\0\0}\3\251\0\0\0\127\4\31\0\0\0\129\3\159\0\0\0\0\0\0\149\7\243\0\0\0\3\4\193\135\3\159\0\0\0\6\5\25\0\0\0\8\4\183\t\4\165\n\4\177\142\3\155\12\5\29\0\0\0\14\5!\172\6O" .. ("\0"):rep(15) .. "\152\0\0\0\0\0\0\0\0\172\7\225" .. ("\0"):rep(30) .. "#\4\177\0\0\0\0\0\0&\5%\0\0\0\0\0\0\0\0\0\0\0\0+\4\249\0\0\0-\4\245\0\0\0/\4\241\0\0\0001\4\237\181\3\2473\4\233\183\4'5\4\229\0\0\0007\4\225\0\0\0009\4\221\0\0\0;\4\217\0\0\0=\4\213\0\0\0?\4\209\0\0\0A\4\205\0\0\0C\4\201\0\0\0E\4\197\0\0\0\0\0\0H\4\183\0\0\0J\4\253" .. ("\0"):rep(45) .. "Z\5\21\0\0\0\0\0\0]\5\17\0\0\0\0\0\0\0\0\0\0\0\0b\4\189\0\0\0\0\0\0\0\0\0\3\7\139\0\0\0\0\0\0\6\7{\0\0\0\8\7\29\t\7\11\n\7\23n\5\r\12\7\127\0\0\0\14\7\131\0\0\0s\5\t" .. ("\0"):rep(27) .. "}\5\5\0\0\0\0\0\0\0\0\0\129\4\171\8\tO\t\t=\n\tI\0\0\0#\7\23\135\4\171\0\0\0&\7\135\0\0\0\0\0\0\0\0\0\0\0\0+\7[\0\0\0-\7W\0\0\0/\7S\0\0\0001\7O\0\0\0003\7K\0\0\0005\7G\0\0\0007\7C\0\0\0009\7?#\tI;\7;\0\0\0=\0077\0\0\0?\0073\0\0\0A\7/\0\0\0C\7+\0\0\0E\7'\0\0\0\0\0\0H\7\29\0\0\0J\7_\8\8\129\t\8o\n\8{\0\0\0\0\0\0\0\0\0\0\0\0\181\5\1\0\0\0\183\5)" .. ("\0"):rep(15) .. "Z\7w\0\0\0\0\0\0]\7s\0\0\0H\tO\0\0\0J\tYb\7#\0\0\0\0\0\0\0\0\0#\8{" .. ("\0"):rep(21) .. "n\7o\0\0\0\0\0\0Z\tq\0\0\0s\7k]\tm\0\0\0003\8\171\0\0\0005\8\167b\tU7\8\163\0\0\0\0\0\0}\7g;\8\159\0\0\0=\8\155\129\7\17?\8\151\0\0\0A\8\147n\tiC\8\143\135\7\17E\8\139\0\0\0s\teH\8\129\0\0\0J\8\175\0\0\0\0\0\0\8\8\228\t\8\210\n\8\222\0\0\0}\ta\0\0\0\0\0\0\0\0\0\129\tC\0\0\0\0\0\0\0\0\0\0\0\0Z\8\199\135\tC\0\0\0]\8\195\0\0\0\0\0\0\0\0\0\0\0\0b\8\135" .. ("\0"):rep(15) .. "#\8\222" .. ("\0"):rep(15) .. "n\8\191\0\0\0+\t\22\0\0\0\181\7cs\8\187\183\0\0\0\0\0\0\0\0\0\0\0003\t\18\0\0\0005\t\14\0\0\0007\t\n}\8\1839\t\6\0\0\0;\t\2\129\8u=\8\254\0\0\0?\8\250\0\0\0A\8\246\135\8uC\8\242\181\t]E\8\238\0\0\0\0\0\0H\8\228\0\0\0J\t\26\8\t\142\t\t|\n\t\136" .. ("\0"):rep(18) .. "\8\t\213\t\t\195\n\t\207\0\0\0\0\0\0\0\0\0Z\t2\0\0\0\0\0\0]\t.\0\0\0\0\0\0\0\0\0\0\0\0b\8\234\0\0\0\0\0\0\0\0\0#\t\136" .. ("\0"):rep(21) .. "n\t*#\t\207\181\8\179\0\0\0\0\0\0s\t&\0\0\0\0\0\0003\t\156" .. ("\0"):rep(18) .. "}\t\"\0\0\0003\t\223\0\0\0\129\8\216" .. ("\0"):rep(15) .. "\135\8\216E\t\152\0\0\0\0\0\0H\t\142\0\0\0J\t\160" .. ("\0"):rep(18) .. "H\t\213\0\0\0J\t\227" .. ("\0"):rep(18) .. "Z\t\184\0\0\0\0\0\0]\t\180\0\0\0\0\0\0\0\0\0\0\0\0b\t\148Z\t\251\0\0\0\0\0\0]\t\247\0\0\0\0\0\0\0\0\0\0\0\0b\t\219\0\0\0\0\0\0n\t\176\0\0\0\0\0\0\0\0\0\181\t\30s\t\172\0\0\0\0\0\0\0\0\0n\t\243\0\0\0\0\0\0\0\0\0\0\0\0s\t\239}\t\168\0\0\0\0\0\0\0\0\0\129\t\130\0\0\0\0\0\0\0\0\0\0\0\0}\t\235\135\t\130\0\0\0\0\0\0\129\t\201" .. ("\0"):rep(15) .. "\135\t\201\0\0\0\6\nv\0\0\0\8\n\24\t\n\6\n\n\18\0\0\0\12\nz\0\0\0\14\n~" .. ("\0"):rep(54) .. "!\n\130\0\0\0#\n\18\0\0\0\0\0\0&\n\138\0\0\0\0\0\0\181\t\164\0\0\0+\nV\0\0\0-\nR\0\0\0/\nN\0\0\0001\nJ\181\t\2313\nF\0\0\0005\nB\0\0\0007\n>\0\0\0009\n:\0\0\0;\n6\0\0\0=\n2\0\0\0?\n.\0\0\0A\n*\0\0\0C\n&\0\0\0E\n\"\0\0\0\0\0\0H\n\24\0\0\0J\nZ" .. ("\0"):rep(21) .. "R\n\134" .. ("\0"):rep(21) .. "Z\nr\0\0\0\0\0\0]\nn\0\0\0\0\0\0\0\0\0\0\0\0b\n\30" .. ("\0"):rep(33) .. "n\nj\0\0\0\0\0\0\0\0\0\0\0\0s\nf" .. ("\0"):rep(27) .. "}\nb\0\0\0\0\0\0\0\0\0\129\n\12" .. ("\0"):rep(15) .. "\135\n\12" .. ("\0"):rep(129) .. "\179\n\130\0\0\0\181\n^" +local error_program_start, error_program = 471, "\6\1\0\3\5\186\0\3\6B\0\3\0060\0\3\6(\0\3\6 \0\3\6\21\0\3\6\7\0\3\5\253\0\3\5\236\0\3\5\223\0\1\0\3\5\203\0\3\5\195\0\1\0\3\5\186\0\3\5\178\0\3\5\170\0\3\5\170\0\3\5\170\0\3\5\170\0\3\5\170\0\3\5\146\0\3\5\146\0\3\5\170\0\3\5\162\0\3\5\154\0\3\5\154\0\3\5\154\0\3\5\146\0\3\5\138\0\3\5p\0\3\5h\0\3\5`\0\3\5X\0\3\5P\0\3\4\158\0\3\5F\0\3\5B\0\3\0057\0\3\4\171\0\3\4\167\0\3\4\159\0\3\1\226\0\3\4\158\0\3\4\152\0\3\4\146\0\3\4\141\0\3\4\131\0\3\4{\0\3\4p\0\3\4O\0\3\4J\0\1\0\3\4E\0\1\0\3\4@\0\3\0043\0\3\4(\0\3\3\139\0\3\3\131\0\3\3\127\0\3\3w\0\3\3g\0\3\3{\0\3\3w\0\3\3w\0\3\3s\0\3\3o\0\3\2\233\0\3\3k\0\3\2\218\0\3\2\218\0\3\3g\0\3\3`\0\3\2\218\0\3\2\233\0\3\2\218\0\3\2\218\0\3\2\218\0\3\2\218\0\3\2\225\0\3\2\218\0\3\2\218\0\3\2\218\0\3\2\218\0\3\2Q\0\3\2I\0\3\2=\0\3\2E\0\3\2A\0\3\2\25\0\3\2\21\0\3\2=\0\3\0029\0\3\0025\0\3\0021\0\3\2\17\0\3\2-\0\3\2!\0\3\2)\0\3\2%\0\3\2!\0\3\2\29\0\3\2\25\0\3\2\21\0\3\2\17\0\3\2\r\0\3\2\t\0\3\2\5\0\3\2\1\0\3\2\1\0\3\1\253\0\3\1\249\0\3\1\245\0\3\1\245\0\3\1\235\0\3\1\241\0\3\1\235\0\3\1\226\0\5\0\0\3\4\136\0\3\6M\0\5\0\14\1\0\3\4@\0\1\0\3\4@\0\3\6M\0\3\6U\0\3\6U\0\3\1\249\0\3\6^\0\3\6b\0\3\4J\0\3\6f\0\3\2)\0\3\4p\0\3\2\21\0\3\2\25\0\3\2\17\0\3\2I\0\3\2=\0\3\0029\0\3\2-\0\3\6j\0\3\2!\0\3\2%\0\3\6\142\0\3\2A\0\3\6\142\0\3\2\21\0\5\0\5\1\0\3\4@\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\210\0\3\6\210\0\3\6\210\0\3\6\202\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\3\2\218\0\3\2\218\0\3\2\218\0\3\3w\0\5\0\190\3\6\149\0\3\3g\0\3\7.\0\5\0\8\3\7\182\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\210\0\3\6\210\0\3\6\210\0\3\6\202\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\5\1\3\3\6\149\0\3\3w\0\3\7\189\0\3\7.\0\3\7\182\0\3\2\218\0\3\7\193\0\3\3\131\0\3\7\197\0\3\7\r\0\5\0\27\1\0\3\7\29\0\3\7\211\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\4J\0\1\0\3\4@\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\210\0\3\6\210\0\3\6\210\0\3\6\202\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\3\2\218\0\3\2\218\0\3\2\218\0\3\3w\0\3\7\201\0\3\2I\0\3\6b\0\3\6M\0\5\1f\3\6\149\0\3\1\249\0\4\2\0\0\5\0\31\1\0\3\4E\0\4\4\0\1\6\4\5\0\1\6\4\8\0\0\6\4\12\0\0\6\1\0\3\7\219\0\3\7\243\0\3\7\239\0\3\7\235\0\3\7\228\0\1\0\3\7\219\0\4\12\0\0\5\0\214\3\8\1\0\4\12\0\0\3\8$\0\4\r\0\0\6\4\14\0\0\6\4\15\0\0\6\1\0\3\4@\0\1\0\3\4E\0\6\3\7\211\0\3\2I\0\5\0\18\6\3\7\201\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\4J\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\210\0\3\6\210\0\3\6\210\0\3\6\202\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\3\2\218\0\3\2\218\0\3\2\218\0\3\3w\0\3\6b\0\5\1\233\3\6\149\0\3\7\235\0\5\0\30\6\3\7\239\0\1\0\3\8+\0\4\1\0\0\5\0B\6\4\1\0\0\3\0080\0\4\1\0\0\3\0084\0\4\1\0\0\3\2=\0\4\1\0\0\3\0088\0\1\0\3\4@\0\3\2%\0\3\2=\0\3\2\21\0\4\1\0\0\5\0T\6\4\1\0\0\3\8<\0\4\1\0\0\3\8@\0\4\1\0\0\3\8D\0\4\1\0\0\3\8H\0\4\1\0\0\3\8L\0\4\12\0\0\4\n\0\1\6\4\r\0\0\3\8P\0\4\14\0\0\3\8Z\0\3\8\1\0\3\7\243\0\3\7\228\0\5\1\19\1\0\3\7\219\0\3\8^\0\4\1\0\0\4\0\0\0\5\0008\1\0\3\8f\0\4\1\0\0\1\0\3\4@\0\4\1\0\0\1\0\3\8o\0\3\7\243\0\4\12\0\0\5\0%\3\8t\0\4\12\0\0\3\7\243\0\4\12\0\0\3\8z\0\4\12\0\0\1\0\3\7\219\0\3\7\243\0\3\8\128\0\4\12\0\0\5\0\224\3\8\132\0\3\1\235\0\3\1\249\0\5\0c\1\0\3\4E\0\3\8\136\0\3\2\t\0\3\8\150\0\3\0021\0\1\0\3\5\186\0\3\0060\0\3\6(\0\3\6 \0\3\6\21\0\1\0\3\5\186\0\3\4O\0\5\1L\3\6B\0\4\1\0\0\6\4\1\0\0\3\8\157\0\4\1\0\0\3\8\165\0\4\1\0\0\3\4p\0\4\1\0\0\3\2%\0\4\1\0\0\3\2I\0\4\1\0\0\3\8\192\0\4\1\0\0\3\t \0\4\1\0\0\3\t\139\0\4\1\0\0\3\t\202\0\4\1\0\0\3\n\17\0\4\1\0\0\3\nT\0\4\4\0\1\4\1\0\0\6\4\6\0\1\4\1\0\0\6\4\t\0\1\4\1\0\0\6\4\2\0\0\4\1\0\0\4\0\0\0\3\6U\0\4\5\0\1\4\2\0\0\4\1\0\0\4\0\0\0\6\3\3s\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\210\0\3\6\210\0\3\6\210\0\3\6\202\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\3\2\218\0\3\2\218\0\3\2\218\0\3\3w\0\3\6\142\0\5\2L\3\6\149\0\3\2\233\0\3\n\227\0\3\0057\0\3\4\167\0\1\0\3\n\234\0\4\r\0\0\5\0V\6\4\n\0\1\6\3\7\243\0\5\0=\3\8t\0\3\7\243\0\3\8z\0\1\0\3\7\219\0\3\7\243\0\3\8\128\0\5\0\230\3\8\132\0\1\0\3\7\219\0\3\7\243\0\3\7\239\0\3\7\235\0\3\7\228\0\1\0\3\7\219\0\5\1U\3\8\1\0\4\t\0\1\6\3\8\157\0\3\8\165\0\3\8\192\0\3\t \0\3\t\139\0\3\t\202\0\3\n\17\0\3\nT\0\1\0\3\n\234\0\3\n\239\0\5\0\198\6\4\2\0\0\3\6U\0\4\5\0\1\4\2\0\0\6\4\6\0\1\6\1\0\3\n\246\0\1\0\3\n\255\0\3\11\7\0\3\11\11\0\3\4\131\0\1\0\3\5\186\0\3\11\15\0\5\0\192\3\0060\0\3\0084\0\3\8^\0\5\0m\1\0\3\8f\0\1\0\3\4@\0\3\2%\0\3\2=\0\3\2\21\0\5\0\148\6\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\5\2\143\3\6\149\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\202\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\5\2\212\3\6\149\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\5\2c\3\6\149\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\234\0\3\6\218\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\5\3\23\3\6\149\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\218\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\5\3 \3\6\149\0\1\0\3\7\29\0\3\7\r\0\1\0\3\7\4\0\1\0\3\6\251\0\1\0\3\6\242\0\3\6\234\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\226\0\3\6\202\0\3\6\202\0\3\6\226\0\3\6\218\0\3\6\210\0\3\6\210\0\3\6\210\0\3\6\202\0\3\6\194\0\3\6\186\0\3\6\178\0\3\6\178\0\3\6\170\0\3\6\162\0\3\6\154\0\3\2\218\0\3\2\218\0\3\2\218\0\3\7\182\0\3\7.\0\3\3w\0\5\3\163\3\6\149\0\4\7\0\1\6\5\0\11\3\11\25\0\2\1\0\1\0\3\11\30\0\4\n\0\1\3\4\158\0\3\7\243\0\3\11'\0\4\12\0\0\1\0\3\n\246\0\4\3\0\1\6\4\11\1\2\6\3\11.\0\5\0\136\3\0112\0\3\7\243\0\3\0116\0\3\11.\0" +local error_tbl_ks, error_tbl_vs, error_tbl = 1, 2, "\0\0\199\1\0\147\2\0\0\3\0\195\0\0\0\5\1K\0\0\0\7\1G\0\0\0\t\0\207\0\0\0\11\1C\0\0\0\r\1?\0\0\0\15\0\223\16\0\187\17\0\213\18\1\211\19\0\167\20\1\207\21\0\0\22\1\203\23\0\179\24\1\163\25\1\199\26\0\11\27\0'\28\1;\29\0017\30\0013\31\0\151 \1/!\1+\"\1'\21\1\222$\1#%\1\31\0\0\0'\1\27(\1\23)\1\19*\0\27\0\0\0,\0o\0\0\0.\0k\0\0\0000\0g\0\0\0002\0c\0\0\0004\0_\0\0\0006\0[\0\0\0008\0W\0\0\0:\0S\0\0\0<\0O\0\0\0>\0K\0\0\0@\0G\0\0\0B\0C\0\0\0D\0?\0\0\0F\0;G\0\235H\0\213I\1OJ\0+K\0wL\0\179M\1\15N\0sO\0\0P\0\231Q\0\0R\0\0S\1\11T\1\7U\1\3V\0\255W\0\251X\0\27Y\0\0R\2\229[\0\131\\\0\227R\0\0^\0\127_\0\219`\1\195a\1\191b\1\187c\1\183d\0#e\0\175f\0\247g\0\31h\0\243i\0\239j\0\135k\0\7l\0'm\1\159n\1\155o\1\151p\1\147q\0\199r\0\147n\2Mt\0{u\0\0v\0\183w\0001x\0\23y\0'z\1\143{\0\159|\1\139b\3\135~\1\135\127\0\183\128\0\155b\4/\130\0\135\131\0\19\132\0\147\133\0\139\134\1\131u\4\163\136\0\135\137\0\15\138\0\143\139\0005\140\0'\141\1\127\142\0\183\143\0\163\144\0\187\145\0\0\146\1\179\147\0\0\148\1\175\149\0\23\150\0'\151\1{\152\0\183\153\0\171b\5\232\155\0\135H\5x\157\0\135\158\0\7\159\0'\160\1w\131\5>\162\0\135\163\0\7\164\0'\165\1s\166\0\0\137\5>\168\0\0\169\0\7\170\0'\171\1o\172\0\1\173\0'\174\1k\175\1g\176\1c\177\1_\178\1[\179\0\0\180\0\203\144\6\17\182\1W\183\0\183\184\1\171\185\1\167\186\0\191\187\1S\188\0\0\189\0\0\190\0\0\191\0\0\192\0\0\3\0\0n\5\134\129\5J\6\2\202b\6Q\8\2p\t\2Z\n\2j\135\5J\12\2\206u\7\205\14\2\210\144\7\224\8\8Vb\8\161\16\8\140}\5\130" .. ("\0"):rep(15) .. "\17\8V\0\0\0\0\0\0\0\0\0\0\0\0H\8\174\0\0\0\0\0\0\0\0\0\0\0\0#\2jZ\11#\0\0\0&\2\214]\11#\0\0\0\0\0\0\0\0\0+\2\170\0\0\0-\2\166\0\0\0/\2\162\0\0\0001\2\158\26\4Z3\2\154\0\0\0005\2\150\0\0\0007\2\146\0\0\0009\2\142\0\0\0;\2\138\0\0\0=\2\134\0\0\0?\2\130\0\0\0A\2~\0\0\0C\2zn\8\188E\2v\0\0\0}\11#H\2p\0\0\0J\2\174\181\5~\0\0\0\8\3\6\t\2\240\n\3\0H\8V\0\0\0\0\0\0}\8\184" .. ("\0"):rep(18) .. "Z\2\198\0\0\0\0\0\0]\2\194\0\0\0\0\0\0\0\0\0\0\0\0b\2`\0\0\0\154\11#\0\0\0\0\0\0\0\0\0#\3\0\0\0\0\0\0\0I\6>\0\0\0\0\0\0n\2\190\26\5\215+\3@I\7\253-\3\137\4^]\3X\139\4j\0\0\0\0\0\0~\7\253b\2\246\26\6t\0\0\0\0\0\0\3\3\170\0\0\0\149\4f\6\4\8\0\0\0\8\3\164\26\8\14\n\3\158n\3T\12\4\12\181\2\178\14\4\16\183\0\0s\3P\17\3\174\0\0\0\0\0\0\0\0\0\21\4$\0\0\0\0\0\0k\5\211\0\0\0}\3L\0\0\0\172\4T\0\0\0\129\2\250\0\0\0\0\0\0\0\0\0\168\6>#\3\158\135\2\250x\5\219&\4\20\0\0\0\168\7\253\0\0\0\0\0\0+\3\232\0\0\0-\3\228\0\0\0/\3\224\182\6>1\3\220\0\0\0003\3\216\0\0\0005\3\212\182\7\2537\3\208\190\6>9\3\204\0\0\0;\3\200\0\0\0=\3\196\190\7\253?\3\192\0\0\0A\3\188\149\5\219C\3\184\0\0\0E\3\180\0\0\0\0\0\0H\3\164\0\0\0J\3\236\158\5\211\0\0\0\0\0\0\0\0\0\0\0\0\163\5\211\0\0\0\181\3H\0\0\0\0\0\0\0\0\0\169\5\211q\6\138\0\0\0\0\0\0Z\4\4\0\0\0\0\0\0]\4\0x\6\128\0\0\0q" .. ("\0"):rep(20) .. "x\8\26\0\0\0\131\6|\0\0\0\0\0\0\0\0\0\0\0\0n\3\252\137\6x\0\0\0\139\6\132\131\8\22s\3\248\0\0\0u\4\28v\3\148\0\0\0\137\8\18\0\0\0\139\8\30\149\6\128\0\0\0}\3\244\0\0\0\127\4\24\0\0\0\129\3\152\0\0\0\0\0\0\149\8\26\0\0\0\3\4\203\135\3\152\0\0\0\6\5#\0\0\0\8\4\197\t\4\175\n\4\191\142\3\148\12\5'\0\0\0\14\5+\172\6n" .. ("\0"):rep(15) .. "\152\0\0\0\0\0\0\0\0\172\8\8" .. ("\0"):rep(30) .. "#\4\191\0\0\0\0\0\0&\5/\0\0\0\0\0\0\0\0\0\0\0\0+\5\3\0\0\0-\4\255\0\0\0/\4\251\0\0\0001\4\247\181\3\2403\4\243\183\4 5\4\239\0\0\0007\4\235\0\0\0009\4\231\0\0\0;\4\227\0\0\0=\4\223\0\0\0?\4\219\0\0\0A\4\215\0\0\0C\4\211\0\0\0E\4\207\0\0\0\0\0\0H\4\197\0\0\0J\5\7" .. ("\0"):rep(45) .. "Z\5\31\0\0\0\0\0\0]\5\27\0\0\0\0\0\0\0\0\0\0\0\0b\4\181\0\0\0\0\0\0\0\0\0\3\7\178\0\0\0\0\0\0\6\7\162\0\0\0\8\7H\t\0072\n\7Bn\5\23\12\7\166\0\0\0\14\7\170\0\0\0s\5\19" .. ("\0"):rep(27) .. "}\5\15\0\0\0\0\0\0\0\0\0\129\4\185\8\t\168\t\t\146\n\t\162\0\0\0#\7B\135\4\185\0\0\0&\7\174\0\0\0\0\0\0\0\0\0\0\0\0+\7\130\0\0\0-\7~\0\0\0/\7z\0\0\0001\7v\0\0\0003\7r\0\0\0005\7n\0\0\0007\7j\0\0\0009\7f#\t\162;\7b\0\0\0=\7^\0\0\0?\7Z\0\0\0A\7V\0\0\0C\7R\0\0\0E\7N\0\0\0\0\0\0H\7H\0\0\0J\7\134\8\8\218\t\8\196\n\8\212\0\0\0\0\0\0\0\0\0\0\0\0\181\5\11\0\0\0\183\0053" .. ("\0"):rep(15) .. "Z\7\158\0\0\0\0\0\0]\7\154\0\0\0H\t\168\0\0\0J\t\174b\0078\0\0\0\0\0\0\0\0\0#\8\212" .. ("\0"):rep(21) .. "n\7\150\0\0\0\0\0\0Z\t\198\0\0\0s\7\146]\t\194\0\0\0003\t\0\0\0\0005\8\252b\t\1527\8\248\0\0\0\0\0\0}\7\142;\8\244\0\0\0=\8\240\129\7