Skip to content
Permalink
Browse files
Add reflect_type, class_type, and Typing documentation.
As part of writing examples for the documentation, I came across the error in #1192,
which allowed me to reliably reproduce it, so this fixes #1192 also.
  • Loading branch information
LadyCailin committed May 23, 2020
1 parent 5813d34 commit b8a07c42f181599161272932f1a3904bcb774602
Show file tree
Hide file tree
Showing 19 changed files with 585 additions and 73 deletions.
@@ -418,6 +418,21 @@ public static String getString(Mixed c, Target t) {
return c.val();
}

/**
* Returns a String object from the given construct. Note that unlike {@link #getString}, this strictly expects a
* string object, and will throw a CRECastException if it is not a string.
*
* @param c
* @param t
* @return
*/
public static String getStringObject(Mixed c, Target t) {
if(!c.isInstanceOf(CString.class)) {
throw new CRECastException("Expected a string, but found " + c.typeof() + " instead.", t);
}
return c.val();
}

/**
* Returns true if any of the constructs are a CDouble, false otherwise.
*
@@ -44,6 +44,12 @@ public final class CClassType extends Construct implements com.laytonsmith.core.
@SuppressWarnings("FieldNameHidesFieldInSuperclass")
public static final CClassType TYPE;
public static final CClassType AUTO;
/**
* Used to differentiate between null and uninitialized.
*
* NOTE: This must come before the below static blocks are run.
*/
private static final Mixed[] UNINITIALIZED = new Mixed[0];

static {
try {
@@ -67,10 +73,6 @@ public final class CClassType extends Construct implements com.laytonsmith.core.
private final boolean isTypeUnion;
private final FullyQualifiedClassName fqcn;

/**
* Used to differentiate between null and uninitialized.
*/
private static final Mixed[] UNINITIALIZED = new Mixed[0];
/**
* This is an invalid instance of the underlying type that can only be used for Documentation purposes or finding
* out meta information about the class. Because these can be a type union, this is an array.
@@ -56,8 +56,8 @@ public String docs() {
+ " automatically to a double, and instead you must manually cast it via double(@decimal). This will"
+ " fail in cases where the value cannot be represented in a double, but in places that readily"
+ " accept a decimal value, they will always work appropriately. A decimal can be created from another"
+ " value using the decimal() function, or, if directly in code, defined with a trailing m character,"
+ " such as 123456.1234m. In general, it is not useful to store integral values that would otherwise"
+ " value using the decimal() function, or, if directly in code, defined with a prefix 0m character,"
+ " such as 0m123456.1234. In general, it is not useful to store integral values that would otherwise"
+ " fit in an int datatype, as operations with them will be less efficient, but having an otherwise"
+ " double value does make sense for where precision needs to be exact, and java's floating point math"
+ " and rounding does not suffice.";
@@ -8,6 +8,7 @@
import com.laytonsmith.annotations.MEnum;
import com.laytonsmith.annotations.api;
import com.laytonsmith.annotations.core;
import com.laytonsmith.core.ArgumentValidation;
import com.laytonsmith.core.FullyQualifiedClassName;
import com.laytonsmith.core.MSVersion;
import com.laytonsmith.core.Optimizable;
@@ -20,6 +21,7 @@
import com.laytonsmith.core.compiler.Keyword;
import com.laytonsmith.core.compiler.KeywordList;
import com.laytonsmith.core.constructs.CArray;
import com.laytonsmith.core.constructs.CBoolean;
import com.laytonsmith.core.constructs.CClassType;
import com.laytonsmith.core.constructs.CFunction;
import com.laytonsmith.core.constructs.CInt;
@@ -38,6 +40,7 @@
import com.laytonsmith.core.exceptions.CRE.CREIOException;
import com.laytonsmith.core.exceptions.CRE.CREIllegalArgumentException;
import com.laytonsmith.core.exceptions.CRE.CREInsufficientArgumentsException;
import com.laytonsmith.core.exceptions.CRE.CRENotFoundException;
import com.laytonsmith.core.exceptions.CRE.CREThrowable;
import com.laytonsmith.core.exceptions.ConfigCompileException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
@@ -335,6 +338,60 @@ public MSVersion since() {
}
}

@api
public static class reflect_type extends AbstractFunction {

@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class};
}

@Override
public boolean isRestricted() {
return true;
}

@Override
public Boolean runAsync() {
return null;
}

@Override
public Mixed exec(Target t, Environment environment, Mixed... args) throws ConfigRuntimeException {
CClassType type = ArgumentValidation.getClassType(args[0], t);
CArray ret = new CArray(t);
ret.set("fqcn", type.getFQCN().getFQCN());
ret.set("name", type.getFQCN().getSimpleName());
ret.set("package", type.getPackage() == null ? CNull.NULL : type.getPackage(), t);
ret.set("docs", type.docs());
ret.set("isNative", CBoolean.get(type.getNativeType() != null), t);
ret.set("interfaces", new CArray(t, type.getInterfacesForType(environment)), t);
ret.set("superclasses", new CArray(t, type.getSuperclassesForType(environment)), t);
return ret;
}

@Override
public String getName() {
return "reflect_type";
}

@Override
public Integer[] numArgs() {
return new Integer[]{1};
}

@Override
public String docs() {
return getBundledDocs();
}

@Override
public Version since() {
return MSVersion.V3_3_4;
}

}

@api
public static class reflect_docs extends AbstractFunction implements Optimizable {

@@ -842,4 +899,55 @@ public Version since() {
}

}

@api
public static class class_type extends AbstractFunction {

@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CRENotFoundException.class};
}

@Override
public boolean isRestricted() {
return true;
}

@Override
public Boolean runAsync() {
return null;
}

@Override
public Mixed exec(Target t, Environment environment, Mixed... args) throws ConfigRuntimeException {
String type = ArgumentValidation.getStringObject(args[0], t);
try {
return CClassType.get(FullyQualifiedClassName.forName(args[0].val(), t, environment));
} catch (CRECastException | ClassNotFoundException ex) {
throw new CRENotFoundException("Could not find type " + type, t);
}
}

@Override
public String getName() {
return "class_type";
}

@Override
public Integer[] numArgs() {
return new Integer[]{1};
}

@Override
public String docs() {
return "ClassType {string name} Returns a ClassType object for the given fully qualified class name."
+ " If the given class doesn't exist, a NotFoundException is thrown.";
}

@Override
public Version since() {
return MSVersion.V3_3_4;
}

}
}
@@ -11,7 +11,7 @@
* Things that implement this can be accessed like an array, with array_get, or [].
*/
@typeof("ms.lang.ArrayAccess")
public interface ArrayAccess extends Mixed {
public interface ArrayAccess extends Booleanish {

@SuppressWarnings("FieldNameHidesFieldInSuperclass")
public static final CClassType TYPE = CClassType.get(ArrayAccess.class);
@@ -665,6 +665,7 @@ public String generate(String... args) {
}

};
public static final Generator TAKE_NOTE = NOTE;

public static final Generator MYSQL_CREATE_TABLE_QUERY = new Generator() {

@@ -1,12 +1,14 @@
Often times you may find yourself with a unique set of predefined constants, for instance,
{{unimplemented}}

Often times you may find yourself with a unique set of predefined constants, for instance,
compass directions NORTH, SOUTH, EAST, and WEST, or days of the week. Additionally, you may
have a set of these enums, which can be represented as a bit mask. For these two situations,
you should use the ''enum'' and ''mask'' types, respectively.

== Enum Types ==
An enum is a specially declared class, which follows certain extra rules, but otherwise behaves
just like a normal class. It may have data members and methods, just like any other class. The
exceptions are that the class is effectively final (it cannot be extended by other classes/enums),
exceptions are that the class is effectively final (it cannot be extended by other classes/enums),
and the constructor, if provided, must be
private. The default constructor for enum values are private as well. Essentially, there must be no
way to instantiate the enum object outside of the context of the enum itself, and it can't be
@@ -77,17 +79,17 @@ getInfo() for each one would return different data.

<%CODE|
enum Compass {
NORTH('up'),
SOUTH('down'),
EAST('right'),
NORTH('up'),
SOUTH('down'),
EAST('right'),
WEST('left');

private final string @info;

private Compass(string @info){
@this->info = @info;
}

public string getInfo(){
return(@info);
}
@@ -1,3 +1,5 @@
{{unimplemented}}

The Federation system allows for easy, and secure execution of code on remote systems.
Usage consists of two steps for the client, and one step for the server.

@@ -7,16 +9,16 @@ Usage consists of two steps for the client, and one step for the server.
All MethodScript processes support Federation, even cmdline programs, which allows
for easy access to a MethodScript process from other sources. The Federation protocol is even
straightforward enough that third party systems could be made to interface with
a Federated server.
a Federated server.

For the purposes of this article, the following definitions are used:

* "MethodScript process" - refers to the process that is running a MethodScript
* "MethodScript process" - refers to the process that is running a MethodScript
interpreter. This might be a server that is hosting the MethodScript process,
a cmdline process, or some other system.
* "Server" - refers to a Federated server, not the server that is running the
MethodScript process.
* "Client" - refers to a Federated client. All MethodScript processes can
* "Client" - refers to a Federated client. All MethodScript processes can
simultaneously be both a server and a client.
* "Federated System" - any system that is either a server or a client, and knows
the Federation Protocol. Third party tools, so long as they properly implement
@@ -25,7 +27,7 @@ the Federation Protocol are considered a Federation System.
All Federated Systems must use standard TCP Sockets, though they are allowed to
fall back to other communication systems if the server and client can agree
on the communication medium. For those interested in implementing the protocol,
or generally learning more about the specifics of the protocol, see the
or generally learning more about the specifics of the protocol, see the
%%DOCLINK|FederationProtocol|technical description%% of the protocol.

== Server Setup ==
@@ -1,3 +1,5 @@
{{unimplemented}}

The Federation Protocol is the way that the client and server speak with each
other. It is an object based streaming protocol. There are several steps required
to set up a connection, but once the connection is established, making requests
@@ -57,7 +59,7 @@ server.
Each slave server should regularly check that the master port is bound to, and if
not, it should spin up a master server in addition to the slave server it is already
running. This can be done in the heartbeat thread for the slave server. Each
slave server is responsible for picking a random unused port (usually between
slave server is responsible for picking a random unused port (usually between
%%CONST|com.laytonsmith.core.federation.Federation.DYNAMIC_PORT_MINIMUM%% and
%%CONST|com.laytonsmith.core.federation.Federation.DYNAMIC_PORT_MAXIMUM%%), and
then registering that information with the file system. In standard MethodScript
@@ -71,7 +73,7 @@ checking for the master server, should update this timestamp on a regular basis.
A client request to the master server simply requests the port that the slave server
is on. This is required, since the request may come in from an outside host, that
wouldn't have access to the file system. The master server looks up the requested
server in the registry, reports the port, and kills the socket.
server in the registry, reports the port, and kills the socket.

If and only if the slave server only allows connections from localhost, may it
not start up the master server. On the other hand, if a client is connecting
@@ -93,7 +95,7 @@ it will disconnect.

Assuming the HELLO and version check are successful, the client will write the string
"GET PORT", then another string, the server name it is requesting the port of. The
server will respond with the string "OK", then the integer port number, if it found the server,
server will respond with the string "OK", then the integer port number, if it found the server,
or the string "ERROR" and then another string, which is the error message, if it could not.
Then the connection is closed.

@@ -1,12 +1,13 @@
{{unimplemented}}
<%NOTE|This is implemented, but hasn't been used yet, and so may still have missing features or bugs. Please get in
touch if you're willing to help with the localization effort%>

The MethodScript website supports localization efforts, to better serve communities that are primarily non-English
speaking. While much of the application itself is necessarily written in English (function names, etc would be a
disaster to try to localize), the documentation for the application does not necessarily need to be in English.

To this end, a Localization (or L10n) framework has been put in place to facilitate bilingual localizers to help
translate the documentation into languages other than English! Much care has been put into ensuring the accuracy of
the documentation, and the language of it is no exception. This document describes the l10n framework,
the documentation, and the language of it is no exception. This document describes the l10n framework,
which is important to understand if you wish to contribute l10ns.

== General Overview ==
@@ -42,7 +43,7 @@ important to have an overview of the core of the system. The primary database is

There are three layers to the database. The summary file,
the locale master file, and individual page files. Each translation file is an xml file. For each segment, there are
at least 2 pieces of information that all segments have, the translation id, and the original English.
at least 2 pieces of information that all segments have, the translation id, and the original English.

In the summary file, (summary.xml) we find information that relates to the segment across all locales, for instance,
whether or not this segment should be eligible for machine translation. If the value is null, then this segment hasn't
@@ -123,7 +124,7 @@ localized, as function names may only be in English. You must use common sense w
that you don't change things that must be there for site functionality or may confuse the reader. In some cases, the
entire segment is correct without any translation, for instance segments that are just the name of a function. In this
case, do not copy the english over, instead, switch to general mode and mark it as untranslatable. This will cause it
to use the English in all locales, but also remove it from the list of "missing translations". Unfortunately,
to use the English in all locales, but also remove it from the list of "missing translations". Unfortunately,
segments that contain strings that shouldn't be translated are not going to be eligible for automatic translation, and
must be manually translated, ensuring that the untranslatable parts are left intact.

@@ -137,7 +138,7 @@ create a local deployment, follow the directions [[SiteDeployTool|here]], but yo
a server that serves your local copy of the database. These directions can be modified if you wish, but the easiest
method to do so is to do the following.

Before running the site-deploy tool, in your site-deploy.ini file, set production-translations to
Before running the site-deploy tool, in your site-deploy.ini file, set production-translations to
"http://localhost:7585". Run the site-deploy tool like normal, with the latest version of the codebase. You can
either download the latest release, or build it yourself, but to avoid missing segments, it must be based on the latest
release build.
@@ -152,7 +153,7 @@ up and running, then:
* Open a new terminal, in addition to the terminal running the server for the main site.
* Run <%PRE|npx http-server -c-1 -p 7585 --cors%>

Now, when you save the localizations in the UI, you simply need to refresh the page to see the new translations.
Now, when you save the localizations in the UI, you simply need to refresh the page to see the new translations.

<%TAKENOTE|Please proofread all your translations, to ensure they are correct, and have appeared on the local site
correctly.%>
@@ -1,3 +1,5 @@
{{unimplemented}}

Creating an object and using an existing one are two different topics, and are thus given separate
treatments. This article discusses the usage of objects, rather than how they are created, for that
discussion, see [Classes].
@@ -94,7 +96,7 @@ ClassType<? extends MySuperclass> @i = MySubclass;
In this example, @i could be any subclass of MySuperclass (or MySuperclass itself). Assuming MySuperclass defines
a method named StaticMethod, then this becomes useful if MySubclass may or may not override that static method.

<%TAKENOTE|Overriding static methods is beyond the scope of this article, see the article about [Classes] for
<%TAKENOTE|Overriding static methods is beyond the scope of this article, see the article about [Classes] for
a further discussion.%>

It is worth pointing out that ClassTypes are themselves instances, and have instance methods, thus the need for

0 comments on commit b8a07c4

Please sign in to comment.