feat(muscript): Language versions

This commit is contained in:
Johannes Frohnmeyer 2023-09-15 19:21:14 +02:00
parent e84f8adc88
commit 6ffd67f23f
Signed by: Johannes
GPG Key ID: E76429612C2929F4
5 changed files with 139 additions and 64 deletions

View File

@ -4,6 +4,7 @@ import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
import io.gitlab.jfronny.muscript.compiler.MuScriptVersion;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.data.dynamic.additional.*;
@ -21,68 +22,83 @@ import static io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal.of;
public class StandardLib {
private static final Random rnd = new Random();
@Deprecated
public static Scope createScope() {
return addTo(new Scope());
return createScope(MuScriptVersion.DEFAULT);
}
public static Scope createScope(MuScriptVersion version) {
return addTo(version, new Scope());
}
@Deprecated
public static Scope addTo(Scope scope) {
return scope
.set("PI", Math.PI)
.set("E", Math.E)
.set("date", new DCallableObject(Map.of(
"today", new DDate(LocalDate::now)
), DFinal.of(args -> {
// Constructor
if (args.size() == 1) return new DDate(() -> LocalDate.ofEpochDay(args.get(0).asNumber().getValue().longValue()));
if (args.size() != 3) throw new IllegalArgumentException("Expected 3 arguments for full date constructor");
int a0 = args.get(0).asNumber().getValue().intValue();
int a1 = args.get(1).asNumber().getValue().intValue();
int a2 = args.get(2).asNumber().getValue().intValue();
return new DDate(() -> LocalDate.of(a0, a1, a2));
}, "date")))
.set("time", new DCallableObject(Map.of(
"now", new DTime(LocalTime::now)
), DFinal.of(args -> {
// Constructor
if (args.size() == 1) return new DTime(() -> LocalTime.ofSecondOfDay(args.get(0).asNumber().getValue().intValue()));
if (args.size() != 3) throw new IllegalArgumentException("Expected 3 arguments for full time constructor");
int a0 = args.get(0).asNumber().getValue().intValue();
int a1 = args.get(1).asNumber().getValue().intValue();
int a2 = args.get(2).asNumber().getValue().intValue();
return new DTime(() -> LocalTime.of(a0, a1, a2));
}, "time")))
return addTo(MuScriptVersion.DEFAULT, scope);
}
.set("round", StandardLib::round)
.set("floor", StandardLib::floor)
.set("ceil", StandardLib::ceil)
.set("abs", StandardLib::abs)
.set("random", StandardLib::random)
public static Scope addTo(MuScriptVersion version, Scope scope) {
if (version.contains(MuScriptVersion.V1)) {
scope
.set("PI", Math.PI)
.set("E", Math.E)
.set("date", new DCallableObject(Map.of(
"today", new DDate(LocalDate::now)
), DFinal.of(args -> {
// Constructor
if (args.size() == 1) return new DDate(() -> LocalDate.ofEpochDay(args.get(0).asNumber().getValue().longValue()));
if (args.size() != 3) throw new IllegalArgumentException("Expected 3 arguments for full date constructor");
int a0 = args.get(0).asNumber().getValue().intValue();
int a1 = args.get(1).asNumber().getValue().intValue();
int a2 = args.get(2).asNumber().getValue().intValue();
return new DDate(() -> LocalDate.of(a0, a1, a2));
}, "date")))
.set("time", new DCallableObject(Map.of(
"now", new DTime(LocalTime::now)
), DFinal.of(args -> {
// Constructor
if (args.size() == 1) return new DTime(() -> LocalTime.ofSecondOfDay(args.get(0).asNumber().getValue().intValue()));
if (args.size() != 3) throw new IllegalArgumentException("Expected 3 arguments for full time constructor");
int a0 = args.get(0).asNumber().getValue().intValue();
int a1 = args.get(1).asNumber().getValue().intValue();
int a2 = args.get(2).asNumber().getValue().intValue();
return new DTime(() -> LocalTime.of(a0, a1, a2));
}, "time")))
.set("toUpper", StandardLib::toUpper)
.set("toLower", StandardLib::toLower)
.set("contains", StandardLib::contains)
.set("replace", StandardLib::replace)
.set("round", StandardLib::round)
.set("floor", StandardLib::floor)
.set("ceil", StandardLib::ceil)
.set("abs", StandardLib::abs)
.set("random", StandardLib::random)
.set("listOf", StandardLib::listOf)
.set("len", StandardLib::len)
.set("isEmpty", StandardLib::isEmpty)
.set("concat", StandardLib::concat)
.set("filter", StandardLib::filter)
.set("allMatch", StandardLib::allMatch)
.set("anyMatch", StandardLib::anyMatch)
.set("map", StandardLib::map)
.set("flatMap", StandardLib::flatMap)
.set("fold", StandardLib::fold)
.set("forEach", StandardLib::forEach)
.set("toObject", StandardLib::toObject)
.set("toUpper", StandardLib::toUpper)
.set("toLower", StandardLib::toLower)
.set("contains", StandardLib::contains)
.set("replace", StandardLib::replace);
}
if (version.contains(MuScriptVersion.V2)) {
scope
.set("listOf", StandardLib::listOf)
.set("len", StandardLib::len)
.set("isEmpty", StandardLib::isEmpty)
.set("concat", StandardLib::concat)
.set("filter", StandardLib::filter)
.set("allMatch", StandardLib::allMatch)
.set("anyMatch", StandardLib::anyMatch)
.set("map", StandardLib::map)
.set("flatMap", StandardLib::flatMap)
.set("fold", StandardLib::fold)
.set("forEach", StandardLib::forEach)
.set("toObject", StandardLib::toObject)
.set("callableObject", StandardLib::callableObject)
.set("enum", StandardLib::enum_)
.set("keys", StandardLib::keys)
.set("values", StandardLib::values)
.set("callableObject", StandardLib::callableObject)
.set("enum", StandardLib::enum_)
.set("keys", StandardLib::keys)
.set("values", StandardLib::values)
.set("fail", StandardLib::fail)
.set("try", StandardLib::try_);
.set("fail", StandardLib::fail)
.set("try", StandardLib::try_);
}
return scope;
}
// Numbers

View File

@ -5,7 +5,7 @@ import java.util.Set;
import java.util.regex.Pattern;
// Heavily inspired by starscript
public class Lexer {
public class Lexer extends VersionedComponent {
public final String file;
/**
@ -22,11 +22,22 @@ public class Lexer {
public final String source;
public int start, current;
@Deprecated
public Lexer(String source) {
this(source, null);
this(MuScriptVersion.DEFAULT, source);
}
@Deprecated
public Lexer(String source, String file) {
this(MuScriptVersion.DEFAULT, source, file);
}
public Lexer(MuScriptVersion version, String source) {
this(version, source, null);
}
public Lexer(MuScriptVersion version, String source, String file) {
super(version);
this.source = Objects.requireNonNull(source);
this.file = file;
}

View File

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

View File

@ -17,33 +17,58 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class Parser {
public class Parser extends VersionedComponent {
private final Lexer lexer;
private final TokenData previous = new TokenData();
private final TokenData current = new TokenData();
@Deprecated
public static Expr<?> parse(String source) {
return parse(source, null);
return parse(MuScriptVersion.DEFAULT, source);
}
@Deprecated
public static Expr<?> parse(String source, String file) {
return new Parser(new Lexer(source, file)).parse().optimize();
return parse(MuScriptVersion.DEFAULT, source, file);
}
@Deprecated
public static Script parseScript(String source) {
return parseScript(source, null);
return parseScript(MuScriptVersion.DEFAULT, source);
}
@Deprecated
public static Script parseScript(String source, String file) {
return new Parser(new Lexer(source, file)).parseScript().optimize();
return parseScript(MuScriptVersion.DEFAULT, source, file);
}
@Deprecated
public static Script parseMultiScript(String startFile, SourceFS filesystem) {
return new Script(parseMultiScript(startFile, filesystem, new HashSet<>()).stream().flatMap(Script::stream).toList());
return parseMultiScript(MuScriptVersion.DEFAULT, startFile, filesystem);
}
private static List<Script> parseMultiScript(String startFile, SourceFS filesystem, Set<String> alreadyIncluded) {
public static Expr<?> parse(MuScriptVersion version, String source) {
return parse(version, source, null);
}
public static Expr<?> parse(MuScriptVersion version, String source, String file) {
return new Parser(new Lexer(version, source, file)).parse().optimize();
}
public static Script parseScript(MuScriptVersion version, String source) {
return parseScript(version, source, null);
}
public static Script parseScript(MuScriptVersion version, String source, String file) {
return new Parser(new Lexer(version, source, file)).parseScript().optimize();
}
public static Script parseMultiScript(MuScriptVersion version, String startFile, SourceFS filesystem) {
return new Script(parseMultiScript(version, startFile, filesystem, new HashSet<>()).stream().flatMap(Script::stream).toList());
}
private static List<Script> parseMultiScript(MuScriptVersion version, String startFile, SourceFS filesystem, Set<String> alreadyIncluded) {
alreadyIncluded.add(startFile);
boolean isIncludes = true;
StringBuilder src = new StringBuilder();
@ -59,7 +84,7 @@ public class Parser {
String file = s.substring(includePrefix.length());
src.append("// include ").append(file).append("\n");
if (!alreadyIncluded.contains(file)) {
includes.addAll(parseMultiScript(file, filesystem, alreadyIncluded));
includes.addAll(parseMultiScript(version, file, filesystem, alreadyIncluded));
}
} else {
throw new ParseException(PrettyPrintError.builder()
@ -77,6 +102,7 @@ public class Parser {
}
public Parser(Lexer lexer) {
super(lexer.version);
this.lexer = lexer;
}
@ -89,7 +115,7 @@ public class Parser {
public Expr<?> parse() {
advance();
Expr<?> expr = expression();
if (!isAtEnd())
if (!isAtEnd() && version.contains(MuScriptVersion.V2))
throw new ParseException(PrettyPrintError.builder(lexer.location()).setMessage("Unexpected element after end of expression").build());
return expr;
}

View File

@ -0,0 +1,11 @@
package io.gitlab.jfronny.muscript.compiler;
import java.util.Objects;
public abstract class VersionedComponent {
protected final MuScriptVersion version;
public VersionedComponent(MuScriptVersion version) {
this.version = Objects.requireNonNull(version);
}
}