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 `InputBus` and added several convenience methods
|
||||||
* `[PlutoCore]` Refactored input callbacks
|
* `[PlutoCore]` Refactored input callbacks
|
||||||
* `[PlutoStatic]` Slight cleanup in the `Display` and `DisplayBuilder` classes
|
* `[PlutoStatic]` Slight cleanup in the `Display` and `DisplayBuilder` classes
|
||||||
* `[PlutoCommandParser]` Code cleanup
|
* `[PlutoCommandParser]` **Initial release**
|
||||||
|
|
||||||
## 20.2.0.0-alpha.1
|
## 20.2.0.0-alpha.1
|
||||||
* `[PlutoLib#cz.tefek.pluto.io.logger]` Refactored the Logger subsystem
|
* `[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;
|
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
|
@Override
|
||||||
public final int hashCode()
|
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_PREFIX,
|
||||||
UNRESOLVED_COMMAND_NAME,
|
UNRESOLVED_COMMAND_NAME,
|
||||||
UNRESOLVED_PARAMETERS,
|
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;
|
break;
|
||||||
|
|
||||||
case END:
|
case END:
|
||||||
this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK;
|
|
||||||
return false;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK;
|
this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK;
|
||||||
return false;
|
return false;
|
||||||
|
@ -156,6 +153,8 @@ public class CommandParser
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.ctx.command(this.command);
|
||||||
|
|
||||||
return true;
|
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