From e1dffaa33496a120be6333360bdb613882bfc200 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Tue, 28 Feb 2023 20:52:14 -0500 Subject: [PATCH 01/11] Add certificate workaround for Let's Encrypt --- .../core/apis/http/NetworkUtils.java | 148 +++++++++++++++++- 1 file changed, 144 insertions(+), 4 deletions(-) diff --git a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index 05d312b55..63150bbc8 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -24,12 +24,17 @@ import io.netty.handler.traffic.GlobalTrafficShapingHandler; import javax.annotation.Nonnull; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.*; +import java.io.ByteArrayInputStream; import java.net.InetSocketAddress; import java.net.URI; import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStoreException; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -68,6 +73,38 @@ private NetworkUtils() private static SslContext sslContext; private static boolean triedSslContext = false; + private static final String letsEncryptRootCert = "-----BEGIN CERTIFICATE-----\n" + + "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" + + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" + + "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" + + "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" + + "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" + + "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" + + "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" + + "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" + + "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" + + "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" + + "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" + + "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" + + "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" + + "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" + + "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" + + "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" + + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" + + "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" + + "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" + + "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" + + "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" + + "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" + + "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" + + "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" + + "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" + + "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" + + "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" + + "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" + + "-----END CERTIFICATE-----\n"; + private static TrustManagerFactory getTrustManager() { if( trustManager != null ) return trustManager; @@ -78,7 +115,28 @@ private static TrustManagerFactory getTrustManager() TrustManagerFactory tmf = null; try { - tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); + Certificate ca = CertificateFactory.getInstance( "X.509" ) + .generateCertificate( new ByteArrayInputStream( letsEncryptRootCert.getBytes() ) ); + + KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() ); + ks.load( null, null ); + ks.setCertificateEntry( Integer.toString( 1 ), ca ); + + TrustManagerFactory additional = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); + additional.init( ks ); + + // Get hold of the extension trust manager + X509TrustManager x509tm = null; + for ( TrustManager tm : additional.getTrustManagers() ) + { + if ( tm instanceof X509TrustManager ) + { + x509tm = (X509TrustManager) tm; + break; + } + } + + tmf = new MergedTrustManagerFactory( TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ), x509tm ); tmf.init( (KeyStore) null ); } catch( Exception e ) @@ -210,4 +268,86 @@ else if( cause instanceof SSLHandshakeException || (cause instanceof DecoderExce return "Could not connect"; } } + + private static class MergedX509TrustManager implements X509TrustManager + { + private final X509TrustManager base, additional; + + MergedX509TrustManager( X509TrustManager b, X509TrustManager a ) + { + this.base = b; + this.additional = a; + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return base.getAcceptedIssuers(); + } + + @Override + public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException + { + try + { + base.checkServerTrusted( chain, authType ); + } + catch ( CertificateException e ) + { + additional.checkServerTrusted( chain, authType ); + } + } + + @Override + public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException + { + base.checkClientTrusted( chain, authType ); + } + } + + private static class MergedTrustManagerFactory extends TrustManagerFactory + { + private static class Spi extends TrustManagerFactorySpi + { + private final TrustManagerFactory base; + private final X509TrustManager additional; + + Spi( TrustManagerFactory b, X509TrustManager a ) + { + this.base = b; + this.additional = a; + } + + @Override + protected void engineInit( KeyStore keyStore ) throws KeyStoreException + { + base.init( keyStore ); + } + + @Override + protected void engineInit( ManagerFactoryParameters managerFactoryParameters ) throws InvalidAlgorithmParameterException + { + base.init( managerFactoryParameters ); + } + + @Override + protected TrustManager[] engineGetTrustManagers() + { + TrustManager[] managers = base.getTrustManagers(); + for ( int i = 0; i < managers.length; i++ ) + { + if ( managers[i] instanceof X509TrustManager ) + { + managers[i] = new MergedX509TrustManager( (X509TrustManager) managers[i], additional ); + } + } + return managers; + } + } + + MergedTrustManagerFactory( TrustManagerFactory b, X509TrustManager a ) + { + super( new Spi( b, a ), b.getProvider(), b.getAlgorithm() ); + } + } } From e4dd4dbef0db17b8b3a3bef502eb16512455d5e0 Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Thu, 2 Mar 2023 18:30:54 -0500 Subject: [PATCH 02/11] Added version check to certificate addition --- .../core/apis/http/NetworkUtils.java | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index 63150bbc8..53a3fb2ea 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -26,6 +26,7 @@ import javax.annotation.Nonnull; import javax.net.ssl.*; import java.io.ByteArrayInputStream; +import java.lang.System; import java.net.InetSocketAddress; import java.net.URI; import java.security.KeyStore; @@ -115,28 +116,36 @@ private static TrustManagerFactory getTrustManager() TrustManagerFactory tmf = null; try { - Certificate ca = CertificateFactory.getInstance( "X.509" ) - .generateCertificate( new ByteArrayInputStream( letsEncryptRootCert.getBytes() ) ); - - KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() ); - ks.load( null, null ); - ks.setCertificateEntry( Integer.toString( 1 ), ca ); - - TrustManagerFactory additional = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); - additional.init( ks ); - - // Get hold of the extension trust manager - X509TrustManager x509tm = null; - for ( TrustManager tm : additional.getTrustManagers() ) + String version = System.getProperty( "java.version" ); + if ( version.regionMatches( 0, "1.8.0_", 0, 6 ) && version.length() == 8 ) // 1.8.0_xx (xx < 100) { - if ( tm instanceof X509TrustManager ) - { - x509tm = (X509TrustManager) tm; - break; - } - } + Certificate ca = CertificateFactory.getInstance( "X.509" ) + .generateCertificate( new ByteArrayInputStream( letsEncryptRootCert.getBytes() ) ); - tmf = new MergedTrustManagerFactory( TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ), x509tm ); + KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() ); + ks.load( null, null ); + ks.setCertificateEntry( Integer.toString( 1 ), ca ); + + TrustManagerFactory additional = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); + additional.init( ks ); + + // Get hold of the extension trust manager + X509TrustManager x509tm = null; + for ( TrustManager tm : additional.getTrustManagers() ) + { + if ( tm instanceof X509TrustManager ) + { + x509tm = (X509TrustManager) tm; + break; + } + } + + tmf = new MergedTrustManagerFactory( TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ), x509tm ); + } + else + { + tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); + } tmf.init( (KeyStore) null ); } catch( Exception e ) From 6f65bad9af84e9e410d714068c0d3beec2dcb505 Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Sat, 4 Mar 2023 02:02:05 -0500 Subject: [PATCH 03/11] Fixed CI --- .../java/dan200/computercraft/core/apis/http/NetworkUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index 53a3fb2ea..f8623374f 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -26,7 +26,6 @@ import javax.annotation.Nonnull; import javax.net.ssl.*; import java.io.ByteArrayInputStream; -import java.lang.System; import java.net.InetSocketAddress; import java.net.URI; import java.security.KeyStore; From 0fce3212a3eadb904fecda557b72c16ed822a75c Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 4 Mar 2023 10:29:52 +0000 Subject: [PATCH 04/11] Distinguish between all parsers passing and failing Given an input like f(x), which is both a valid statement and expression, both parsers would accept the whole input. However, this was treated the same as both parsers rejecting the input, resulting in a crash when trying to print the error. We now return immediately when any parser accepts the input. Fixes #1354 --- .../modules/main/cc/internal/syntax/init.lua | 23 +++++++++++++++---- .../modules/cc/internal/syntax/init_spec.lua | 14 +++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) 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..00df37a24 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 @@ -120,22 +120,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/test/resources/test-rom/spec/modules/cc/internal/syntax/init_spec.lua b/src/test/resources/test-rom/spec/modules/cc/internal/syntax/init_spec.lua index 69bddaa89..10ae30f78 100644 --- a/src/test/resources/test-rom/spec/modules/cc/internal/syntax/init_spec.lua +++ b/src/test/resources/test-rom/spec/modules/cc/internal/syntax/init_spec.lua @@ -49,4 +49,18 @@ describe("cc.internal.syntax", function() describe_golden("the lexer", "lexer_spec.md", true) describe_golden("the parser", "parser_spec.md", false) describe_golden("the parser (all states)", "parser_exhaustive_spec.md", false) + + describe("the REPL input parser", function() + it("returns true when accepted by both parsers", function() + helpers.with_window(50, 10, function() + expect(syntax.parse_repl("print(x)")):eq(true) + end) + end) + + it("returns true when accepted by one parser", function() + helpers.with_window(50, 10, function() + expect(syntax.parse_repl("x")):eq(true) + end) + end) + end) end) From 2a9f35de5e1c16cda824fdae2f2739388cab67fe Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 3 May 2023 23:24:49 +0100 Subject: [PATCH 05/11] Backport several ROM/Lua commits - Fix GPS returning nan on duplicate positions. - Distinguish between all parsers passing and failing. - Improve several comma related parse errors. - Ignore metatables in textutils.serialize. - Detect common audio containers in "speaker". Co-authored-by: Wojbie --- .../data/computercraft/lua/rom/apis/gps.lua | 16 ++++-- .../computercraft/lua/rom/apis/settings.lua | 9 ++-- .../computercraft/lua/rom/apis/textutils.lua | 11 +++- .../main/cc/internal/syntax/errors.lua | 42 ++++++++++++++++ .../main/cc/internal/syntax/parser.lua | 50 +++++++++++++------ .../lua/rom/programs/fun/speaker.lua | 10 ++++ 6 files changed, 113 insertions(+), 25 deletions(-) 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/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/parser.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua index a78466880..125942952 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 Date: Wed, 3 May 2023 23:34:22 +0100 Subject: [PATCH 06/11] Use correct model for the turtle modem Introduced in 0c3de1087eaab54203054d8255f7784b28e642b4, so should only affect 1.16.5 and 1.18.2. Fixes #1426 --- .../computercraft/shared/turtle/upgrades/TurtleModem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 272fb1611..df543390a 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java @@ -87,7 +87,7 @@ public TurtleModem( boolean advanced, ResourceLocation id ) } 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" ); From db2616d1c039b3a5fee1150bc2bfcdbc059e09be Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 3 May 2023 23:38:12 +0100 Subject: [PATCH 07/11] Don't (metaphorically) explode on null explosions Closes #1423. --- .../computercraft/shared/turtle/blocks/BlockTurtle.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 1c5e82ca1..76f6138d5 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java @@ -172,9 +172,9 @@ public void setPlacedBy( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull B } @Override - public float getExplosionResistance( BlockState state, IBlockReader world, BlockPos pos, Explosion explosion ) + public float getExplosionResistance( BlockState state, IBlockReader world, BlockPos pos, @Nullable Explosion explosion ) { - Entity exploder = explosion.getExploder(); + Entity exploder = explosion == null ? null : explosion.getExploder(); if( getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof DamagingProjectileEntity ) { return 2000; From f7fdb6e7295e60437878f91068a3f5d71b0247ca Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 6 Jul 2023 23:15:55 +0100 Subject: [PATCH 08/11] Backport a couple of ROM commits - Improve REPL's handling of expressions (655d5aeca80d1b0a91fb346296447c861f01b7b1) - Some tiny optimisations to the window API (4accda6b8ef75d0c2e9ff0d1cc2c23bb3a21465e) - Be lazy in reporting errors in the lexer (54ab98473f92f85dda8249f4b74ea6a1d3a839a2) - Update lua.lua require logic. (88f0c441525d11b194fd432df910e163b0887f9d) --- .../computercraft/lua/rom/apis/window.lua | 74 ++++++++----------- .../modules/main/cc/internal/syntax/init.lua | 12 ++- .../modules/main/cc/internal/syntax/lexer.lua | 24 +++--- .../main/cc/internal/syntax/parser.lua | 2 +- .../computercraft/lua/rom/programs/edit.lua | 4 +- .../lua/rom/programs/fun/speaker.lua | 3 +- .../computercraft/lua/rom/programs/lua.lua | 41 ++++------ .../cc/internal/syntax/syntax_helpers.lua | 5 +- 8 files changed, 71 insertions(+), 94 deletions(-) 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/modules/main/cc/internal/syntax/init.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/init.lua index 00df37a24..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 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 125942952..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 @@ -240,7 +240,7 @@ local function handle_error(context, stack, stack_n, token, token_start, token_e end end - context.report(errors.unexpected_token(token, token_start, token_end)) + context.report(errors.unexpected_token, token, token_start, token_end) return false end diff --git a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua index fe7788074..d055396be 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/edit.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/edit.lua @@ -107,7 +107,9 @@ local function set_status(text, ok) status_text = text end -if not bReadOnly and fs.getFreeSpace(sPath) < 1024 then +if bReadOnly then + set_status("File is read only", false) +elseif fs.getFreeSpace(sPath) < 1024 then set_status("Disk is low on space", false) else local message diff --git a/src/main/resources/data/computercraft/lua/rom/programs/fun/speaker.lua b/src/main/resources/data/computercraft/lua/rom/programs/fun/speaker.lua index 17f2d06ae..1a06bdf59 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/fun/speaker.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/fun/speaker.lua @@ -27,7 +27,7 @@ local function pcm_decoder(chunk) end local function report_invalid_format(format) - printError(("The speaker cannot play %s files."):format(format)) + printError(("speaker cannot play %s files."):format(format)) local pp = require "cc.pretty" pp.print("Run '" .. pp.text("help speaker", colours.lightGrey) .. "' for information on supported formats.") end @@ -103,6 +103,7 @@ elseif cmd == "play" then elseif start == "OggS" then return report_invalid_format("Ogg") elseif start == "fLaC" then return report_invalid_format("FLAC") elseif start:sub(1, 3) == "ID3" then return report_invalid_format("MP3") + elseif start == " Date: Thu, 6 Jul 2023 23:27:17 +0100 Subject: [PATCH 09/11] Tighten up the $private HTTP rule - Block multicast and the fd00::/8 address ranges. - Block several cloud metadata providers which sit outside the standard address ranges. --- .../apis/http/options/AddressPredicate.java | 35 ++++++++++++++++--- .../apis/http/options/AddressRuleTest.java | 9 ++++- 2 files changed, 39 insertions(+), 5 deletions(-) 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/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java b/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java index a57cc9a3d..f5111bdea 100644 --- a/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java +++ b/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java @@ -34,7 +34,14 @@ public void matchesPort() @ValueSource( strings = { "0.0.0.0", "[::]", "localhost", "127.0.0.1.nip.io", "127.0.0.1", "[::1]", - "172.17.0.1", "192.168.1.114", "[0:0:0:0:0:ffff:c0a8:172]", "10.0.0.1" + "172.17.0.1", "192.168.1.114", "[0:0:0:0:0:ffff:c0a8:172]", "10.0.0.1", + // Multicast + "224.0.0.1", "ff02::1", + // Cloud metadata providers + "100.100.100.200", // Alibaba + "192.0.0.192", // Oracle + "fd00:ec2::254", // AWS + "169.254.169.254" // AWS, Digital Ocean, GCP, etc.. } ) public void blocksLocalDomains( String domain ) { From 7436447a6e3d649447b59dc489faf78f431c2b8a Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 6 Jul 2023 23:41:02 +0100 Subject: [PATCH 10/11] Several command permission fixes - Attach permission checks to the first argument (so the literal command name) rather than the last argument. This fixes commands showing up when they shouldn't. - HelpingArgumentBuilder now inherits permissions of its leaf nodes. This only really impacts the "track" subcommand. - Don't autocomplete the computer selector for the "queue" subcommand. As everyone has permission for this command, it's possible to find all computer ids and labels in the world. I'm in mixed minds about this, but don't think this is an exploit - computer ids/labels are sent to in-range players so shouldn't be considered secret - but worth patching none-the-less. --- .../shared/command/CommandComputerCraft.java | 8 ++++- .../shared/command/UserLevel.java | 26 +++++++++++++- .../command/builder/CommandBuilder.java | 27 +++++++------- .../builder/HelpingArgumentBuilder.java | 36 +++++++++++++++++-- 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index fb68d06b3..d1e1497fe 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; @@ -201,7 +204,10 @@ else if( b.getWorld() == 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 0a5e22e83..fb68440c1 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( CommandSource 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( CommandSource source ) { MinecraftServer server = source.getServer(); Entity sender = source.getEntity(); return server.isDedicatedServer() ? source.getEntity() == null && source.hasPermission( 4 ) && source.getTextName().equals( "Server" ) - : sender instanceof PlayerEntity && ((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ); + : sender instanceof PlayerEntity && server.isSingleplayerOwner( ((PlayerEntity) sender).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 71404ee0e..3a1cacffd 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 696bdcea3..4e4dff4af 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.command.CommandSource; import net.minecraft.util.text.IFormattableTextComponent; import net.minecraft.util.text.ITextComponent; @@ -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) ) return x -> requirements.stream().anyMatch( y -> y.test( x ) ); + userLevel = UserLevel.union( userLevel, (UserLevel) requirement ); + } + + return userLevel; + } + @Override public LiteralArgumentBuilder executes( final Command command ) { @@ -99,9 +131,7 @@ private LiteralCommandNode buildImpl( String id, String command ) 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() ) From aa89e5163955e398f9e77b79479358d5c0c29875 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 6 Jul 2023 23:54:44 +0100 Subject: [PATCH 11/11] Bump CC:T to 1.101.3 0/10, would not recommend. --- gradle.properties | 2 +- .../computercraft/lua/rom/help/changelog.md | 19 ++++++++++++++ .../computercraft/lua/rom/help/whatsnew.md | 26 +++++++++++-------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/gradle.properties b/gradle.properties index d4856afab..db64e87ab 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.16.5 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 1da596bdf..d6f777f71 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,22 @@ +# New features in CC: Tweaked 1.101.3 + +* Support LetsEncrypt certificates within the HTTP API (MCJack123). +* 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..9cfa534d4 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,20 @@ -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. +* Support LetsEncrypt certificates within the HTTP API (MCJack123). +* 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.