mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-04 23:40:00 +00:00
Merge branch 'mc-1.16.x' into mc-1.18.x
This commit is contained in:
commit
edf372a695
@ -5,7 +5,7 @@ kotlin.stdlib.default.dependency=false
|
|||||||
kotlin.jvm.target.validation.mode=error
|
kotlin.jvm.target.validation.mode=error
|
||||||
|
|
||||||
# Mod properties
|
# 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
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.18.2
|
mcVersion=1.18.2
|
||||||
|
@ -8,9 +8,13 @@ package dan200.computercraft.core.apis.http.options;
|
|||||||
import com.google.common.net.InetAddresses;
|
import com.google.common.net.InetAddresses;
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
|
||||||
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A predicate on an address. Matches against a domain and an ip address.
|
* A predicate on an address. Matches against a domain and an ip address.
|
||||||
@ -135,13 +139,36 @@ interface AddressPredicate
|
|||||||
{
|
{
|
||||||
static final PrivatePattern INSTANCE = new PrivatePattern();
|
static final PrivatePattern INSTANCE = new PrivatePattern();
|
||||||
|
|
||||||
|
private static final Set<InetAddress> 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
|
@Override
|
||||||
public boolean matches( InetAddress socketAddress )
|
public boolean matches( InetAddress socketAddress )
|
||||||
{
|
{
|
||||||
return socketAddress.isAnyLocalAddress()
|
return socketAddress.isAnyLocalAddress() // 0.0.0.0, ::0
|
||||||
|| socketAddress.isLoopbackAddress()
|
|| socketAddress.isLoopbackAddress() // 127.0.0.0/8, ::1
|
||||||
|| socketAddress.isLinkLocalAddress()
|
|| socketAddress.isLinkLocalAddress() // 169.254.0.0/16, fe80::/10
|
||||||
|| socketAddress.isSiteLocalAddress();
|
|| 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 <a href="https://en.wikipedia.org/wiki/Unique_local_address">Unique local address on Wikipedia</a>
|
||||||
|
*/
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,13 @@ package dan200.computercraft.shared.command;
|
|||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import dan200.computercraft.core.metrics.Metrics;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
|
||||||
import dan200.computercraft.shared.command.text.TableBuilder;
|
import dan200.computercraft.shared.command.text.TableBuilder;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
@ -200,7 +203,10 @@ public final class CommandComputerCraft
|
|||||||
|
|
||||||
.then( command( "queue" )
|
.then( command( "queue" )
|
||||||
.requires( UserLevel.ANYONE )
|
.requires( UserLevel.ANYONE )
|
||||||
.arg( "computer", manyComputers() )
|
.arg(
|
||||||
|
RequiredArgumentBuilder.<CommandSourceStack, ComputersArgumentType.ComputersSupplier>argument( "computer", manyComputers() )
|
||||||
|
.suggests( ( context, builder ) -> Suggestions.empty() )
|
||||||
|
)
|
||||||
.argManyValue( "args", StringArgumentType.string(), Collections.emptyList() )
|
.argManyValue( "args", StringArgumentType.string(), Collections.emptyList() )
|
||||||
.executes( ( ctx, args ) -> {
|
.executes( ( ctx, args ) -> {
|
||||||
Collection<ServerComputer> computers = getComputersArgument( ctx, "computer" );
|
Collection<ServerComputer> computers = getComputersArgument( ctx, "computer" );
|
||||||
|
@ -61,12 +61,36 @@ public enum UserLevel implements Predicate<CommandSourceStack>
|
|||||||
return source.hasPermission( toLevel() );
|
return source.hasPermission( toLevel() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take the union of two {@link UserLevel}s.
|
||||||
|
* <p>
|
||||||
|
* 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 )
|
private static boolean isOwner( CommandSourceStack source )
|
||||||
{
|
{
|
||||||
MinecraftServer server = source.getServer();
|
MinecraftServer server = source.getServer();
|
||||||
Entity sender = source.getEntity();
|
Entity sender = source.getEntity();
|
||||||
return server.isDedicatedServer()
|
return server.isDedicatedServer()
|
||||||
? source.getEntity() == null && source.hasPermission( 4 ) && source.getTextName().equals( "Server" )
|
? 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() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,10 +52,15 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>>
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandBuilder<S> arg( ArgumentBuilder<S, ?> arg )
|
||||||
|
{
|
||||||
|
args.add( arg );
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandBuilder<S> arg( String name, ArgumentType<?> type )
|
public CommandBuilder<S> arg( String name, ArgumentType<?> type )
|
||||||
{
|
{
|
||||||
args.add( RequiredArgumentBuilder.argument( name, type ) );
|
return arg( RequiredArgumentBuilder.argument( name, type ) );
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> CommandNodeBuilder<S, ArgCommand<S, List<T>>> argManyValue( String name, ArgumentType<T> type, List<T> empty )
|
public <T> CommandNodeBuilder<S, ArgCommand<S, List<T>>> argManyValue( String name, ArgumentType<T> type, List<T> empty )
|
||||||
@ -84,7 +89,7 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>>
|
|||||||
|
|
||||||
return command -> {
|
return command -> {
|
||||||
// The node for no arguments
|
// The node for no arguments
|
||||||
ArgumentBuilder<S, ?> tail = tail( ctx -> command.run( ctx, empty.get() ) );
|
ArgumentBuilder<S, ?> tail = setupTail( ctx -> command.run( ctx, empty.get() ) );
|
||||||
|
|
||||||
// The node for one or more arguments
|
// The node for one or more arguments
|
||||||
ArgumentBuilder<S, ?> moreArg = RequiredArgumentBuilder
|
ArgumentBuilder<S, ?> moreArg = RequiredArgumentBuilder
|
||||||
@ -93,7 +98,7 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>>
|
|||||||
|
|
||||||
// Chain all of them together!
|
// Chain all of them together!
|
||||||
tail.then( moreArg );
|
tail.then( moreArg );
|
||||||
return link( tail );
|
return buildTail( tail );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,22 +111,18 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>>
|
|||||||
@Override
|
@Override
|
||||||
public CommandNode<S> executes( Command<S> command )
|
public CommandNode<S> executes( Command<S> command )
|
||||||
{
|
{
|
||||||
if( args.isEmpty() ) throw new IllegalStateException( "Cannot have empty arg chain builder" );
|
return buildTail( setupTail( command ) );
|
||||||
|
|
||||||
return link( tail( command ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArgumentBuilder<S, ?> tail( Command<S> command )
|
private ArgumentBuilder<S, ?> setupTail( Command<S> command )
|
||||||
{
|
{
|
||||||
ArgumentBuilder<S, ?> defaultTail = args.get( args.size() - 1 );
|
return args.get( args.size() - 1 ).executes( command );
|
||||||
defaultTail.executes( command );
|
|
||||||
if( requires != null ) defaultTail.requires( requires );
|
|
||||||
return defaultTail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CommandNode<S> link( ArgumentBuilder<S, ?> tail )
|
private CommandNode<S> buildTail( ArgumentBuilder<S, ?> tail )
|
||||||
{
|
{
|
||||||
for( int i = args.size() - 2; i >= 0; i-- ) tail = args.get( i ).then( 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();
|
return tail.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import dan200.computercraft.shared.command.UserLevel;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.network.chat.ClickEvent;
|
import net.minecraft.network.chat.ClickEvent;
|
||||||
@ -22,6 +23,10 @@ import net.minecraft.network.chat.TextComponent;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
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.coloured;
|
||||||
import static dan200.computercraft.shared.command.text.ChatHelpers.translate;
|
import static dan200.computercraft.shared.command.text.ChatHelpers.translate;
|
||||||
@ -44,6 +49,33 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
|||||||
return new HelpingArgumentBuilder( literal );
|
return new HelpingArgumentBuilder( literal );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LiteralArgumentBuilder<CommandSourceStack> requires( Predicate<CommandSourceStack> requirement )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( "Cannot use requires on a HelpingArgumentBuilder" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Predicate<CommandSourceStack> getRequirement()
|
||||||
|
{
|
||||||
|
// The requirement of this node is the union of all child's requirements.
|
||||||
|
List<Predicate<CommandSourceStack>> 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<CommandSourceStack> requirement : requirements )
|
||||||
|
{
|
||||||
|
if( !(requirement instanceof UserLevel level) ) return x -> requirements.stream().anyMatch( y -> y.test( x ) );
|
||||||
|
userLevel = UserLevel.union( userLevel, level );
|
||||||
|
}
|
||||||
|
|
||||||
|
return userLevel;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiteralArgumentBuilder<CommandSourceStack> executes( final Command<CommandSourceStack> command )
|
public LiteralArgumentBuilder<CommandSourceStack> executes( final Command<CommandSourceStack> command )
|
||||||
{
|
{
|
||||||
@ -99,9 +131,7 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
|||||||
helpCommand.node = node;
|
helpCommand.node = node;
|
||||||
|
|
||||||
// Set up a /... help command
|
// Set up a /... help command
|
||||||
LiteralArgumentBuilder<CommandSourceStack> helpNode = LiteralArgumentBuilder.<CommandSourceStack>literal( "help" )
|
LiteralArgumentBuilder<CommandSourceStack> helpNode = LiteralArgumentBuilder.<CommandSourceStack>literal( "help" ).executes( helpCommand );
|
||||||
.requires( x -> getArguments().stream().anyMatch( y -> y.getRequirement().test( x ) ) )
|
|
||||||
.executes( helpCommand );
|
|
||||||
|
|
||||||
// Add all normal command children to this and the help node
|
// Add all normal command children to this and the help node
|
||||||
for( CommandNode<CommandSourceStack> child : getArguments() )
|
for( CommandNode<CommandSourceStack> child : getArguments() )
|
||||||
|
@ -167,7 +167,7 @@ public class BlockTurtle extends BlockComputerBase<TileTurtle> implements Simple
|
|||||||
@Override
|
@Override
|
||||||
public float getExplosionResistance( BlockState state, BlockGetter world, BlockPos pos, Explosion explosion )
|
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 )
|
if( getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile )
|
||||||
{
|
{
|
||||||
return 2000;
|
return 2000;
|
||||||
|
@ -82,7 +82,7 @@ public class TurtleModem extends AbstractTurtleUpgrade
|
|||||||
}
|
}
|
||||||
else
|
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" );
|
rightOffModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_off_right" );
|
||||||
leftOnModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_on_left" );
|
leftOnModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_on_left" );
|
||||||
rightOnModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_on_right" );
|
rightOnModel = new ResourceLocation( ComputerCraft.MOD_ID, "block/turtle_modem_normal_on_right" );
|
||||||
|
@ -153,12 +153,22 @@ function locate(_nTimeout, _bDebug)
|
|||||||
if tFix.nDistance == 0 then
|
if tFix.nDistance == 0 then
|
||||||
pos1, pos2 = tFix.vPosition, nil
|
pos1, pos2 = tFix.vPosition, nil
|
||||||
else
|
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 #tFixes >= 3 then
|
||||||
if not pos1 then
|
if not pos1 then
|
||||||
pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[#tFixes])
|
pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[3])
|
||||||
else
|
else
|
||||||
pos1, pos2 = narrow(pos1, pos2, tFixes[#tFixes])
|
pos1, pos2 = narrow(pos1, pos2, tFixes[3])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -74,8 +74,7 @@ function undefine(name)
|
|||||||
details[name] = nil
|
details[name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set_value(name, value)
|
local function set_value(name, new)
|
||||||
local new = reserialize(value)
|
|
||||||
local old = values[name]
|
local old = values[name]
|
||||||
if old == nil then
|
if old == nil then
|
||||||
local opt = details[name]
|
local opt = details[name]
|
||||||
@ -103,7 +102,7 @@ function set(name, value)
|
|||||||
local opt = details[name]
|
local opt = details[name]
|
||||||
if opt and opt.type then expect(2, value, opt.type) end
|
if opt and opt.type then expect(2, value, opt.type) end
|
||||||
|
|
||||||
set_value(name, value)
|
set_value(name, reserialize(value))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the value of a setting.
|
--- 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
|
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]
|
local opt = details[k]
|
||||||
if not opt or not opt.type or ty_v == opt.type then
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -292,6 +292,13 @@ local g_tLuaKeywords = {
|
|||||||
["while"] = true,
|
["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 serialize_infinity = math.huge
|
||||||
local function serialize_impl(t, tracking, indent, opts)
|
local function serialize_impl(t, tracking, indent, opts)
|
||||||
local sType = type(t)
|
local sType = type(t)
|
||||||
@ -318,11 +325,11 @@ local function serialize_impl(t, tracking, indent, opts)
|
|||||||
|
|
||||||
result = open
|
result = open
|
||||||
local seen_keys = {}
|
local seen_keys = {}
|
||||||
for k, v in ipairs(t) do
|
for k, v in inext, t do
|
||||||
seen_keys[k] = true
|
seen_keys[k] = true
|
||||||
result = result .. sub_indent .. serialize_impl(v, tracking, sub_indent, opts) .. comma
|
result = result .. sub_indent .. serialize_impl(v, tracking, sub_indent, opts) .. comma
|
||||||
end
|
end
|
||||||
for k, v in pairs(t) do
|
for k, v in next, t do
|
||||||
if not seen_keys[k] then
|
if not seen_keys[k] then
|
||||||
local sEntry
|
local sEntry
|
||||||
if type(k) == "string" and not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then
|
if type(k) == "string" and not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then
|
||||||
|
@ -127,11 +127,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||||
for y = 1, nHeight do
|
for y = 1, nHeight do
|
||||||
tLines[y] = {
|
tLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
|
||||||
text = sEmptyText,
|
|
||||||
textColor = sEmptyTextColor,
|
|
||||||
backgroundColor = sEmptyBackgroundColor,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 0, 15 do
|
for i = 0, 15 do
|
||||||
@ -161,7 +157,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
local function redrawLine(n)
|
local function redrawLine(n)
|
||||||
local tLine = tLines[n]
|
local tLine = tLines[n]
|
||||||
parent.setCursorPos(nX, nY + n - 1)
|
parent.setCursorPos(nX, nY + n - 1)
|
||||||
parent.blit(tLine.text, tLine.textColor, tLine.backgroundColor)
|
parent.blit(tLine[1], tLine[2], tLine[3])
|
||||||
end
|
end
|
||||||
|
|
||||||
local function redraw()
|
local function redraw()
|
||||||
@ -184,9 +180,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
-- Modify line
|
-- Modify line
|
||||||
local tLine = tLines[nCursorY]
|
local tLine = tLines[nCursorY]
|
||||||
if nStart == 1 and nEnd == nWidth then
|
if nStart == 1 and nEnd == nWidth then
|
||||||
tLine.text = sText
|
tLine[1] = sText
|
||||||
tLine.textColor = sTextColor
|
tLine[2] = sTextColor
|
||||||
tLine.backgroundColor = sBackgroundColor
|
tLine[3] = sBackgroundColor
|
||||||
else
|
else
|
||||||
local sClippedText, sClippedTextColor, sClippedBackgroundColor
|
local sClippedText, sClippedTextColor, sClippedBackgroundColor
|
||||||
if nStart < 1 then
|
if nStart < 1 then
|
||||||
@ -206,9 +202,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
sClippedBackgroundColor = sBackgroundColor
|
sClippedBackgroundColor = sBackgroundColor
|
||||||
end
|
end
|
||||||
|
|
||||||
local sOldText = tLine.text
|
local sOldText = tLine[1]
|
||||||
local sOldTextColor = tLine.textColor
|
local sOldTextColor = tLine[2]
|
||||||
local sOldBackgroundColor = tLine.backgroundColor
|
local sOldBackgroundColor = tLine[3]
|
||||||
local sNewText, sNewTextColor, sNewBackgroundColor
|
local sNewText, sNewTextColor, sNewBackgroundColor
|
||||||
if nStart > 1 then
|
if nStart > 1 then
|
||||||
local nOldEnd = nStart - 1
|
local nOldEnd = nStart - 1
|
||||||
@ -227,9 +223,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
sNewBackgroundColor = sNewBackgroundColor .. string_sub(sOldBackgroundColor, nOldStart, nWidth)
|
sNewBackgroundColor = sNewBackgroundColor .. string_sub(sOldBackgroundColor, nOldStart, nWidth)
|
||||||
end
|
end
|
||||||
|
|
||||||
tLine.text = sNewText
|
tLine[1] = sNewText
|
||||||
tLine.textColor = sNewTextColor
|
tLine[2] = sNewTextColor
|
||||||
tLine.backgroundColor = sNewBackgroundColor
|
tLine[3] = sNewBackgroundColor
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Redraw line
|
-- Redraw line
|
||||||
@ -276,11 +272,10 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||||
for y = 1, nHeight do
|
for y = 1, nHeight do
|
||||||
tLines[y] = {
|
local line = tLines[y]
|
||||||
text = sEmptyText,
|
line[1] = sEmptyText
|
||||||
textColor = sEmptyTextColor,
|
line[2] = sEmptyTextColor
|
||||||
backgroundColor = sEmptyBackgroundColor,
|
line[3] = sEmptyBackgroundColor
|
||||||
}
|
|
||||||
end
|
end
|
||||||
if bVisible then
|
if bVisible then
|
||||||
redraw()
|
redraw()
|
||||||
@ -291,14 +286,10 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
|
|
||||||
function window.clearLine()
|
function window.clearLine()
|
||||||
if nCursorY >= 1 and nCursorY <= nHeight then
|
if nCursorY >= 1 and nCursorY <= nHeight then
|
||||||
local sEmptyText = sEmptySpaceLine
|
local line = tLines[nCursorY]
|
||||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
line[1] = sEmptySpaceLine
|
||||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
line[2] = tEmptyColorLines[nTextColor]
|
||||||
tLines[nCursorY] = {
|
line[3] = tEmptyColorLines[nBackgroundColor]
|
||||||
text = sEmptyText,
|
|
||||||
textColor = sEmptyTextColor,
|
|
||||||
backgroundColor = sEmptyBackgroundColor,
|
|
||||||
}
|
|
||||||
if bVisible then
|
if bVisible then
|
||||||
redrawLine(nCursorY)
|
redrawLine(nCursorY)
|
||||||
updateCursorColor()
|
updateCursorColor()
|
||||||
@ -427,11 +418,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
if y >= 1 and y <= nHeight then
|
if y >= 1 and y <= nHeight then
|
||||||
tNewLines[newY] = tLines[y]
|
tNewLines[newY] = tLines[y]
|
||||||
else
|
else
|
||||||
tNewLines[newY] = {
|
tNewLines[newY] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
|
||||||
text = sEmptyText,
|
|
||||||
textColor = sEmptyTextColor,
|
|
||||||
backgroundColor = sEmptyBackgroundColor,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
tLines = tNewLines
|
tLines = tNewLines
|
||||||
@ -474,7 +461,8 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
error("Line is out of range.", 2)
|
error("Line is out of range.", 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
return tLines[y].text, tLines[y].textColor, tLines[y].backgroundColor
|
local line = tLines[y]
|
||||||
|
return line[1], line[2], line[3]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Other functions
|
-- Other functions
|
||||||
@ -570,26 +558,22 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||||
for y = 1, new_height do
|
for y = 1, new_height do
|
||||||
if y > nHeight then
|
if y > nHeight then
|
||||||
tNewLines[y] = {
|
tNewLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
|
||||||
text = sEmptyText,
|
|
||||||
textColor = sEmptyTextColor,
|
|
||||||
backgroundColor = sEmptyBackgroundColor,
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
local tOldLine = tLines[y]
|
local tOldLine = tLines[y]
|
||||||
if new_width == nWidth then
|
if new_width == nWidth then
|
||||||
tNewLines[y] = tOldLine
|
tNewLines[y] = tOldLine
|
||||||
elseif new_width < nWidth then
|
elseif new_width < nWidth then
|
||||||
tNewLines[y] = {
|
tNewLines[y] = {
|
||||||
text = string_sub(tOldLine.text, 1, new_width),
|
string_sub(tOldLine[1], 1, new_width),
|
||||||
textColor = string_sub(tOldLine.textColor, 1, new_width),
|
string_sub(tOldLine[2], 1, new_width),
|
||||||
backgroundColor = string_sub(tOldLine.backgroundColor, 1, new_width),
|
string_sub(tOldLine[3], 1, new_width),
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
tNewLines[y] = {
|
tNewLines[y] = {
|
||||||
text = tOldLine.text .. string_sub(sEmptyText, nWidth + 1, new_width),
|
tOldLine[1] .. string_sub(sEmptyText, nWidth + 1, new_width),
|
||||||
textColor = tOldLine.textColor .. string_sub(sEmptyTextColor, nWidth + 1, new_width),
|
tOldLine[2] .. string_sub(sEmptyTextColor, nWidth + 1, new_width),
|
||||||
backgroundColor = tOldLine.backgroundColor .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width),
|
tOldLine[3] .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -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
|
# New features in CC: Tweaked 1.101.2
|
||||||
|
|
||||||
* Error messages in `edit` are now displayed in red on advanced computers.
|
* Error messages in `edit` are now displayed in red on advanced computers.
|
||||||
|
@ -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.
|
* Improve syntax errors when missing commas in tables, and on trailing commas in parameter lists.
|
||||||
* Improvements to the display of errors in the shell and REPL.
|
* `speaker` program now reports an error on common unsupported audio formats.
|
||||||
|
* Small optimisations to the `window` API.
|
||||||
|
|
||||||
Several bug fixes:
|
Several bug fixes:
|
||||||
* Fix `import.lua` failing to upload a file.
|
* Fix the REPL syntax reporting crashing on valid parses.
|
||||||
* Fix several issues with sparse Lua tables (Shiranuit).
|
* Ignore metatables in `textutils.serialize`.
|
||||||
* Computer upgrades now accept normal computers, rather than uselessly allowing you to upgrade an advanced computer to an advanced computer!
|
* Fix `gps.locate` returning `nan` when receiving a duplicate location (Wojbie).
|
||||||
* Correctly clamp speaker volume.
|
* Ignore metatables in `textutils.serialize`.
|
||||||
* Fix rednet queueing the wrong message when sending a message to the current computer.
|
* Fix wireless turtles having an invalid model.
|
||||||
* Fix the Lua VM crashing when a `__len` metamethod yields.
|
* Fix crash when turtles are exploded by a null explosion.
|
||||||
* Trim spaces from filesystem paths.
|
* Lua REPL no longer accepts `)(` as a valid expression.
|
||||||
* Correctly format 12AM/PM with `%I`.
|
* 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.
|
Type "help changelog" to see the full version history.
|
||||||
|
@ -364,6 +364,48 @@ function errors.table_key_equals(start_pos, end_pos)
|
|||||||
}
|
}
|
||||||
end
|
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
|
-- Statement parsing errors
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@ -17,6 +17,8 @@ local error_printer = require "cc.internal.error_printer"
|
|||||||
local error_sentinel = {}
|
local error_sentinel = {}
|
||||||
|
|
||||||
local function make_context(input)
|
local function make_context(input)
|
||||||
|
expect(1, input, "string")
|
||||||
|
|
||||||
local context = {}
|
local context = {}
|
||||||
|
|
||||||
local lines = { 1 }
|
local lines = { 1 }
|
||||||
@ -69,8 +71,9 @@ local function parse(input, start_symbol)
|
|||||||
expect(2, start_symbol, "number")
|
expect(2, start_symbol, "number")
|
||||||
|
|
||||||
local context = make_context(input)
|
local context = make_context(input)
|
||||||
function context.report(msg)
|
function context.report(msg, ...)
|
||||||
expect(1, msg, "table")
|
expect(1, msg, "table", "function")
|
||||||
|
if type(msg) == "function" then msg = msg(...) end
|
||||||
error_printer(context, msg)
|
error_printer(context, msg)
|
||||||
error(error_sentinel)
|
error(error_sentinel)
|
||||||
end
|
end
|
||||||
@ -106,8 +109,9 @@ local function parse_repl(input)
|
|||||||
local context = make_context(input)
|
local context = make_context(input)
|
||||||
|
|
||||||
local last_error = nil
|
local last_error = nil
|
||||||
function context.report(msg)
|
function context.report(msg, ...)
|
||||||
expect(1, msg, "table")
|
expect(1, msg, "table", "function")
|
||||||
|
if type(msg) == "function" then msg = msg(...) end
|
||||||
last_error = msg
|
last_error = msg
|
||||||
error(error_sentinel)
|
error(error_sentinel)
|
||||||
end
|
end
|
||||||
@ -120,22 +124,35 @@ local function parse_repl(input)
|
|||||||
assert(coroutine.resume(parsers[i], context, coroutine.yield, start_code))
|
assert(coroutine.resume(parsers[i], context, coroutine.yield, start_code))
|
||||||
end
|
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 ok, err = pcall(function()
|
||||||
local parsers_n = #parsers
|
local parsers_n = #parsers
|
||||||
while true do
|
while true do
|
||||||
local token, start, finish = lexer()
|
local token, start, finish = lexer()
|
||||||
|
|
||||||
local stop = true
|
local all_failed = true
|
||||||
for i = 1, parsers_n do
|
for i = 1, parsers_n do
|
||||||
local parser = parsers[i]
|
local parser = parsers[i]
|
||||||
if coroutine.status(parser) ~= "dead" then
|
if parser then
|
||||||
stop = false
|
|
||||||
local ok, err = coroutine.resume(parser, token, start, finish)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
if stop then error(error_sentinel) end
|
if all_failed then error(error_sentinel) end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ local function lex_number(context, str, start)
|
|||||||
local contents = sub(str, start, pos - 1)
|
local contents = sub(str, start, pos - 1)
|
||||||
if not tonumber(contents) then
|
if not tonumber(contents) then
|
||||||
-- TODO: Separate error for "2..3"?
|
-- TODO: Separate error for "2..3"?
|
||||||
context.report(errors.malformed_number(start, pos - 1))
|
context.report(errors.malformed_number, start, pos - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
return tokens.NUMBER, pos - 1
|
return tokens.NUMBER, pos - 1
|
||||||
@ -114,14 +114,14 @@ local function lex_string(context, str, start_pos, quote)
|
|||||||
return tokens.STRING, pos
|
return tokens.STRING, pos
|
||||||
elseif c == "\n" or c == "\r" or c == "" then
|
elseif c == "\n" or c == "\r" or c == "" then
|
||||||
-- We don't call newline here, as that's done for the next token.
|
-- 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
|
return tokens.STRING, pos - 1
|
||||||
elseif c == "\\" then
|
elseif c == "\\" then
|
||||||
c = sub(str, pos + 1, pos + 1)
|
c = sub(str, pos + 1, pos + 1)
|
||||||
if c == "\n" or c == "\r" then
|
if c == "\n" or c == "\r" then
|
||||||
pos = newline(context, str, pos + 1, c)
|
pos = newline(context, str, pos + 1, c)
|
||||||
elseif c == "" then
|
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
|
return tokens.STRING, pos
|
||||||
elseif c == "z" then
|
elseif c == "z" then
|
||||||
pos = pos + 2
|
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)
|
local next_pos, _, c = find(str, "([%S\r\n])", pos)
|
||||||
|
|
||||||
if not next_pos then
|
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
|
return tokens.STRING, #str
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ local function lex_long_str(context, str, start, len)
|
|||||||
elseif c == "[" then
|
elseif c == "[" then
|
||||||
local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "[")
|
local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "[")
|
||||||
if ok and boundary_pos - pos == len and len == 1 then
|
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
|
end
|
||||||
|
|
||||||
pos = boundary_pos
|
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)
|
local end_pos = lex_long_str(context, str, boundary_pos + 1, boundary_pos - pos)
|
||||||
if end_pos then return tokens.STRING, end_pos end
|
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
|
return tokens.ERROR, #str
|
||||||
elseif pos + 1 == boundary_pos then -- Just a "["
|
elseif pos + 1 == boundary_pos then -- Just a "["
|
||||||
return tokens.OSQUARE, pos
|
return tokens.OSQUARE, pos
|
||||||
else -- Malformed long string, for instance "[="
|
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
|
return tokens.ERROR, boundary_pos
|
||||||
end
|
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)
|
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
|
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
|
return tokens.ERROR, #str
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -313,18 +313,18 @@ local function lex_token(context, str, pos)
|
|||||||
if end_pos - pos <= 3 then
|
if end_pos - pos <= 3 then
|
||||||
local contents = sub(str, pos, end_pos)
|
local contents = sub(str, pos, end_pos)
|
||||||
if contents == "&&" then
|
if contents == "&&" then
|
||||||
context.report(errors.wrong_and(pos, end_pos))
|
context.report(errors.wrong_and, pos, end_pos)
|
||||||
return tokens.AND, end_pos
|
return tokens.AND, end_pos
|
||||||
elseif contents == "||" then
|
elseif contents == "||" then
|
||||||
context.report(errors.wrong_or(pos, end_pos))
|
context.report(errors.wrong_or, pos, end_pos)
|
||||||
return tokens.OR, end_pos
|
return tokens.OR, end_pos
|
||||||
elseif contents == "!=" or contents == "<>" then
|
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
|
return tokens.NE, end_pos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context.report(errors.unexpected_character(pos))
|
context.report(errors.unexpected_character, pos)
|
||||||
return tokens.ERROR, end_pos
|
return tokens.ERROR, end_pos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
File diff suppressed because one or more lines are too long
@ -107,7 +107,9 @@ local function set_status(text, ok)
|
|||||||
status_text = text
|
status_text = text
|
||||||
end
|
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)
|
set_status("Disk is low on space", false)
|
||||||
else
|
else
|
||||||
local message
|
local message
|
||||||
|
@ -26,6 +26,12 @@ local function pcm_decoder(chunk)
|
|||||||
return buffer
|
return buffer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function report_invalid_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
|
||||||
|
|
||||||
|
|
||||||
local cmd = ...
|
local cmd = ...
|
||||||
if cmd == "stop" then
|
if cmd == "stop" then
|
||||||
@ -93,6 +99,11 @@ elseif cmd == "play" then
|
|||||||
|
|
||||||
handle.read(4)
|
handle.read(4)
|
||||||
start = nil
|
start = nil
|
||||||
|
-- Detect several other common audio files.
|
||||||
|
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 == "<!DO" --[[<!DOCTYPE]] then return report_invalid_format("HTML")
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Playing " .. file)
|
print("Playing " .. file)
|
||||||
|
@ -21,21 +21,13 @@ local tEnv = {
|
|||||||
}
|
}
|
||||||
setmetatable(tEnv, { __index = _ENV })
|
setmetatable(tEnv, { __index = _ENV })
|
||||||
|
|
||||||
-- Replace our package.path, so that it loads from the current directory, rather
|
-- Replace our require with new instance that loads from the current directory
|
||||||
-- than from /rom/programs. This makes it a little more friendly to use and
|
-- rather than from /rom/programs. This makes it more friendly to use and closer
|
||||||
-- closer to what you'd expect.
|
-- to what you'd expect.
|
||||||
do
|
do
|
||||||
|
local make_package = require "cc.require".make
|
||||||
local dir = shell.dir()
|
local dir = shell.dir()
|
||||||
if dir:sub(1, 1) ~= "/" then dir = "/" .. dir end
|
_ENV.require, _ENV.package = make_package(_ENV, dir)
|
||||||
if dir:sub(-1) ~= "/" then dir = dir .. "/" end
|
|
||||||
|
|
||||||
local strip_path = "?;?.lua;?/init.lua;"
|
|
||||||
local path = package.path
|
|
||||||
if path:sub(1, #strip_path) == strip_path then
|
|
||||||
path = path:sub(#strip_path + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
package.path = dir .. "?;" .. dir .. "?.lua;" .. dir .. "?/init.lua;" .. path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if term.isColour() then
|
if term.isColour() then
|
||||||
@ -78,18 +70,13 @@ while running do
|
|||||||
|
|
||||||
local name, offset = "=lua[" .. chunk_idx .. "]", 0
|
local name, offset = "=lua[" .. chunk_idx .. "]", 0
|
||||||
|
|
||||||
local force_print = 0
|
|
||||||
local func, err = load(input, name, "t", tEnv)
|
local func, err = load(input, name, "t", tEnv)
|
||||||
|
if load("return " .. input) then
|
||||||
local expr_func = load("return _echo(" .. input .. ");", name, "t", tEnv)
|
-- We wrap the expression with a call to _echo(...), which prevents tail
|
||||||
if not func then
|
-- calls (and thus confusing errors). Note we check this is a valid
|
||||||
if expr_func then
|
-- expression separately, to avoid accepting inputs like `)--` (which are
|
||||||
func = expr_func
|
-- parsed as `_echo()--)`.
|
||||||
offset = 13
|
func = load("return _echo(" .. input .. "\n)", name, "t", tEnv)
|
||||||
force_print = 1
|
|
||||||
end
|
|
||||||
elseif expr_func then
|
|
||||||
func = expr_func
|
|
||||||
offset = 13
|
offset = 13
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -99,9 +86,8 @@ while running do
|
|||||||
|
|
||||||
local results = table.pack(exception.try(func))
|
local results = table.pack(exception.try(func))
|
||||||
if results[1] then
|
if results[1] then
|
||||||
local n = 1
|
for i = 2, results.n do
|
||||||
while n < results.n or n <= force_print do
|
local value = results[i]
|
||||||
local value = results[n + 1]
|
|
||||||
local ok, serialised = pcall(pretty.pretty, value, {
|
local ok, serialised = pcall(pretty.pretty, value, {
|
||||||
function_args = settings.get("lua.function_args"),
|
function_args = settings.get("lua.function_args"),
|
||||||
function_source = settings.get("lua.function_source"),
|
function_source = settings.get("lua.function_source"),
|
||||||
@ -111,7 +97,6 @@ while running do
|
|||||||
else
|
else
|
||||||
print(tostring(value))
|
print(tostring(value))
|
||||||
end
|
end
|
||||||
n = n + 1
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
printError(results[2])
|
printError(results[2])
|
||||||
|
@ -34,7 +34,14 @@ public class AddressRuleTest
|
|||||||
@ValueSource( strings = {
|
@ValueSource( strings = {
|
||||||
"0.0.0.0", "[::]",
|
"0.0.0.0", "[::]",
|
||||||
"localhost", "127.0.0.1.nip.io", "127.0.0.1", "[::1]",
|
"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 )
|
public void blocksLocalDomains( String domain )
|
||||||
{
|
{
|
||||||
|
@ -49,4 +49,18 @@ describe("cc.internal.syntax", function()
|
|||||||
describe_golden("the lexer", "lexer_spec.md", true)
|
describe_golden("the lexer", "lexer_spec.md", true)
|
||||||
describe_golden("the parser", "parser_spec.md", false)
|
describe_golden("the parser", "parser_spec.md", false)
|
||||||
describe_golden("the parser (all states)", "parser_exhaustive_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)
|
end)
|
||||||
|
@ -45,8 +45,9 @@ local function capture_parser(input, print_tokens, start)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local context = make_context(input)
|
local context = make_context(input)
|
||||||
function context.report(message)
|
function context.report(message, ...)
|
||||||
expect(3, message, "table")
|
expect(3, message, "table", "function")
|
||||||
|
if type(message) == "function" then message = message(...) end
|
||||||
|
|
||||||
for _, msg in ipairs(message) do
|
for _, msg in ipairs(message) do
|
||||||
if type(msg) == "table" and msg.tag == "annotate" then
|
if type(msg) == "table" and msg.tag == "annotate" then
|
||||||
|
Loading…
Reference in New Issue
Block a user