Merged port and link behaviour into only one type.

master
Nekojimi 2 years ago
parent f4e9c3e823
commit dee5a39bcf
  1. 130
      README.md
  2. 32
      nbactions.xml
  3. 13
      pom.xml
  4. 42
      src/main/java/moe/nekojimi/nodeprocessor/links/ConstantLink.java
  5. 69
      src/main/java/moe/nekojimi/nodeprocessor/links/Link.java
  6. 38
      src/main/java/moe/nekojimi/nodeprocessor/links/PullLink.java
  7. 42
      src/main/java/moe/nekojimi/nodeprocessor/links/QueueLink.java
  8. 25
      src/main/java/moe/nekojimi/nodeprocessor/nodes/MethodNode.java
  9. 45
      src/main/java/moe/nekojimi/nodeprocessor/nodes/Node.java
  10. 5
      src/main/java/moe/nekojimi/nodeprocessor/nodes/ObjectNode.java
  11. 3
      src/main/java/moe/nekojimi/nodeprocessor/nodes/SerialiserNode.java
  12. 10
      src/main/java/moe/nekojimi/nodeprocessor/nodes/ValueNode.java
  13. 18
      src/main/java/moe/nekojimi/nodeprocessor/ports/ConstantOutPort.java
  14. 5
      src/main/java/moe/nekojimi/nodeprocessor/ports/InPort.java
  15. 7
      src/main/java/moe/nekojimi/nodeprocessor/ports/OutPort.java
  16. 37
      src/main/java/moe/nekojimi/nodeprocessor/ports/PullOutPort.java
  17. 18
      src/main/java/moe/nekojimi/nodeprocessor/ports/QueueOutPort.java

@ -1,2 +1,130 @@
# JavaMavenTemplate
# Inner Platform (Working Title)
A visual graph-based hacking language made for stupid purposes.
## Applications
This repository contains the source for three applications:
`inner-platform`, the runtime for executing programs
`inner-server`, a daemon for managing, debugging, and deploying your programs
`inner-editor`, a GUI application for editing programs
## Specification
A basic and rubbish spec for the Inner Platfom Hacking Language (IPHL)
### Concepts
The executable unit of IPHL code is the **program**, which lives in a .iphp file. Non-executable code (i.e libraries) is a **module** which lives in a .iphm file.
These are mostly the same and so the word program will be used for both cases; maybe I'll have to come up with a generic name for both at some point.
A program is made up of **nodes** and **pipes**. A node represents a function, object, or other thing-that-does-processing. A **pipe** carries **values** in one direction between nodes, and also carry **signals** in either direction which transfer control.
Each node has a number of **ports**, which can be either input or output ports. A pipe always connects one output port to one input port. Normally, any port may have zero to many pipes connected to it, although some nodes may impose a uniqueness rule on their ports (see Ports).
### Nodes
A node's purpose is to take input from it's input ports, process it, and send it to the output ports. It may also communicate with other sources it owns (such as a websocket, external process), although it should not cause side effects that are observable from other nodes, unless the programmer sets this up deliberately.
#### Representation
A node is visually represented by a box.
#### Properties
Things a node has:
- A name, which can be changed but defaults to a value
- A class, see below
- Zero or more input ports
- Zero or more output ports
- A reset function, which clears any internal state from a node
- A check function, which tests if a node has enough input data for it's process function to complete
- A process function, which takes values from the input ports and turns them into values in the output ports.
Nodes should have at least one port (of either type) otherwise they're useless.
#### Node classes
### Ports
#### Properties
A port has:
- A name
- A type
- A mode (see Ports#Modes), for output ports
- Zero or more connected pipes
#### Modes
Output ports can operate in one of three modes, which defines the mechanics of when and how new values are passed to a connected pipe:
- **Push ports**: these push a new value into the pipes at the prompting of the node they belong to; they also signal the node at the other end of the pipe when they do. These are used for nodes that provide spontaneous input to the program, such as receiving button press or IM messages.
- **Pull ports:**: these provide a value at the prompting of the node at the other end of the pipe (the one with a connected input port). They may block while waiting for a value, or may indicate that there is no value available, which prevents the pulling node from processing. If a pull-port is connected to multiple pipes, pulling a value from one causes the same value to get pushed to all the others.
- **Static ports**: these hold a single value that is infinitely copied, and is not expected to change often. They function like push-ports in most respects, but they only push a value when the value they hold changes. They always have a value available to be pulled, and pulling this value does not change the state.
#### Representation
A port is visually represented by a small shape, such as a circle. The colour of a port represents its type, a listing of suggested colours is below:
- Red: boolean values
- Orange: signal only (see Pipes)
- Blue: integers
- Cyan: floats
- Green: strings
- Yellow: complex objects
- Black/white (depending which way up your colour scheme is): any type, or unknown
If a port's type is an array (or collection, etc), it's colour is that of the array type, but the port icon is double-lined.
An output port's mode determines its shape:
- Push ports are circles;
- Pull ports are diamonds;
- Static ports are squares.
All input ports are circles.
### Pipes
Pipes connect an output port to an input port, and carry values between them.
### Program Flow
#### Node Deque
Internally, each program thread maintains a deque of nodes. At each step of the program, the node at the front of the deque is removed, and this node is processed; it reads or takes values from its input ports, and writes new values to its output ports. When a new value is written to an output port, a signal is sent through any connected pipes.
#### Signalling
To transfer control between nodes, there is a process called **signalling**; when a node is signalled, its check function is called to determine if the node can be processed. If so, this node is added to the node deque, normally at the back.
A node is signalled under the following conditions:
- When a signal arrives at an input port from another node, indicating that a new value is available in the pipe.
- When another node wants to pull a value from a pull-port of a node. **Note that** in this case, the signalled node goes to the front of the node deque, not the back; this is so a chain of nodes with pull-ports will be processed in the reverse order that they were signalled.
- Whenever a node has just finished processing, that same node is signalled again.
- A node may signal itself when an object it controls (running in another thread) receives new data. For example, a node implementing a bot on an IM program will signal itself when a new message arrives; a node representing a button will signal itself when the button is pressed.
- At the start of the program, the start nodes are signalled.
#### Program Start
At the start of the program, the following things happen in order:
- All nodes are asked to reset; this means to clear any volatile state information they hold. Nodes may keep persistent state between runs of the program, but only if this is an obvious part of their function.
- Start nodes are signalled in an arbitrary order.
### File Types
IPHL code is stored in YAML format, although with renamed file extensions. Programs themselves are stored in .iphp files (for Inner Platform Hacking Program) while modules (parts of programs) are stored in .iphm files (Inner Platform Hacking Module). Besides their different contents, the two file types are identical.
The exact structure of these files isn't defined yet, but each one is composed of a number of sections. Most sections are optional; if a IPHL editor doesn't know what to do with an section, it should pass it through without touching it.
- shebang: files always begin with #!/bin/innerplatform, which identifies them to the shell.
- `metadata:` contains metadata: what this file is, who made it, and so on.
- `code:` contains the actual nodes and pipes. This is the only mandatory section.
- `layout:` contains layout information: chiefly, where the nodes are positioned on screen, and what routes the pipes take.

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<actions>
<action>
<actionName>run</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.5.0:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath moe.nekojimi.nodeprocessor.Main</exec.args>
<exec.executable>java</exec.executable>
</properties>
</action>
<action>
<actionName>debug</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.5.0:exec</goal>
</goals>
<properties>
<exec.args>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath moe.nekojimi.nodeprocessor.Main</exec.args>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
</properties>
</action>
</actions>

@ -35,4 +35,17 @@
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.amihaiemil.web</groupId>
<artifactId>eo-yaml</artifactId>
<version>5.2.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
</dependencies>
</project>

@ -1,42 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moe.nekojimi.nodeprocessor.links;
import moe.nekojimi.nodeprocessor.ports.ConstantOutPort;
import moe.nekojimi.nodeprocessor.ports.InPort;
public class ConstantLink<T> extends Link<T, InPort<T>, ConstantOutPort<T>>
{
T value = null;
public ConstantLink(ConstantOutPort<T> from, InPort<T> to)
{
super(from, to);
}
@Override
public T read()
{
return value;
}
@Override
public void write(T val)
{
if (val != value)
signal();
value = val;
}
@Override
public boolean has()
{
return value != null;
}
}

@ -5,21 +5,23 @@
*/
package moe.nekojimi.nodeprocessor.links;
import java.util.ArrayDeque;
import java.util.Queue;
import moe.nekojimi.nodeprocessor.nodes.Node;
import moe.nekojimi.nodeprocessor.ports.ConstantOutPort;
import moe.nekojimi.nodeprocessor.ports.InPort;
import moe.nekojimi.nodeprocessor.ports.OutPort;
import moe.nekojimi.nodeprocessor.ports.QueueOutPort;
/**
*
* @author jimj316
*/
public abstract class Link<T, I extends InPort<T>, O extends OutPort<T>>
public class Link<T, I extends InPort<T>, O extends OutPort<T>>
{
final O from;
private final I to;
private final Queue<T> queue = new ArrayDeque<>(1);
public Link(O from, I to)
{
this.from = from;
@ -41,35 +43,58 @@ public abstract class Link<T, I extends InPort<T>, O extends OutPort<T>>
to.disconnect(this);
}
public abstract T read();
/**
* Attempt to take a value from the link
*
* @return
*/
public T read()
{
return queue.poll();
}
public abstract void write(T val);
public void write(T val)
{
queue.add(val);
push();
}
public abstract boolean has();
public boolean has()
{
return !queue.isEmpty();
}
public static <T> Link<T, ?, ?> makeLink(Class<T> clazz, Node from, String fromPort, Node to, String toPort)
public boolean pull()
{
OutPort<T> outPort = from.getOutPort(fromPort, clazz);
InPort<T> inPort = to.getInPort(toPort, clazz);
if (outPort instanceof QueueOutPort)
{
QueueOutPort<T> q = (QueueOutPort<T>) outPort;
return new QueueLink<>(q, inPort);
} else if (outPort instanceof ConstantOutPort)
{
ConstantOutPort<T> c = (ConstantOutPort<T>) outPort;
return new ConstantLink<>(c, inPort);
} else
{
return null;
}
from.pull();
return has();
}
protected void signal()
protected void push()
{
to.signal();
}
public static <T> Link<T, ?, ?> makeLink(Class<T> clazz, Node from, String fromPort, Node to, String toPort)
{
OutPort<T> outPort = from.getOutPort(fromPort, clazz);
InPort<T> inPort = to.getInPort(toPort, clazz);
// if (outPort instanceof QueueOutPort)
// {
// QueueOutPort<T> q = (QueueOutPort<T>) outPort;
// return new QueueLink<>(q, inPort);
// } else if (outPort instanceof ConstantOutPort)
// {
// ConstantOutPort<T> c = (ConstantOutPort<T>) outPort;
// return new ConstantLink<>(c, inPort);
// } else
// {
// return null;
// }
return new Link<>(outPort, inPort);
}
public O getFrom()
{
return from;

@ -1,38 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moe.nekojimi.nodeprocessor.links;
import moe.nekojimi.nodeprocessor.ports.InPort;
import moe.nekojimi.nodeprocessor.ports.PullOutPort;
public class PullLink<T> extends Link<T, InPort<T>, PullOutPort<T>>
{
public PullLink(PullOutPort<T> from, InPort<T> to)
{
super(from, to);
}
@Override
public T read()
{
return from.pull();
}
@Override
public void write(T val)
{
// not used
}
@Override
public boolean has()
{
return true;
}
}

@ -1,42 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moe.nekojimi.nodeprocessor.links;
import java.util.LinkedList;
import java.util.Queue;
import moe.nekojimi.nodeprocessor.ports.InPort;
import moe.nekojimi.nodeprocessor.ports.QueueOutPort;
public class QueueLink<T> extends Link<T, InPort<T>, QueueOutPort<T>>
{
private final Queue<T> queue = new LinkedList<>();
public QueueLink(QueueOutPort<T> from, InPort<T> to)
{
super(from, to);
}
@Override
public T read()
{
return queue.poll();
}
@Override
public void write(T val)
{
queue.add(val);
signal();
}
@Override
public boolean has()
{
return !queue.isEmpty();
}
}

@ -10,10 +10,12 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import moe.nekojimi.nodeprocessor.Program;
import moe.nekojimi.nodeprocessor.ports.QueueOutPort;
import moe.nekojimi.nodeprocessor.ports.OutPort;
/**
*
@ -24,13 +26,17 @@ public class MethodNode<C> extends Node
{
private final InPort<C> objectInPort;
private final QueueOutPort returnOutPort;
private final OutPort returnOutPort;
private final List<InPort> argInPorts;
private final Method method;
public MethodNode(Program scene, Class<C> clazz, Method method)
{
super(scene);
assert (method.getDeclaringClass() == clazz);
argInPorts = new ArrayList<>();
if (Modifier.isStatic(method.getModifiers()))
objectInPort = null;
else
@ -41,9 +47,13 @@ public class MethodNode<C> extends Node
this.method = method;
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters)
addInPort(new InPort(parameter.getName(), parameter.getType(), this));
{
final InPort inPort = new InPort(parameter.getName(), parameter.getType(), this);
argInPorts.add(inPort);
addInPort(inPort);
}
Class<?> returnType = method.getReturnType();
returnOutPort = new QueueOutPort("out", returnType, this);
returnOutPort = new OutPort("out", returnType, this);
addOutPort(returnOutPort);
setName(method.getDeclaringClass().getSimpleName() + " " + method.getName());
@ -58,7 +68,12 @@ public class MethodNode<C> extends Node
C object = null;
if (objectInPort != null)
object = objectInPort.get();
Object ret = method.invoke(object);
Object[] args = new Object[argInPorts.size()];
for (int i = 0; i < args.length; i++)
args[i] = argInPorts.get(i).get();
Object ret = method.invoke(object, args);
returnOutPort.set(ret);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex)
{

@ -6,9 +6,12 @@
package moe.nekojimi.nodeprocessor.nodes;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import moe.nekojimi.nodeprocessor.ports.InPort;
import moe.nekojimi.nodeprocessor.ports.OutPort;
import moe.nekojimi.nodeprocessor.Program;
import moe.nekojimi.nodeprocessor.ports.Port;
/**
*
@ -52,28 +55,38 @@ public abstract class Node
public final <X> InPort<X> getInPort(String name, Class<X> clazz)
{
Class<? super X> c = clazz;
InPort ret;
do
{
ret = inPorts.get(getPortName(c, name));
c = c.getSuperclass();
} while (ret == null && c != null);
return ret;
return (InPort<X>) getPort(name, clazz, inPorts);
}
public final <X> OutPort<X> getOutPort(String name, Class<X> clazz)
{
return (OutPort<X>) getPort(name, clazz, outPorts);
}
private final <X> Port<X> getPort(String name, Class<X> clazz, Map<String, ? extends Port> ports)
{
Class<? super X> c = clazz;
OutPort ret;
Port ret;
do
{
ret = outPorts.get(getPortName(clazz, name));
ret = ports.get(getPortName(c, name));
c = c.getSuperclass();
} while (ret == null && c != null);
if (ret == null)
if (clazz.isPrimitive())
return getPort(name, getBoxedType(clazz), ports);
else
portFindError(name, clazz, ports);
return ret;
}
private static final void portFindError(String name, Class clazz, Map<String, ? extends Port> ports)
{
String portList = ports.keySet().stream().collect(Collectors.joining(", "));
throw new IllegalArgumentException("Cannot find port with identity " + getPortName(clazz, name) + ", possibilities are: " + portList);
}
public final Collection<InPort> getInPorts()
{
return inPorts.values();
@ -155,4 +168,16 @@ public abstract class Node
return ret;
};
private static final Class getBoxedType(Class clazz)
{
if (clazz == int.class)
return Integer.class;
else if (clazz == long.class)
return Long.class;
else if (clazz == byte.class)
return Byte.class;
else
throw new IllegalArgumentException("wtf");
}
}

@ -15,7 +15,6 @@ import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import moe.nekojimi.nodeprocessor.Program;
import moe.nekojimi.nodeprocessor.ports.ConstantOutPort;
import moe.nekojimi.nodeprocessor.ports.InPort;
import moe.nekojimi.nodeprocessor.ports.OutPort;
@ -43,7 +42,7 @@ public class ObjectNode<T> extends Node
super(scene);
this.clazz = clazz;
this.constructor = constructor;
objectOutPort = new ConstantOutPort<>("this", clazz, this);
objectOutPort = new OutPort<>("this", clazz, this);
addConstructor();
addAutoMethods();
@ -65,7 +64,7 @@ public class ObjectNode<T> extends Node
} else if (returnType != null && parameters.length == 0 && (name.startsWith("get") || name.startsWith("is")))
{
// this is a getter method
ConstantOutPort out = new ConstantOutPort(name, returnType, this);
OutPort out = new OutPort(name, returnType, this);
addOutPort(out);
getterOutPorts.add(out);
}

@ -9,7 +9,6 @@ import java.util.Iterator;
import moe.nekojimi.nodeprocessor.Program;
import moe.nekojimi.nodeprocessor.ports.InPort;
import moe.nekojimi.nodeprocessor.ports.OutPort;
import moe.nekojimi.nodeprocessor.ports.QueueOutPort;
public class SerialiserNode<T> extends Node
{
@ -27,7 +26,7 @@ public class SerialiserNode<T> extends Node
aClass = (Class<Iterable<T>>) it.getClass();
addInPort(new InPort("in", aClass, this));
this.clazz = clazz;
addOutPort(new QueueOutPort("out", clazz, this));
addOutPort(new OutPort("out", clazz, this));
}
@Override

@ -6,15 +6,15 @@
package moe.nekojimi.nodeprocessor.nodes;
import moe.nekojimi.nodeprocessor.Program;
import moe.nekojimi.nodeprocessor.ports.ConstantOutPort;
import moe.nekojimi.nodeprocessor.ports.InPort;
import moe.nekojimi.nodeprocessor.ports.OutPort;
public class ValueNode<T> extends Node
{
private T value;
private final ConstantOutPort<T> outPort;
private final OutPort<T> outPort;
private final InPort<T> inPort;
public ValueNode(Program scene, Class<T> clazz)
@ -22,14 +22,14 @@ public class ValueNode<T> extends Node
super(scene);
inPort = new InPort("set", clazz, this);
addInPort(inPort);
outPort = new ConstantOutPort("out", clazz, this);
outPort = new OutPort("out", clazz, this);
addOutPort(outPort);
}
public void setValue(T val)
{
value = val;
signal();
// signal();
}
public T getValue()
@ -49,7 +49,7 @@ public class ValueNode<T> extends Node
@Override
public boolean check()
{
return inPort.has();
return true;
}
}

@ -1,18 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moe.nekojimi.nodeprocessor.ports;
import moe.nekojimi.nodeprocessor.nodes.Node;
public class ConstantOutPort<T> extends OutPort<T>
{
public ConstantOutPort(String name, Class<T> clazz, Node node)
{
super(name, clazz, node);
}
}

@ -35,6 +35,11 @@ public class InPort<T> extends Port<T>
if (link.has())
return link.read();
}
for (Link<T, ?, ?> link : links)
{
link.pull(); // push the from side to prompt it to give us a value; it goes in the queue
node.signal(); // push the to side to receive the new value (or not)
}
return null;
}

@ -12,7 +12,7 @@ import moe.nekojimi.nodeprocessor.nodes.Node;
*
* @author jimj316
*/
public abstract class OutPort<T> extends Port<T>
public class OutPort<T> extends Port<T>
{
public OutPort(String name, Class<T> clazz, Node node)
@ -28,6 +28,11 @@ public abstract class OutPort<T> extends Port<T>
}
}
public void pull()
{
node.signal();
}
@Override
public void connect(Link<T, ?, ?> link)
{

@ -1,37 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moe.nekojimi.nodeprocessor.ports;
import java.util.function.Supplier;
import moe.nekojimi.nodeprocessor.nodes.Node;
public class PullOutPort<T> extends OutPort<T>
{
private Supplier<T> supplier;
public PullOutPort(String name, Class<T> clazz, Node node)
{
super(name, clazz, node);
}
public void setSupplier(Supplier<T> supplier)
{
this.supplier = supplier;
}
@Override
public void set(T value)
{
// not used
}
public T pull()
{
return supplier.get();
}
}

@ -1,18 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moe.nekojimi.nodeprocessor.ports;
import moe.nekojimi.nodeprocessor.nodes.Node;
public class QueueOutPort<T> extends OutPort<T>
{
public QueueOutPort(String name, Class<T> clazz, Node node)
{
super(name, clazz, node);
}
}
Loading…
Cancel
Save