feat(muscript): V3: enforce new line or semicolon between expressions
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-09-15 19:32:01 +02:00
parent 6ffd67f23f
commit 0ae082e7d0
Signed by: Johannes
GPG Key ID: E76429612C2929F4
4 changed files with 26 additions and 8 deletions

View File

@ -22,6 +22,8 @@ public class Lexer extends VersionedComponent {
public final String source;
public int start, current;
public boolean passedNewline = false;
@Deprecated
public Lexer(String source) {
this(MuScriptVersion.DEFAULT, source);
@ -51,6 +53,7 @@ public class Lexer extends VersionedComponent {
*/
public void next() {
start = current;
passedNewline = false;
if (isAtEnd()) {
createToken(Token.EOF);
@ -111,7 +114,10 @@ public class Lexer extends VersionedComponent {
case '{' -> createToken(Token.LeftBrace);
case '}' -> createToken(Token.RightBrace);
case ';' -> createToken(Token.Semicolon);
case ';' -> {
createToken(Token.Semicolon);
passedNewline = true;
}
default -> unexpected();
}
@ -177,7 +183,11 @@ public class Lexer extends VersionedComponent {
if (isAtEnd()) return;
switch (peek()) {
case ' ', '\r', '\t', '\n' -> advance();
case ' ', '\t' -> advance();
case '\r', '\n' -> {
advance();
passedNewline = true;
}
case '/' -> {
switch (peekNext()) {
case '/' -> {

View File

@ -1,9 +1,9 @@
package io.gitlab.jfronny.muscript.compiler;
public enum MuScriptVersion {
V1, V2;
V1, V2, V3;
public static final MuScriptVersion DEFAULT = V2;
public static final MuScriptVersion DEFAULT = V3;
public boolean contains(MuScriptVersion version) {
return compareTo(version) >= 0;

View File

@ -131,7 +131,10 @@ public class Parser extends VersionedComponent {
List<Expr<?>> expressions = new LinkedList<>();
while (!isAtEnd()) {
expressions.add(expression());
match(Token.Semicolon); // Consume semicolon if present
// Consume semicolon if present
if (!lexer.passedNewline & !match(Token.Semicolon) & !isAtEnd() & version.contains(MuScriptVersion.V3)) {
throw error("Either a semicolon or a new line must separate expressions in scripts");
}
}
if (expressions.isEmpty()) throw new ParseException(PrettyPrintError.builder(lexer.location()).setMessage("Missing any elements in closure").build());
return new Script(expressions);
@ -401,7 +404,11 @@ public class Parser extends VersionedComponent {
List<Expr<?>> expressions = new LinkedList<>();
while (!match(Token.RightBrace)) {
expressions.add(expression());
match(Token.Semicolon); // Consume semicolon if present
// Consume semicolon if present
if (!lexer.passedNewline & !match(Token.Semicolon) & version.contains(MuScriptVersion.V3)) {
if (match(Token.RightBrace)) break;
throw error("Either a semicolon or a new line must separate expressions in closures");
}
}
int end = previous.start;
return new Closure(location(start, end), boundArgs, expressions, variadic);

View File

@ -8,11 +8,12 @@ import org.junit.jupiter.api.Test;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.makeArgs;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.number;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ClosureTest {
@Test
void testScript() {
assertEquals(8, Parser.parseScript("function(2, 1) function(2, 2) function(2, 3)")
assertEquals(8, Parser.parseScript("function(2, 1); function(2, 2); function(2, 3)")
.run(makeArgs())
.asNumber()
.getValue());
@ -23,7 +24,7 @@ class ClosureTest {
assertEquals(2, number("{->2}()"));
assertEquals(2, number("{n->n}(2)"));
assertEquals(2, number("{n->n()}({->2})"));
assertEquals(2, number("{->num = 2 num = num * 2 num = num - 2}()"));
assertThrows(Parser.ParseException.class, () -> number("{->num = 2 num = num * 2 num = num - 2}()"));
assertEquals(2, number("{->num = 2; num = num * 2; num = num - 2}()"));
}