Code restructure and began documentation
This commit is contained in:
parent
aa70f1bbe2
commit
5952a26fee
|
@ -17,7 +17,7 @@ can now only be modified only through public setters
|
|||
* `[PlutoCore]` Refactored `InputBus` and added several convenience methods
|
||||
* `[PlutoCore]` Refactored input callbacks
|
||||
* `[PlutoStatic]` Slight cleanup in the `Display` and `DisplayBuilder` classes
|
||||
* `[PlutoCommandParser]` Code cleanup
|
||||
* `[PlutoCommandParser]` **Initial release**
|
||||
|
||||
## 20.2.0.0-alpha.1
|
||||
* `[PlutoLib#cz.tefek.pluto.io.logger]` Refactored the Logger subsystem
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# plutoengine:plutocommandparser
|
||||
|
||||
PlutoEngine's command parser.
|
||||
|
||||
## Description
|
||||
|
||||
PlutoCommandParser is an attempt to streamline my previous attempts at command parsers. Its main
|
||||
goal is to provide a streamlined and flexible tokenizer, parser and evaluator for a simple
|
||||
user-friendly CLI-like language called `PlutoCmd`.
|
||||
|
||||
## Goals
|
||||
|
||||
* High syntax error tolerance
|
||||
* Provide implementations for basic Java types, such as primitives and Strings
|
||||
* Allow extensibility while providing a strong foundation
|
||||
* Complete user control over localization, no hardcoded Strings
|
||||
|
||||
## Non-goals
|
||||
|
||||
PlutoCmd is *not* a replacement for standard scripting languages and will most likely
|
||||
never be a Turing-complete language.
|
||||
|
||||
|
||||
## Implementation style
|
||||
|
||||
### Command implementation
|
||||
|
||||
Each command has its own *final* class, abstractions over CommandBase are howered allowed.
|
||||
Note the usage of `ConstantExpression` annotation over some interface methods. These
|
||||
annotations **must** be respected - methods must be stateless and deterministic.
|
||||
|
||||
## PlutoCmd language specification
|
||||
|
||||
### General syntax
|
||||
|
||||
```
|
||||
[prefix]command [arg1] [arg2] ... [argN]
|
||||
```
|
||||
|
||||
`prefix` is an optional identifier String to distinguish PlutoCmd commands from other commands.
|
||||
|
||||
`command` is an alias for a command, handled by one of the command handlers
|
||||
|
||||
`argX` is an argument of the invoked command-function, optionally quoted to preserve whitespace.
|
||||
|
|
@ -1,14 +1,59 @@
|
|||
package cz.tefek.pluto.command;
|
||||
|
||||
public abstract class CommandBase
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import cz.tefek.pluto.command.invoke.InvokeHandler;
|
||||
|
||||
public abstract class CommandBase implements ICommand
|
||||
{
|
||||
public abstract String name();
|
||||
private final Class<? extends CommandBase> clazz;
|
||||
|
||||
public abstract String[] aliases();
|
||||
public final Class<?> commandClass()
|
||||
{
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
public abstract String description();
|
||||
protected CommandBase()
|
||||
{
|
||||
this.clazz = this.getClass();
|
||||
|
||||
public abstract Class<?> commandClass();
|
||||
if (!Modifier.isFinal(this.clazz.getModifiers()))
|
||||
{
|
||||
// Class must be final
|
||||
// Throwing an exception here is okay, since this is the developer's fault
|
||||
//
|
||||
// You can still create abstract wrappers for CommandBase, however implemented commands
|
||||
// must be final.
|
||||
throw new RuntimeException("Command classes MUST be final. Offender: " + this.clazz);
|
||||
}
|
||||
|
||||
var methods = this.clazz.getMethods();
|
||||
|
||||
for (var method : methods)
|
||||
{
|
||||
// Silently skip methods without annotations
|
||||
if (!method.isAnnotationPresent(InvokeHandler.class))
|
||||
continue;
|
||||
|
||||
var modifiers = method.getModifiers();
|
||||
|
||||
if (Modifier.isStatic(modifiers))
|
||||
{
|
||||
// Method must be non-static
|
||||
// Throwing an exception here is okay, since this is the developer's fault
|
||||
throw new RuntimeException("Invoke handlers MUST NOT be static. Offender: " + method);
|
||||
}
|
||||
|
||||
if (!Modifier.isPublic(modifiers))
|
||||
{
|
||||
// Method must be public
|
||||
// Throwing an exception here is okay, since this is the developer's fault
|
||||
throw new RuntimeException("Invoke handlers MUST be public. Offender: " + method);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode()
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package cz.tefek.pluto.command;
|
||||
|
||||
import cz.tefek.pluto.annotation.ConstantExpression;
|
||||
|
||||
public interface ICommand
|
||||
{
|
||||
@ConstantExpression
|
||||
String name();
|
||||
|
||||
@ConstantExpression
|
||||
String[] aliases();
|
||||
|
||||
@ConstantExpression
|
||||
String description();
|
||||
|
||||
@ConstantExpression
|
||||
Class<?> commandClass();
|
||||
}
|
|
@ -91,6 +91,6 @@ public class CommandContextBuilder
|
|||
UNRESOLVED_PREFIX,
|
||||
UNRESOLVED_COMMAND_NAME,
|
||||
UNRESOLVED_PARAMETERS,
|
||||
UNRESOLVED_UNEXPECTED_STATE;
|
||||
UNRESOLVED_UNEXPECTED_STATE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package cz.tefek.pluto.command.invoke;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Denotes a handler for an invocation of a command.
|
||||
*
|
||||
* <p><em>
|
||||
* Method must be public and non-static.
|
||||
* </em></p>
|
||||
*
|
||||
* <p>
|
||||
* While classes implementing the Command API must be final, it is not required for the handler methods
|
||||
* as that would be redundant.
|
||||
* </p>
|
||||
*
|
||||
* @author 493msi
|
||||
*
|
||||
* @since 20.2.0.0-alpha.2
|
||||
* */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface InvokeHandler
|
||||
{
|
||||
}
|
|
@ -131,9 +131,6 @@ public class CommandParser
|
|||
break;
|
||||
|
||||
case END:
|
||||
this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK;
|
||||
return false;
|
||||
|
||||
default:
|
||||
this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK;
|
||||
return false;
|
||||
|
@ -156,6 +153,8 @@ public class CommandParser
|
|||
return false;
|
||||
}
|
||||
|
||||
this.ctx.command(this.command);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package cz.tefek.pluto.command.resolver.primitive;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import cz.tefek.pluto.command.resolver.GenericResolver;
|
||||
|
||||
public class StringResolver extends GenericResolver<String>
|
||||
{
|
||||
public StringResolver()
|
||||
{
|
||||
super(String::valueOf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<String> getOutputType()
|
||||
{
|
||||
return String.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package cz.tefek.pluto.command.resolver.command;
|
||||
|
||||
import cz.tefek.pluto.command.CommandBase;
|
||||
import cz.tefek.pluto.command.invoke.InvokeHandler;
|
||||
|
||||
public final class TestCommand extends CommandBase
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases()
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description()
|
||||
{
|
||||
return "The test command - prints Hello World to stdout.";
|
||||
}
|
||||
|
||||
@InvokeHandler
|
||||
public void invoke()
|
||||
{
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package cz.tefek.pluto.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Denotes that the target field or method should be a constant expression - it has no state and always yields
|
||||
* the same deterministic result for given input. Generally, annotated methods should be thread-safe, however
|
||||
* this is not required.
|
||||
*
|
||||
* @author 493msi
|
||||
*
|
||||
* @since 20.2.0.0-alpha.2
|
||||
* */
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({ ElementType.METHOD, ElementType.FIELD })
|
||||
public @interface ConstantExpression
|
||||
{
|
||||
}
|
Loading…
Reference in New Issue