Add the ability to split Text on word boundaries (#1302)

This commit is contained in:
Ara Adkins 2020-11-20 13:29:34 +00:00 committed by GitHub
parent fbe1f4c439
commit e62f6796fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 399 additions and 43 deletions

View File

@ -2,6 +2,7 @@ from Base import all
from Builtins import Text, Prim_Text_Helpers
import Base.Data.Text.Split_Kind
import Base.Meta
from Builtins export Text
@ -10,8 +11,8 @@ polyglot java import org.enso.base.Text_Utils
## Computes the number of characters in the text.
A character is defined as an Extended Grapheme Cluster, see
[Unicode Standard Annex #29](https://unicode.org/reports/tr29/).
A character is defined as an Extended Grapheme Cluster, see Unicode
[Standard Annex #29](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries).
This is the smallest unit that still has semantic meaning in most
text-processing applications.
@ -29,8 +30,8 @@ Text.length =
## Applies `function` to each character in `this`.
A character is defined as an Extended Grapheme Cluster, see
[Unicode Standard Annex #29](https://unicode.org/reports/tr29/).
A character is defined as an Extended Grapheme Cluster, see Unicode
[Standard Annex #29](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries).
This is the smallest unit that still has semantic meaning in most
text-processing applications.
@ -51,8 +52,8 @@ Text.each function =
## Returns a vector containing all characters in the given text.
A character is defined as an Extended Grapheme Cluster, see
[Unicode Standard Annex #29](https://unicode.org/reports/tr29/).
A character is defined as an Extended Grapheme Cluster, see Unicode
[Standard Annex #29](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries).
This is the smallest unit that still has semantic meaning in most
text-processing applications.
@ -76,9 +77,45 @@ Text.split (separator = Split_Kind.Whitespace) =
result = case separator of
Split_Kind.Whitespace -> Text_Utils.split_on_whitespace [this]
Split_Kind.Lines -> Text_Utils.split_on_lines [this]
Split_Kind.Words -> this.words
Text -> Text_Utils.split_by_literal [this, separator]
Vector.from_polyglot_array result
## Returns a vector containing all words in the given text.
A word is defined based on the definition of Word Boundaries in the Unicode
[Standard Annex #29](https://www.unicode.org/reports/tr29/#Word_Boundaries),
supplemented by language-specific dictionaries for Chinese, Japanese, Thai,
and Khmer.
By default, the function doesn't include the whitespace between words, but
this can be enabled.
> Example
Getting the words in the sentence "I have not one, but two cats."
"I have not one, but two cats.".words
Text.words : Boolean -> Vector.Vector
Text.words keep_whitespace=False =
iterator = BreakIterator.getWordInstance []
iterator.setText [this]
bldr = Vector.new_builder
fst = iterator.first []
nxt = iterator.next []
build prev nxt = if nxt == -1 then Nothing else
word = Text_Utils.substring [this, prev, nxt]
word_not_whitespace = (Text_Utils.is_whitespace [word]).not
if word_not_whitespace then bldr.append word else
if keep_whitespace then
bldr.append word
next_nxt = iterator.next []
@Tail_Call build nxt next_nxt
build fst nxt
bldr.to_vector
## Checks whether `this` is equal to `that`.
The definition of equality includes Unicode canonicalization. I.e. two texts
@ -92,7 +129,8 @@ Text.split (separator = Split_Kind.Whitespace) =
`e` followed by U+0301, COMBINING ACUTE ACCENT). Therefore:
('é' == 'e\u0301') == True
Text.== : Any -> Boolean
Text.== that = Text_Utils.equals [this, that]
Text.== that = if Meta.is_same_object this Text then Meta.is_same_object that Text else
Text_Utils.equals [this, that]
## Checks whether `this` is equal to `that`, ignoring case considerations.

View File

@ -6,6 +6,8 @@ type Split_Kind
type Whitespace
## Split into lines.
type Lines
## Split into words.
type Words
## Split on a literal.
Text

View File

@ -73,6 +73,43 @@ Polyglot.get_language =
lang_str = Builtins.Meta.get_polyglot_language
if lang_str == "java" then Java else Unknown
## Checks if `this` is an instance of `typ`.
Any.is_a : Any -> Boolean
Any.is_a typ = here.is_a this typ
## Checks if `this` is an instance of `typ`.
Any.is_an : Any -> Boolean
Any.is_an typ = here.is_a this typ
## Checks if `value` is an instance of `typ`.
is_a : Any -> Any -> Boolean
is_a value typ = if typ == Any then True else
case value of
Array -> typ == Array
Boolean -> if typ == Boolean then True else value == typ
Text -> typ == Text
Number -> if typ == Number then True else case value of
Integer -> typ == Integer
Decimal -> typ == Decimal
Base.Polyglot -> typ == Base.Polyglot
_ ->
meta_val = here.meta value
case meta_val of
Atom _ -> meta_val.constructor == typ
Constructor _ ->
meta_typ = here.meta typ
case meta_typ of
Atom _ -> meta_val.constructor == meta_typ.constructor
Constructor _ -> meta_val.constructor == meta_typ
_ -> False
Error _ -> typ == Error
Unresolved_Symbol _ -> typ == Unresolved_Symbol
_ -> False
## Checks if `value` is an instance of `typ`.
is_an : Any -> Any -> Boolean
is_an value typ = here.is_a value typ
## Returns a meta-representation of a given runtime entity.
meta : Any -> Meta
meta value = if Builtins.Meta.is_atom value then Atom value else

View File

@ -54,15 +54,8 @@ expect_fail_with ~action matcher =
case res of
_ -> here.fail ("Expected a " + matcher.to_text + " to be thrown, but the action succeeded.")
err = res.catch x->x
case Meta.meta matcher of
Meta.Atom _ -> if err == matcher then Success else
here.fail ("Expected a " + matcher.to_text + "be thrown, but found " + err.to_text + ".")
Meta.Constructor _ ->
meta_err = Meta.meta err
case meta_err of
Meta.Atom _ -> if meta_err.constructor == matcher then Success else
here.fail ("Unexpected error " + err.to_text + " thrown.")
_ -> here.fail ("Incorrect instance " + err.to_text + ".")
if err.is_a matcher then Nothing else
here.fail ("Unexpected error " + err.to_text + " thrown.")
## Asserts that `this` value is equal to the expected value.
Any.should_equal that = case this == that of

View File

@ -12,6 +12,7 @@ import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.node.callable.function.CreateFunctionNode;
import org.enso.interpreter.runtime.builtin.Bool;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;

View File

@ -0,0 +1,56 @@
package org.enso.interpreter.node.controlflow;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.builtin.Bool;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
@NodeInfo(shortName = "BooleanConsMatch", description = "Match using the Boolean constructor.")
public abstract class BooleanConstructorBranchNode extends BranchNode {
private final AtomConstructor boolCons;
private final AtomConstructor trueCons;
private final AtomConstructor falseCons;
private final ConditionProfile profile = ConditionProfile.createCountingProfile();
BooleanConstructorBranchNode(Bool bool, RootCallTarget branch) {
super(branch);
this.boolCons = bool.getBool();
this.trueCons = bool.getTrue();
this.falseCons = bool.getFalse();
}
/**
* Creates a new node for handling matching using the Boolean constructor in a case expression.
*
* @param bool the boolean container
* @param branch the expression to be executed if (@code matcher} matches
* @return a node for matching in a case expression
*/
public static BooleanConstructorBranchNode build(Bool bool, RootCallTarget branch) {
return BooleanConstructorBranchNodeGen.create(bool, branch);
}
@Specialization
void doConstructor(VirtualFrame frame, Object state, Atom target) {
var shouldMatch =
(target.getConstructor() == boolCons)
|| (target.getConstructor() == falseCons)
|| (target.getConstructor() == trueCons);
if (profile.profile(shouldMatch)) {
accept(frame, state, new Object[0]);
}
}
@Specialization
void doBool(VirtualFrame frame, Object state, boolean target) {
accept(frame, state, new Object[0]);
}
@Fallback
void doFallback(VirtualFrame frame, Object state, Object target) {}
}

View File

@ -6,6 +6,7 @@ import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.builtin.Number;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@ -13,27 +14,35 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger;
@NodeInfo(shortName = "IntegerMatch", description = "Allows matching on the Integer type.")
public abstract class IntegerBranchNode extends BranchNode {
private final AtomConstructor integer;
private final AtomConstructor smallInteger;
private final AtomConstructor bigInteger;
private final ConditionProfile profile = ConditionProfile.createCountingProfile();
public IntegerBranchNode(AtomConstructor integer, RootCallTarget branch) {
public IntegerBranchNode(Number number, RootCallTarget branch) {
super(branch);
this.integer = integer;
this.integer = number.getInteger();
this.smallInteger = number.getSmallInteger();
this.bigInteger = number.getBigInteger();
}
/**
* Create a new node to handle matching with the Integer constructor.
*
* @param integer the constructor used for matching
* @param number the constructor used for matching
* @param branch the code to execute
* @return an integer branch node
*/
public static IntegerBranchNode build(AtomConstructor integer, RootCallTarget branch) {
return IntegerBranchNodeGen.create(integer, branch);
public static IntegerBranchNode build(Number number, RootCallTarget branch) {
return IntegerBranchNodeGen.create(number, branch);
}
@Specialization
void doConstructor(VirtualFrame frame, Object state, Atom target) {
if (profile.profile(integer == target.getConstructor())) {
var shouldMatch =
(integer == target.getConstructor())
|| (smallInteger == target.getConstructor())
|| (bigInteger == target.getConstructor());
if (profile.profile(shouldMatch)){
accept(frame, state, target.getFields());
}
}

View File

@ -6,6 +6,7 @@ import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.builtin.Number;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@ -13,11 +14,19 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger;
@NodeInfo(shortName = "NumberMatch", description = "Allows matching on the Number type.")
public abstract class NumberBranchNode extends BranchNode {
private final AtomConstructor number;
private final AtomConstructor integer;
private final AtomConstructor bigInteger;
private final AtomConstructor smallInteger;
private final AtomConstructor decimal;
private final ConditionProfile profile = ConditionProfile.createCountingProfile();
NumberBranchNode(AtomConstructor number, RootCallTarget branch) {
NumberBranchNode(Number number, RootCallTarget branch) {
super(branch);
this.number = number;
this.number = number.getNumber();
this.integer = number.getInteger();
this.bigInteger = number.getBigInteger();
this.smallInteger = number.getSmallInteger();
this.decimal = number.getDecimal();
}
/**
@ -27,13 +36,19 @@ public abstract class NumberBranchNode extends BranchNode {
* @param branch the code to execute
* @return an integer branch node
*/
public static NumberBranchNode build(AtomConstructor number, RootCallTarget branch) {
public static NumberBranchNode build(Number number, RootCallTarget branch) {
return NumberBranchNodeGen.create(number, branch);
}
@Specialization
void doConstructor(VirtualFrame frame, Object state, Atom target) {
if (profile.profile(number == target.getConstructor())) {
var shouldMatch =
(target.getConstructor() == number)
|| (target.getConstructor() == integer)
|| (target.getConstructor() == bigInteger)
|| (target.getConstructor() == smallInteger)
|| (target.getConstructor() == decimal);
if (profile.profile(shouldMatch)) {
accept(frame, state, target.getFields());
}
}

View File

@ -1,11 +1,48 @@
package org.enso.interpreter.node.expression.builtin.bool;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
@BuiltinMethod(type = "Boolean", name = "==", description = "Computes the equality of two booleans")
public class EqualsNode extends Node {
boolean execute(boolean _this, boolean that) {
public abstract class EqualsNode extends Node {
abstract boolean execute(Object _this, Object that);
static EqualsNode build() {
return EqualsNodeGen.create();
}
@Specialization
boolean doBoolean(boolean _this, boolean that) {
return _this == that;
}
@Specialization
boolean doAtom(
Atom _this,
Atom that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getBooleanConstructor(ctxRef)") AtomConstructor boolCons
) {
var thisCons = _this.getConstructor();
var thatCons = that.getConstructor();
return (thatCons == boolCons) && (thisCons == thatCons);
}
@Fallback
boolean doOther(Object _this, Object that) {
return false;
}
AtomConstructor getBooleanConstructor(ContextReference<Context> ctxRef) {
return ctxRef.get().getBuiltins().bool().getBool();
}
}

View File

@ -1,16 +1,24 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import cats.derived.IterState.Cont;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "==", description = "Big integer equality.")
public abstract class EqualsNode extends Node {
abstract boolean execute(EnsoBigInteger _this, Object that);
abstract boolean execute(Object _this, Object that);
static EqualsNode build() {
return EqualsNodeGen.create();
@ -26,8 +34,23 @@ public abstract class EqualsNode extends Node {
return BigIntegerOps.toDouble(_this.getValue()) == that;
}
@Specialization
boolean doAtom(
Atom _this,
Atom that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getBigIntegerConstructor(ctxRef)") AtomConstructor bigIntCons) {
var thisCons = _this.getConstructor();
var thatCons = that.getConstructor();
return (thatCons == bigIntCons) && (thisCons == thatCons);
}
@Fallback
boolean doOther(EnsoBigInteger _this, Object that) {
boolean doOther(Object _this, Object that) {
return false;
}
AtomConstructor getBigIntegerConstructor(ContextReference<Context> ctxRef) {
return ctxRef.get().getBuiltins().number().getBigInteger();
}
}

View File

@ -1,16 +1,22 @@
package org.enso.interpreter.node.expression.builtin.number.decimal;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Decimal", name = "==", description = "Equality on numbers.")
public abstract class EqualsNode extends Node {
abstract boolean execute(double _this, Object that);
abstract boolean execute(Object _this, Object that);
static EqualsNode build() {
return EqualsNodeGen.create();
@ -31,8 +37,23 @@ public abstract class EqualsNode extends Node {
return _this == BigIntegerOps.toDouble(that.getValue());
}
@Specialization
boolean doAtom(
Atom _this,
Atom that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getDecimalConstructor(ctxRef)") AtomConstructor decimalCons) {
var thatCons = that.getConstructor();
var thisCons = _this.getConstructor();
return (thatCons == decimalCons) && (thisCons == thatCons);
}
@Fallback
boolean doOther(double _this, Object that) {
boolean doOther(Object _this, Object that) {
return false;
}
AtomConstructor getDecimalConstructor(ContextReference<Context> ctxRef) {
return ctxRef.get().getBuiltins().number().getDecimal();
}
}

View File

@ -1,14 +1,21 @@
package org.enso.interpreter.node.expression.builtin.number.smallInteger;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
@BuiltinMethod(type = "Small_Integer", name = "==", description = "Equality on numbers.")
public abstract class EqualsNode extends Node {
abstract boolean execute(long _this, Object that);
abstract boolean execute(Object _this, Object that);
static EqualsNode build() {
return EqualsNodeGen.create();
@ -24,8 +31,23 @@ public abstract class EqualsNode extends Node {
return (double) _this == that;
}
@Specialization
boolean doAtom(
Atom _this,
Atom that,
@CachedContext(Language.class) ContextReference<Context> ctxRef,
@Cached("getSmallIntegerConstructor(ctxRef)") AtomConstructor smallIntCons) {
var thisCons = _this.getConstructor();
var thatCons = that.getConstructor();
return (thatCons == smallIntCons) && (thisCons == thatCons);
}
@Fallback
boolean doOther(long _this, Object that) {
boolean doOther(Object _this, Object that) {
return false;
}
AtomConstructor getSmallIntegerConstructor(ContextReference<Context> ctxRef) {
return ctxRef.get().getBuiltins().number().getBigInteger();
}
}

View File

@ -701,11 +701,10 @@ class IrToTruffle(
)
runtimeConsOpt.map { atomCons =>
val any = context.getBuiltins.any
val array = context.getBuiltins.mutable.constructor
val bool = context.getBuiltins.bool
val decimal = context.getBuiltins.number.getDecimal
val integer = context.getBuiltins.number.getInteger
val number = context.getBuiltins.number.getNumber
val number = context.getBuiltins.number
val polyglot = context.getBuiltins.polyglot.getPolyglot
val text = context.getBuiltins.text
val branchNode: BranchNode =
@ -713,18 +712,22 @@ class IrToTruffle(
BooleanBranchNode.build(true, branchCodeNode.getCallTarget)
} else if (atomCons == bool.getFalse) {
BooleanBranchNode.build(false, branchCodeNode.getCallTarget)
} else if (atomCons == bool.getBool) {
BooleanConstructorBranchNode.build(bool, branchCodeNode.getCallTarget)
} else if (atomCons == text.getText) {
TextBranchNode.build(text.getText, branchCodeNode.getCallTarget)
} else if (atomCons == integer) {
IntegerBranchNode.build(integer, branchCodeNode.getCallTarget)
} else if (atomCons == decimal) {
DecimalBranchNode.build(decimal, branchCodeNode.getCallTarget)
} else if (atomCons == number) {
} else if (atomCons == number.getInteger) {
IntegerBranchNode.build(number, branchCodeNode.getCallTarget)
} else if (atomCons == number.getDecimal) {
DecimalBranchNode.build(number.getDecimal, branchCodeNode.getCallTarget)
} else if (atomCons == number.getNumber) {
NumberBranchNode.build(number, branchCodeNode.getCallTarget)
} else if (atomCons == array) {
ArrayBranchNode.build(array, branchCodeNode.getCallTarget)
} else if (atomCons == polyglot) {
PolyglotBranchNode.build(polyglot, branchCodeNode.getCallTarget)
} else if (atomCons == any) {
CatchAllBranchNode.build(branchCodeNode.getCallTarget)
} else {
ConstructorBranchNode.build(
atomCons,

View File

@ -89,6 +89,17 @@ public class Text_Utils {
return vertical_space.split(str);
}
/**
* Checks if the provided string consists only of whitespace characters.
*
* @param str the string to check
* @return {@code true} if {@code str} is only whitespace, otherwise {@code false}
*/
public static boolean is_whitespace(String str) {
var matcher = whitespace.matcher(str);
return matcher.matches();
}
/**
* Checks whether two strings are equal up to Unicode canonicalization.
*

View File

@ -20,6 +20,11 @@ spec = describe "Text" <|
utf_8_whitespace_split = ["foo", "bar", "baz", "quux"]
utf_8_vertical = 'foo\n bar \v baz \r quux'
utf_8_vertical_split = ["foo", " bar ", " baz ", " quux"]
sentences = '''
I have a very long block of text, here. It goes on and on, containing
things like decimal points (1.0314e3) and other language scripts as well
건반(Korean).
sentence_words = ['I', 'have', 'a', 'very', 'long', 'block', 'of', 'text', ',', 'here', '.', 'It', 'goes', 'on', 'and', 'on', ',', 'containing', 'things', 'like', 'decimal', 'points', '(', '1.0314e3', ')', 'and', 'other', 'language', 'scripts', 'as', 'well', '건반', '(', 'Korean', ')', '.']
it "should allow naive length computation over grapheme clusters" <|
kshi.length . should_equal 1
facepalm.length . should_equal 1
@ -29,6 +34,8 @@ spec = describe "Text" <|
it "should split the text into grapheme clusters" <|
str = kshi + facepalm + accent_1 + accent_2
str.characters . should_equal [kshi, facepalm, accent_1, accent_2]
it "should be able to split the text into words" <|
sentences.words . should_equal sentence_words
it "should be able to split the text on UTF-8 whitespace" <|
utf_8_whitespace.split . should_equal utf_8_whitespace_split
it "should be able to split the text on UTF-8 newlines" <|

View File

@ -5,6 +5,16 @@ import Test
polyglot java import java.util.Random
spec = describe "Pattern Matches" <|
it "should be able to match on the Boolean type" <|
case Boolean of
Boolean -> Nothing
_ -> Test.fail "Expected the Boolean constructor to match."
case True of
Boolean -> Nothing
_ -> Test.fail "Expected the True constructor to match."
case False of
Boolean -> Nothing
_ -> Test.fail "Expected the False constructor to match."
it "should be able to match on the Integer type" <|
case 1 of
Integer -> Nothing
@ -15,6 +25,12 @@ spec = describe "Pattern Matches" <|
case Integer of
Integer -> Nothing
_ -> Test.fail "Expected the Integer constructor to match."
case Big_Integer of
Integer -> Nothing
_ -> Test.fail "Expected the Big_Integer constructor to match."
case Small_Integer of
Integer -> Nothing
_ -> Test.fail "Expected the Small_Integer constructor to match."
it "should be able to match on the Decimal type" <|
case 1.7 of
Decimal -> Nothing
@ -35,6 +51,18 @@ spec = describe "Pattern Matches" <|
case Number of
Number -> Nothing
_ -> Test.fail "Expected the Number constructor to match."
case Small_Integer of
Number -> Nothing
_ -> Test.fail "Expected the Small_Integer constructor to match."
case Big_Integer of
Number -> Nothing
_ -> Test.fail "Expected the Big_Integer constructor to match."
case Integer of
Number -> Nothing
_ -> Test.fail "Expected the Integer constructor to match."
case Decimal of
Number -> Nothing
_ -> Test.fail "Expected the Decimal constructor to match."
it "should be able to match on the Text type" <|
case "foo" of
Text -> Nothing
@ -57,3 +85,18 @@ spec = describe "Pattern Matches" <|
case Polyglot of
Polyglot -> Nothing
_ -> Test.fail "Expected the Polyglot constructor to match."
it "should be able to match on the Any type" <|
value_1 = 1.23143
value_2 = "foo bar"
case value_1 of
Any -> Nothing
_ -> Test.fail "Expected any value to match Any."
case value_2 of
Any -> Nothing
_ -> Test.fail "Expected any value to match Any."
case Polyglot of
Any -> Nothing
_ -> Test.fail "Expect any constructor to match Any."
case Any of
Any -> Nothing
_ -> Test.fail "Expected the Any constructor to match."

View File

@ -1,10 +1,14 @@
from Base import all
import Test
polyglot java import java.util.Random
type My_Type foo bar baz
My_Type.my_method = this.foo + this.bar + this.baz
type Test_Type x
spec = describe "Meta-Value Manipulation" <|
it "should allow manipulating unresolved symbols" <|
sym = does_not_exist
@ -22,4 +26,38 @@ spec = describe "Meta-Value Manipulation" <|
it "should correctly return representations of different classes of objects" <|
Meta.meta 1 . should equal (Meta.Primitive 1)
Meta.meta "foo" . should equal (Meta.Primitive "foo")
it "should allow checking if a value is of a certain type" <|
1.is_an Any . should_be_true
1.2.is_an Any . should_be_true
(My_Type 1 "foo" Nothing).is_an Any . should_be_true
Array.is_an Array . should_be_true
[].to_array.is_an Array . should_be_true
[].to_array.is_a Decimal . should_be_false
Boolean.is_a Boolean . should_be_true
True.is_a Boolean . should_be_true
False.is_a Boolean . should_be_true
True.is_an Integer . should_be_false
"".is_a Text . should_be_true
"".is_a Decimal . should_be_false
1.is_an Array . should_be_false
1.is_an Integer . should_be_true
1.is_a Number . should_be_true
1.is_a Decimal . should_be_false
1.0.is_a Number . should_be_true
1.0.is_a Decimal . should_be_true
1.0.is_an Integer . should_be_false
1.0.is_a Text . should_be_false
random_gen = Random.new [].to_array
Meta.is_a random_gen Polyglot . should_be_true
Meta.is_an random_gen Integer . should_be_false
(My_Type 1 "foo" Nothing).is_a My_Type . should_be_true
(My_Type 1 "foo" Nothing).is_a Test_Type . should_be_false
(My_Type 1 "foo" Nothing).is_a Number . should_be_false