mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
expressionUpdates do not contain Method Pointer of operators (#7659)
close #7520 Changelog: - update: SectionsToBinOp compiler pass produces function application for left sections - refactor: simplify the registration of builtin methods
This commit is contained in:
parent
5eb4944ccf
commit
bbf96f0d16
@ -15,12 +15,14 @@ import com.oracle.truffle.api.nodes.Node;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import org.enso.interpreter.instrument.profiling.ExecutionTime;
|
import org.enso.interpreter.instrument.profiling.ExecutionTime;
|
||||||
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
|
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
|
||||||
|
import org.enso.interpreter.node.ClosureRootNode;
|
||||||
import org.enso.interpreter.node.ExpressionNode;
|
import org.enso.interpreter.node.ExpressionNode;
|
||||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode;
|
import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode;
|
||||||
|
import org.enso.interpreter.runtime.Module;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
import org.enso.interpreter.runtime.control.TailCallException;
|
import org.enso.interpreter.runtime.control.TailCallException;
|
||||||
@ -29,13 +31,9 @@ import org.enso.interpreter.runtime.error.DataflowError;
|
|||||||
import org.enso.interpreter.runtime.error.PanicException;
|
import org.enso.interpreter.runtime.error.PanicException;
|
||||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||||
import org.enso.interpreter.runtime.state.State;
|
import org.enso.interpreter.runtime.state.State;
|
||||||
|
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
||||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||||
import org.enso.interpreter.runtime.type.Constants;
|
import org.enso.interpreter.runtime.type.Constants;
|
||||||
import org.enso.interpreter.runtime.Module;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import org.enso.interpreter.node.ClosureRootNode;
|
|
||||||
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
|
||||||
|
|
||||||
/** An instrument for getting values from AST-identified expressions. */
|
/** An instrument for getting values from AST-identified expressions. */
|
||||||
@TruffleInstrument.Registration(
|
@TruffleInstrument.Registration(
|
||||||
@ -56,7 +54,7 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
|||||||
this.env = env;
|
this.env = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Factory for creating new id event nodes **/
|
/** Factory for creating new id event nodes. */
|
||||||
private static class IdEventNodeFactory implements ExecutionEventNodeFactory {
|
private static class IdEventNodeFactory implements ExecutionEventNodeFactory {
|
||||||
|
|
||||||
private final CallTarget entryCallTarget;
|
private final CallTarget entryCallTarget;
|
||||||
|
@ -806,6 +806,143 @@ class RuntimeServerTest
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "send method pointer updates of builtin operators" in {
|
||||||
|
val contextId = UUID.randomUUID()
|
||||||
|
val requestId = UUID.randomUUID()
|
||||||
|
val moduleName = "Enso_Test.Test.Main"
|
||||||
|
|
||||||
|
val metadata = new Metadata
|
||||||
|
val id_x_1 = metadata.addItem(48, 5, "aa")
|
||||||
|
|
||||||
|
val code =
|
||||||
|
"""from Standard.Base import all
|
||||||
|
|
|
||||||
|
|main =
|
||||||
|
| x_1 = 3 ^ 4
|
||||||
|
| x_1
|
||||||
|
|""".stripMargin.linesIterator.mkString("\n")
|
||||||
|
val contents = metadata.appendToCode(code)
|
||||||
|
val mainFile = context.writeMain(contents)
|
||||||
|
|
||||||
|
// create context
|
||||||
|
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||||
|
context.receive shouldEqual Some(
|
||||||
|
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||||
|
)
|
||||||
|
|
||||||
|
// open file
|
||||||
|
context.send(
|
||||||
|
Api.Request(Api.OpenFileNotification(mainFile, contents))
|
||||||
|
)
|
||||||
|
context.receiveNone shouldEqual None
|
||||||
|
|
||||||
|
// push main
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
requestId,
|
||||||
|
Api.PushContextRequest(
|
||||||
|
contextId,
|
||||||
|
Api.StackItem.ExplicitCall(
|
||||||
|
Api.MethodPointer(moduleName, moduleName, "main"),
|
||||||
|
None,
|
||||||
|
Vector()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
context.receiveNIgnoreStdLib(4) should contain theSameElementsAs Seq(
|
||||||
|
Api.Response(Api.BackgroundJobsStartedNotification()),
|
||||||
|
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||||
|
TestMessages.update(
|
||||||
|
contextId,
|
||||||
|
id_x_1,
|
||||||
|
ConstantsGen.INTEGER,
|
||||||
|
Api.MethodCall(
|
||||||
|
Api.MethodPointer(
|
||||||
|
"Standard.Base.Data.Numbers",
|
||||||
|
"Standard.Base.Data.Numbers.Integer",
|
||||||
|
"^"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "send method pointer updates of partially applied builtin operators" in {
|
||||||
|
val contextId = UUID.randomUUID()
|
||||||
|
val requestId = UUID.randomUUID()
|
||||||
|
val moduleName = "Enso_Test.Test.Main"
|
||||||
|
|
||||||
|
val metadata = new Metadata
|
||||||
|
val id_x_1 = metadata.addItem(48, 5, "aa")
|
||||||
|
val id_x_2 = metadata.addItem(64, 7, "ab")
|
||||||
|
|
||||||
|
val code =
|
||||||
|
"""from Standard.Base import all
|
||||||
|
|
|
||||||
|
|main =
|
||||||
|
| x_1 = "4" +
|
||||||
|
| x_2 = x_1 "2"
|
||||||
|
| x_2
|
||||||
|
|""".stripMargin.linesIterator.mkString("\n")
|
||||||
|
val contents = metadata.appendToCode(code)
|
||||||
|
val mainFile = context.writeMain(contents)
|
||||||
|
|
||||||
|
// create context
|
||||||
|
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||||
|
context.receive shouldEqual Some(
|
||||||
|
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||||
|
)
|
||||||
|
|
||||||
|
// open file
|
||||||
|
context.send(
|
||||||
|
Api.Request(Api.OpenFileNotification(mainFile, contents))
|
||||||
|
)
|
||||||
|
context.receiveNone shouldEqual None
|
||||||
|
|
||||||
|
// push main
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
requestId,
|
||||||
|
Api.PushContextRequest(
|
||||||
|
contextId,
|
||||||
|
Api.StackItem.ExplicitCall(
|
||||||
|
Api.MethodPointer(moduleName, moduleName, "main"),
|
||||||
|
None,
|
||||||
|
Vector()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val textPlusMethodPointer = Api.MethodPointer(
|
||||||
|
"Standard.Base.Data.Text",
|
||||||
|
"Standard.Base.Data.Text.Text",
|
||||||
|
"+"
|
||||||
|
)
|
||||||
|
context.receiveNIgnoreStdLib(5) should contain theSameElementsAs Seq(
|
||||||
|
Api.Response(Api.BackgroundJobsStartedNotification()),
|
||||||
|
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||||
|
TestMessages.update(
|
||||||
|
contextId,
|
||||||
|
id_x_1,
|
||||||
|
ConstantsGen.FUNCTION,
|
||||||
|
methodCall = Some(Api.MethodCall(textPlusMethodPointer, Vector(1))),
|
||||||
|
payload = Api.ExpressionUpdate.Payload.Value(
|
||||||
|
None,
|
||||||
|
Some(Api.FunctionSchema(textPlusMethodPointer, Vector(1)))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
TestMessages.update(
|
||||||
|
contextId,
|
||||||
|
id_x_2,
|
||||||
|
ConstantsGen.TEXT,
|
||||||
|
Api.MethodCall(textPlusMethodPointer)
|
||||||
|
),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
it should "send method pointer updates of partially applied constructors" in {
|
it should "send method pointer updates of partially applied constructors" in {
|
||||||
val contextId = UUID.randomUUID()
|
val contextId = UUID.randomUUID()
|
||||||
val requestId = UUID.randomUUID()
|
val requestId = UUID.randomUUID()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.enso.interpreter.runtime.builtin;
|
package org.enso.interpreter.runtime.builtin;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -16,7 +17,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.enso.compiler.Passes;
|
import org.enso.compiler.Passes;
|
||||||
import org.enso.compiler.context.CompilerContext;
|
import org.enso.compiler.context.CompilerContext;
|
||||||
import org.enso.compiler.context.FreshNameSupply;
|
import org.enso.compiler.context.FreshNameSupply;
|
||||||
@ -47,16 +47,11 @@ import org.enso.interpreter.node.expression.builtin.runtime.Context;
|
|||||||
import org.enso.interpreter.node.expression.builtin.text.Text;
|
import org.enso.interpreter.node.expression.builtin.text.Text;
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
import org.enso.interpreter.runtime.Module;
|
import org.enso.interpreter.runtime.Module;
|
||||||
import org.enso.interpreter.runtime.builtin.Error;
|
|
||||||
import org.enso.interpreter.runtime.builtin.Number;
|
|
||||||
import org.enso.interpreter.runtime.builtin.System;
|
|
||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
import org.enso.interpreter.runtime.data.Type;
|
import org.enso.interpreter.runtime.data.Type;
|
||||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||||
import org.enso.pkg.QualifiedName;
|
import org.enso.pkg.QualifiedName;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
|
||||||
|
|
||||||
/** Container class for static predefined atoms, methods, and their containing scope. */
|
/** Container class for static predefined atoms, methods, and their containing scope. */
|
||||||
public final class Builtins {
|
public final class Builtins {
|
||||||
|
|
||||||
@ -187,12 +182,11 @@ public final class Builtins {
|
|||||||
// Register a builtin method iff it is marked as auto-register.
|
// Register a builtin method iff it is marked as auto-register.
|
||||||
// Methods can only register under a type or, if we deal with a static method, it's eigen-type.
|
// Methods can only register under a type or, if we deal with a static method, it's eigen-type.
|
||||||
// Such builtins are available on certain types without importing the whole stdlib, e.g. Any or Number.
|
// Such builtins are available on certain types without importing the whole stdlib, e.g. Any or Number.
|
||||||
methods.entrySet().stream().forEach(entry -> {
|
methods.forEach((key, value) -> {
|
||||||
Type tpe = entry.getValue().isAutoRegister ? (!entry.getValue().isStatic() ? type : type.getEigentype()) : null;
|
Type tpe = value.isAutoRegister ? (!value.isStatic() ? type : type.getEigentype()) : null;
|
||||||
if (tpe != null) {
|
if (tpe != null) {
|
||||||
LoadedBuiltinMethod value = entry.getValue();
|
|
||||||
Optional<BuiltinFunction> fun = value.toFunction(language, false);
|
Optional<BuiltinFunction> fun = value.toFunction(language, false);
|
||||||
fun.ifPresent(f -> scope.registerMethod(tpe, entry.getKey(), f.getFunction()));
|
fun.ifPresent(f -> scope.registerMethod(tpe, key, f.getFunction()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -235,7 +229,6 @@ public final class Builtins {
|
|||||||
* builtin type per row. The format of the row is as follows: <Enso name of the builtin
|
* builtin type per row. The format of the row is as follows: <Enso name of the builtin
|
||||||
* type>:<Name of the class representing it>:[<field1>,<field2>,...] where the last column gives a
|
* type>:<Name of the class representing it>:[<field1>,<field2>,...] where the last column gives a
|
||||||
* list of optional type's fields.
|
* list of optional type's fields.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
private static List<Constructor<? extends Builtin>> readBuiltinTypes() {
|
private static List<Constructor<? extends Builtin>> readBuiltinTypes() {
|
||||||
ClassLoader classLoader = Builtins.class.getClassLoader();
|
ClassLoader classLoader = Builtins.class.getClassLoader();
|
||||||
|
@ -95,8 +95,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
* @param section the section to desugar
|
* @param section the section to desugar
|
||||||
* @return the result of desugaring `section`
|
* @return the result of desugaring `section`
|
||||||
*/
|
*/
|
||||||
//noinspection DuplicatedCode
|
private def desugarSections(
|
||||||
def desugarSections(
|
|
||||||
section: Section,
|
section: Section,
|
||||||
freshNameSupply: FreshNameSupply
|
freshNameSupply: FreshNameSupply
|
||||||
): Expression = {
|
): Expression = {
|
||||||
@ -146,21 +145,16 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
)
|
)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
val opCall = Application.Prefix(
|
Application.Prefix(
|
||||||
function = op,
|
function = op,
|
||||||
arguments = List(arg, rightCallArg),
|
arguments = List(arg),
|
||||||
hasDefaultsSuspended = false,
|
hasDefaultsSuspended = false,
|
||||||
location = None,
|
location = loc,
|
||||||
passData,
|
passData,
|
||||||
diagnostics
|
diagnostics
|
||||||
)
|
)
|
||||||
|
|
||||||
Function.Lambda(
|
|
||||||
List(rightDefArg),
|
|
||||||
opCall,
|
|
||||||
loc
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case Section.Sides(op, loc, passData, diagnostics) =>
|
case Section.Sides(op, loc, passData, diagnostics) =>
|
||||||
val leftArgName = freshNameSupply.newName()
|
val leftArgName = freshNameSupply.newName()
|
||||||
val leftCallArg =
|
val leftCallArg =
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package org.enso.compiler;
|
package org.enso.compiler;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import com.oracle.truffle.api.source.Source;
|
import com.oracle.truffle.api.source.Source;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -10,8 +13,6 @@ import org.enso.compiler.core.EnsoParser;
|
|||||||
import org.enso.compiler.core.IR;
|
import org.enso.compiler.core.IR;
|
||||||
import org.enso.compiler.core.ir.Module;
|
import org.enso.compiler.core.ir.Module;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
public abstract class CompilerTest {
|
public abstract class CompilerTest {
|
||||||
|
@ -7,6 +7,7 @@ import org.enso.compiler.core.ir.{
|
|||||||
DefinitionArgument,
|
DefinitionArgument,
|
||||||
Expression,
|
Expression,
|
||||||
Function,
|
Function,
|
||||||
|
Literal,
|
||||||
Name
|
Name
|
||||||
}
|
}
|
||||||
import org.enso.compiler.core.ir.expression.Application
|
import org.enso.compiler.core.ir.expression.Application
|
||||||
@ -63,26 +64,20 @@ class SectionsToBinOpTest extends CompilerTest {
|
|||||||
|(1 +)
|
|(1 +)
|
||||||
|""".stripMargin.preprocessExpression.get.desugar
|
|""".stripMargin.preprocessExpression.get.desugar
|
||||||
|
|
||||||
ir shouldBe an[Function.Lambda]
|
ir shouldBe an[Application.Prefix]
|
||||||
ir.location shouldBe defined
|
ir.location shouldBe defined
|
||||||
|
|
||||||
val irLam = ir.asInstanceOf[Function.Lambda]
|
val irApp = ir.asInstanceOf[Application.Prefix]
|
||||||
irLam.arguments.length shouldEqual 1
|
irApp.arguments.length shouldEqual 1
|
||||||
|
irApp.arguments.head shouldBe an[CallArgument.Specified]
|
||||||
|
|
||||||
val lamArgName =
|
val irAppArgument =
|
||||||
irLam.arguments.head.asInstanceOf[DefinitionArgument.Specified].name
|
irApp.arguments.head.asInstanceOf[CallArgument.Specified]
|
||||||
|
irAppArgument.value shouldBe an[Literal.Number]
|
||||||
|
|
||||||
val lamBody = irLam.body.asInstanceOf[Application.Prefix]
|
irApp.function shouldBe an[Name.Literal]
|
||||||
lamBody.arguments.length shouldEqual 2
|
val irAppFunction = irApp.function.asInstanceOf[Name.Literal]
|
||||||
val lamBodyFirstArg =
|
irAppFunction.name shouldEqual "+"
|
||||||
lamBody
|
|
||||||
.arguments(1)
|
|
||||||
.asInstanceOf[CallArgument.Specified]
|
|
||||||
.value
|
|
||||||
.asInstanceOf[Name.Literal]
|
|
||||||
|
|
||||||
lamBodyFirstArg.name shouldEqual lamArgName.name
|
|
||||||
lamBodyFirstArg.getId should not equal lamArgName.getId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"work for sides sections" in {
|
"work for sides sections" in {
|
||||||
@ -94,8 +89,7 @@ class SectionsToBinOpTest extends CompilerTest {
|
|||||||
|""".stripMargin.preprocessExpression.get.desugar
|
|""".stripMargin.preprocessExpression.get.desugar
|
||||||
|
|
||||||
ir shouldBe an[Function.Lambda]
|
ir shouldBe an[Function.Lambda]
|
||||||
// TODO[DB] Section.Sides location is not parsed
|
ir.location shouldBe defined
|
||||||
//ir.location shouldBe defined
|
|
||||||
|
|
||||||
val leftLam = ir.asInstanceOf[Function.Lambda]
|
val leftLam = ir.asInstanceOf[Function.Lambda]
|
||||||
leftLam.arguments.length shouldEqual 1
|
leftLam.arguments.length shouldEqual 1
|
||||||
@ -167,14 +161,12 @@ class SectionsToBinOpTest extends CompilerTest {
|
|||||||
.asInstanceOf[Function.Lambda]
|
.asInstanceOf[Function.Lambda]
|
||||||
|
|
||||||
ir.body
|
ir.body
|
||||||
.asInstanceOf[Function.Lambda]
|
.asInstanceOf[Application.Prefix]
|
||||||
.body shouldBe an[Application.Prefix]
|
.function shouldBe an[Name.Literal]
|
||||||
ir.body
|
ir.body
|
||||||
.asInstanceOf[Function.Lambda]
|
|
||||||
.body
|
|
||||||
.asInstanceOf[Application.Prefix]
|
.asInstanceOf[Application.Prefix]
|
||||||
.arguments
|
.arguments
|
||||||
.length shouldEqual 2
|
.length shouldEqual 1
|
||||||
}
|
}
|
||||||
|
|
||||||
"flip the arguments when a right section's argument is a blank" in {
|
"flip the arguments when a right section's argument is a blank" in {
|
||||||
|
@ -296,32 +296,32 @@ spec =
|
|||||||
pattern = Regex.compile "(?<건반>[a-z]+)"
|
pattern = Regex.compile "(?<건반>[a-z]+)"
|
||||||
pattern.replace "foo bar, baz" "[$<건반>]" . should_equal "[foo] [bar], [baz]"
|
pattern.replace "foo bar, baz" "[$<건반>]" . should_equal "[foo] [bar], [baz]"
|
||||||
|
|
||||||
Text.group "should correctly evaluate documentation examples" <|
|
Test.group "should correctly evaluate documentation examples" <|
|
||||||
Test.specify "example 1" <|
|
Test.specify "example 1" <|
|
||||||
pattern = Regex.compile 'aa'
|
pattern = Regex.compile 'aa'
|
||||||
pattern.replace 'aaa' 'b' . should_equal 'ba'
|
pattern.replace 'aaa' 'b' . should_equal 'ba'
|
||||||
|
|
||||||
Test.specify "example 2" <|
|
Test.specify "example 2" <|
|
||||||
pattern = Regex.compile '[lo]'
|
pattern = Regex.compile '[lo]'
|
||||||
pattern.replace 'Hello World!' '#' . should_equal 'He### W#r#d!'
|
pattern.replace 'Hello World!' '#' . should_equal 'He### W#r#d!'
|
||||||
|
|
||||||
Test.specify "example 3" <|
|
Test.specify "example 3" <|
|
||||||
pattern = Regex.compile 'l'
|
pattern = Regex.compile 'l'
|
||||||
pattern.replace 'Hello World!' '#' only_first=True . should_equal 'He#lo World!'
|
pattern.replace 'Hello World!' '#' only_first=True . should_equal 'He#lo World!'
|
||||||
|
|
||||||
Test.specify "example 4" <|
|
Test.specify "example 4" <|
|
||||||
pattern = Regex.compile '"(.*?)"'
|
pattern = Regex.compile '"(.*?)"'
|
||||||
pattern.replace '"abc" foo "bar" baz' '($1)' . should_equal '(abc) foo (bar) baz'
|
pattern.replace '"abc" foo "bar" baz' '($1)' . should_equal '(abc) foo (bar) baz'
|
||||||
|
|
||||||
Test.specify "example 5" <|
|
Test.specify "example 5" <|
|
||||||
pattern = Regex.compile "aa"
|
pattern = Regex.compile "aa"
|
||||||
input = "aa ab aa ac ad aa aa ax"
|
input = "aa ab aa ac ad aa aa ax"
|
||||||
match = pattern.replace input "xyz"
|
match = pattern.replace input "xyz"
|
||||||
match . should_equal "xyz ab xyz ac ad xyz xyz ax"
|
match . should_equal "xyz ab xyz ac ad xyz xyz ax"
|
||||||
|
|
||||||
Test.specify "example 6" <|
|
Test.specify "example 6" <|
|
||||||
pattern = Regex.compile "([a-z]+)"
|
pattern = Regex.compile "([a-z]+)"
|
||||||
pattern.replace "foo bar, baz" "[$1]" . should_equal "[foo] [bar], [baz]"
|
pattern.replace "foo bar, baz" "[$1]" . should_equal "[foo] [bar], [baz]"
|
||||||
|
|
||||||
Test.specify "`replace` with an empty pattern should be an error" <|
|
Test.specify "`replace` with an empty pattern should be an error" <|
|
||||||
pattern = Regex.compile ""
|
pattern = Regex.compile ""
|
||||||
|
@ -3,3 +3,4 @@ type Number
|
|||||||
|
|
||||||
@Builtin_Type
|
@Builtin_Type
|
||||||
type Integer
|
type Integer
|
||||||
|
^ self that = @Builtin_Method "Integer.^"
|
||||||
|
Loading…
Reference in New Issue
Block a user