From 6d211928a40953bbebbee574fa5e8928a3ea687b Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 25 Oct 2026 10:11:50 +0000 Subject: Parse function expressions --- .../ast/untyped/UntypedExpressionNode.java | 2 +- .../ast/untyped/UntypedFunctionExpressionNode.java | 60 ++++++++++++++++++++++ .../compiler/ast/untyped/UntypedParamNode.java | 38 ++++++++++++++ .../zwobble/hobgoblin/compiler/parser/Parser.java | 50 ++++++++++++++++++ .../hobgoblin/compiler/parser/TokenType.java | 2 + .../hobgoblin/compiler/parser/Tokenizer.java | 9 ++++ .../UntypedFunctionExpressionNodeMatcher.java | 53 +++++++++++++++++++ .../ast/untyped/UntypedParamNodeMatcher.java | 47 +++++++++++++++++ .../parser/ParserFunctionExpressionTests.java | 55 ++++++++++++++++++++ .../hobgoblin/compiler/parser/TokenizerTests.java | 22 ++++++++ 10 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNode.java create mode 100644 src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNode.java create mode 100644 src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNodeMatcher.java create mode 100644 src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNodeMatcher.java create mode 100644 src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserFunctionExpressionTests.java (limited to 'src') diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java index 11f6b67..a5c54a0 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java @@ -5,7 +5,7 @@ package org.zwobble.hobgoblin.compiler.ast.untyped; // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode imports // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode imports -public sealed interface UntypedExpressionNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode { +public sealed interface UntypedExpressionNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode { public interface Builder { public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode build(); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNode.java new file mode 100644 index 0000000..4e410f3 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNode.java @@ -0,0 +1,60 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode imports + +public record UntypedFunctionExpressionNode( + java.util.List params, + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(java.util.List.of(), org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.arbitrary().build(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + java.util.List params, + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withParams(java.util.List params) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder addParam(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode param) { + var params = new java.util.ArrayList(this.params); + params.add(param); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder addParam(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder param) { + var params = new java.util.ArrayList(this.params); + params.add(param.build()); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withBody(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withBody(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder body) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body.build(), source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNode.java new file mode 100644 index 0000000..068097f --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNode.java @@ -0,0 +1,38 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode imports + +public record UntypedParamNode( + String name, + org.zwobble.hobgoblin.compiler.sources.Source source +) { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder("", org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + String name, + org.zwobble.hobgoblin.compiler.sources.Source source + ) { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder withName(String name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder(name, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java index 976ab75..cdeeedd 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java @@ -457,6 +457,19 @@ public class Parser { return Optional.of(typeParams); } + // == Params == + + private static UntypedParamNode parseParam(TokenIterator tokens) { + var start = tokens.startPosition(); + + var name = parseIdentifier(tokens); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedParamNode(name, source); + } + // == Type-level expressions == static UntypedTypeLevelExpressionNode parseTypeLevelExpression( @@ -545,6 +558,43 @@ public class Parser { private static UntypedExpressionNode parsePrimaryExpression( TokenIterator tokens ) { + if (tokens.isNext(TokenType.SYMBOL_DOLLAR)) { + return parseFunctionExpression(tokens); + } else if (tokens.isNext(TokenType.IDENTIFIER)) { + return parseReference(tokens); + } else { + throw new UnsupportedOperationException("TODO"); + } + } + + private static UntypedFunctionExpressionNode parseFunctionExpression(TokenIterator tokens) { + var start = tokens.startPosition(); + + tokens.skip(TokenType.SYMBOL_DOLLAR); + tokens.skip(TokenType.SYMBOL_PAREN_OPEN); + + var params = parseMany( + () -> tokens.isNext(TokenType.SYMBOL_PAREN_CLOSE), + () -> parseParam(tokens), + () -> tokens.trySkip(TokenType.SYMBOL_COMMA) + ); + + tokens.skip(TokenType.SYMBOL_PAREN_CLOSE); + tokens.skip(TokenType.SYMBOL_FAT_ARROW); + + var body = parseTopLevelExpression(tokens); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedFunctionExpressionNode( + params, + body, + source + ); + } + + private static UntypedReferenceNode parseReference(TokenIterator tokens) { var start = tokens.startPosition(); var name = parseIdentifier(tokens); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java index 2a85072..1181fca 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java @@ -19,8 +19,10 @@ enum TokenType { SYMBOL_BRACE_CLOSE, SYMBOL_COLON, SYMBOL_COMMA, + SYMBOL_DOLLAR, SYMBOL_DOT, SYMBOL_DOT_DOT, + SYMBOL_FAT_ARROW, SYMBOL_FORWARD_SLASH, SYMBOL_PAREN_OPEN, SYMBOL_PAREN_CLOSE, diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java index 1a775b0..56c2123 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java @@ -154,6 +154,10 @@ class Tokenizer { tokenType = TokenType.SYMBOL_DOT_DOT; iterator.skip(); iterator.skip(); + } else if ("=>".contentEquals(iterator.peekSequence(2))) { + tokenType = TokenType.SYMBOL_FAT_ARROW; + iterator.skip(); + iterator.skip(); } else { switch (iterator.peek()) { case '{': @@ -176,6 +180,11 @@ class Tokenizer { iterator.skip(); break; + case '$': + tokenType = TokenType.SYMBOL_DOLLAR; + iterator.skip(); + break; + case '.': tokenType = TokenType.SYMBOL_DOT; iterator.skip(); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNodeMatcher.java new file mode 100644 index 0000000..71356e8 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNodeMatcher.java @@ -0,0 +1,53 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher imports + +public final class UntypedFunctionExpressionNodeMatcher implements org.zwobble.precisely.Matcher { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher isUntypedFunctionExpressionNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(java.util.List.of()); + } + + private final java.util.List> submatchers; + + private UntypedFunctionExpressionNodeMatcher( + java.util.List> submatchers + ) { + this.submatchers = submatchers; + } + + public org.zwobble.precisely.MatchResult match(java.lang.Object actual) { + return this.toMatcher().match(actual); + } + + public org.zwobble.precisely.TextTree describe() { + return this.toMatcher().describe(); + } + + private org.zwobble.precisely.Matcher toMatcher() { + return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher withParams(org.zwobble.precisely.Matcher> params) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("params", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode::params, (org.zwobble.precisely.Matcher) params)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher withBody(org.zwobble.precisely.Matcher body) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("body", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode::body, (org.zwobble.precisely.Matcher) body)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher withSource(org.zwobble.precisely.Matcher source) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNodeMatcher.java new file mode 100644 index 0000000..c140fb2 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNodeMatcher.java @@ -0,0 +1,47 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher imports + +public final class UntypedParamNodeMatcher implements org.zwobble.precisely.Matcher { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher isUntypedParamNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher(java.util.List.of()); + } + + private final java.util.List> submatchers; + + private UntypedParamNodeMatcher( + java.util.List> submatchers + ) { + this.submatchers = submatchers; + } + + public org.zwobble.precisely.MatchResult match(java.lang.Object actual) { + return this.toMatcher().match(actual); + } + + public org.zwobble.precisely.TextTree describe() { + return this.toMatcher().describe(); + } + + private org.zwobble.precisely.Matcher toMatcher() { + return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher withName(org.zwobble.precisely.Matcher name) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode::name, (org.zwobble.precisely.Matcher) name)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher withSource(org.zwobble.precisely.Matcher source) { + var submatchers = new java.util.ArrayList>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserFunctionExpressionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserFunctionExpressionTests.java new file mode 100644 index 0000000..99cf3c0 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserFunctionExpressionTests.java @@ -0,0 +1,55 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher.isUntypedFunctionExpressionNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher.isUntypedParamNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; +import static org.zwobble.precisely.Matchers.isSequence; + +public class ParserFunctionExpressionTests { + @Test + public void canParseFunctionExpressionWithNoParams() { + var source = "$() => x"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedFunctionExpressionNode() + .withParams(isSequence()) + .withBody(isUntypedReferenceNode().withName(equalTo("x"))) + ); + } + + @Test + public void canParseFunctionExpressionWithOneParam() { + var source = "$(a) => x"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedFunctionExpressionNode() + .withParams(isSequence( + isUntypedParamNode().withName(equalTo("a")) + )) + .withBody(isUntypedReferenceNode().withName(equalTo("x"))) + ); + } + @Test + public void canParseFunctionExpressionWithMultipleParams() { + var source = "$(a, b, c) => x"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedFunctionExpressionNode() + .withParams(isSequence( + isUntypedParamNode().withName(equalTo("a")), + isUntypedParamNode().withName(equalTo("b")), + isUntypedParamNode().withName(equalTo("c")) + )) + .withBody(isUntypedReferenceNode().withName(equalTo("x"))) + ); + } +} + diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java index 39aded5..d5b7347 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java @@ -172,6 +172,17 @@ public class TokenizerTests { )); } + @Test + public void symbolDollarIsTokenized() { + var sourceText = SourceText.fromString("", "$"); + + var tokens = Tokenizer.tokenize(sourceText); + + assertThat(tokens.tokens(), isSequence( + equalTo(new Token(TokenType.SYMBOL_DOLLAR, sourceRange(sourceText, 0, 1))) + )); + } + @Test public void symbolDotIsTokenized() { var sourceText = SourceText.fromString("", "."); @@ -194,6 +205,17 @@ public class TokenizerTests { )); } + @Test + public void symbolFatArrowIsTokenized() { + var sourceText = SourceText.fromString("", "=>"); + + var tokens = Tokenizer.tokenize(sourceText); + + assertThat(tokens.tokens(), isSequence( + equalTo(new Token(TokenType.SYMBOL_FAT_ARROW, sourceRange(sourceText, 0, 2))) + )); + } + @Test public void symbolForwardSlashIsTokenized() { var sourceText = SourceText.fromString("", "/"); -- cgit v1.2.3