Remove legacy JVM-specific file system, shell, and related implementations; migrate to platform-agnostic and common main modules.

This commit is contained in:
2025-08-14 16:04:13 +02:00
parent 63f9a1f928
commit c7552c2a95
133 changed files with 981 additions and 898 deletions

View File

@@ -0,0 +1,19 @@
package mtmc.lang;
public class CompilationException extends Exception {
protected Span span;
public CompilationException(String message, Span span) {
super(message);
this.span = span;
}
public CompilationException(CompilationException parent, String message) {
super(message, parent);
this.span = parent.span;
}
public Span getSpan() {
return span;
}
}

View File

@@ -0,0 +1,7 @@
package mtmc.lang;
import mtmc.os.exec.Executable;
public interface Language {
Executable compileExecutable(String filename, String source) throws ParseException, CompilationException;
}

View File

@@ -0,0 +1,55 @@
package mtmc.lang;
public record Location(int index) {
public static int[] getLineNos(String source, int... indices) {
int[] out = new int[indices.length];
int index = 0, line = 1;
while (index < source.length()) {
for (int i = 0; i < indices.length; i++) {
if (indices[i] == index) {
out[i] = line;
}
}
int cp = source.charAt(index);
if (cp == '\n') {
line += 1;
}
index += Character.charCount(cp);
}
return out;
}
public LineInfo getLineInfo(String source) {
int index = 0, lineStart = 0;
int lineno = 1;
int column = 1;
while (index < source.length()) {
if (index == this.index) {
break;
}
int cp = source.charAt(index);
if (cp == '\n') {
lineno += 1;
lineStart = index + 1;
} else {
column += 1;
}
index += Character.charCount(cp);
}
while (index < source.length()) {
int cp = source.charAt(index);
index += Character.charCount(cp);
if (cp == '\n') break;
}
String line = source.substring(lineStart, index);
return new LineInfo(lineno, column, line);
}
public record LineInfo(int lineno, int column, String line) {}
}

View File

@@ -0,0 +1,43 @@
package mtmc.lang;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ParseException extends Exception {
public final List<Message> messages;
public ParseException(Message message, Message ...rest) {
var messages = new ArrayList<Message>(1 + rest.length);
messages.add(message);
messages.addAll(Arrays.asList(rest));
this.messages = Collections.unmodifiableList(messages);
}
public ParseException(ParseException parent, Message message, Message ...rest) {
var messages = new ArrayList<Message>( 1 + rest.length + parent.messages.size());
messages.add(message);
messages.addAll(Arrays.asList(rest));
messages.addAll(parent.messages);
this.messages = Collections.unmodifiableList(messages);
}
public record Message(Span span, String message) {
public Message(Token token, String message) {
this(Span.of(token), message);
}
public Token start() {
return span.start();
}
public Token end() {
return span.end();
}
}
public String report(String source) {
return "TODO: I ain't no snitch";
}
}

View File

@@ -0,0 +1,17 @@
package mtmc.lang;
public record Span(Token start, Token end) {
public static Span of(Token token) {
return new Span(token, token);
}
public static Span of(Token start, Token end) {
return new Span(start, end);
}
public boolean isOnSingleLine(String source) {
int[] lines = Location.getLineNos(source, start.start(), end.end());
return lines[0] == lines[1];
}
}

View File

@@ -0,0 +1,15 @@
package mtmc.lang;
public interface Token {
Location getStart();
Location getEnd();
String getContent();
default int start() {
return getStart().index();
}
default int end() {
return getEnd().index();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,47 @@
package mtmc.lang.sea;
import mtmc.lang.CompilationException;
import mtmc.lang.Language;
import mtmc.lang.ParseException;
import mtmc.lang.sea.ast.Error;
import mtmc.lang.sea.ast.Unit;
import mtmc.os.exec.Executable;
public class SeaLanguage implements Language {
@Override
public Executable compileExecutable(String filename, String source) throws ParseException, CompilationException {
var tokens = Token.tokenize(source);
var parser = new SeaParser(filename, source, tokens);
Unit program = parser.parseUnit();
var errors = program.collectErrors();
if (!errors.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (Error error : errors) {
reportError(source, sb, error.exception());
}
throw new RuntimeException(sb.toString());
}
var compiler = new SeaCompiler(program);
return compiler.compile();
}
private static void reportError(String src, StringBuilder sb, ParseException e) {
sb.append("Error:\n");
for (var msg : e.messages) {
var lo = Token.getLineAndOffset(src, msg.start().start());
int lineNo = lo[0];
int column = lo[1];
var line = Token.getLineFor(src, msg.start().start());
String prefix = " %03d:%03d | ".formatted(lineNo, column);
String info = " ".repeat(prefix.length() - 2) + "| ";
sb.append(info).append(msg.message()).append('\n');
sb.append(prefix).append(line).append('\n');
sb
.repeat(' ', prefix.length() + column - 1)
.repeat('^', Math.max(1, msg.end().end() - msg.start().start()));
sb.append("\n\n");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
package mtmc.lang.sea;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public sealed interface SeaType {
default int size() {
if (this == CHAR) return 2;
if (this == INT) return 2;
if (this == VOID) return 0;
if (this instanceof Pointer) return 2;
if (this instanceof Struct struct) {
int size = 0;
for (Map.Entry<String, SeaType> fieldSet : struct.fields.entrySet()) {
SeaType type = fieldSet.getValue();
size += type.size();
}
return size;
}
throw new IllegalStateException("sizeof " + getClass().getName() + " is undefined");
}
default boolean isArithmetic() {
return this == CHAR || this == INT;
}
default boolean isIntegral() {
return this == CHAR || this == INT || this instanceof Pointer;
}
default boolean isStructure() {
return this instanceof Struct;
}
default boolean isInt() {
return this == INT;
}
default boolean isChar() {
return this == CHAR;
}
default boolean isVoid() {
return this == VOID;
}
default boolean isAnIntegralPointer() {
return this instanceof Pointer(SeaType component) && component.isIntegral();
}
default boolean isAPointerToAnInt() {
return this instanceof Pointer(SeaType component) && component.isInt();
}
default boolean isAPointerTo(SeaType inner) {
return this instanceof Pointer(SeaType it) && it.equals(inner);
}
default boolean isAPointer() {
return this instanceof Pointer;
}
default boolean isFunc() {
return this instanceof Func;
}
default SeaType componentType() {
return ((Pointer) this).component;
}
default SeaType resultType() {
return ((Func) this).result();
}
default void checkConversionTo(SeaType other) throws ConversionError {
if (this.isVoid() || other.isVoid()) throw new ConversionError(this, other, "void is not assignable to void");
if (this.isArithmetic() && other.isArithmetic()) return;
if (this.isAPointer() && other.isAPointer()) return;
if (this instanceof Initializer initializer && other instanceof Struct s) {
// this is kinda complex
// the way this should work is we process named assignments and then based on the last
// index of the named assignments, we start putting in values
// the challenge here is that the blob may have too many values or may initialize them improperly
if (initializer.values.size() != s.fields.size()) {
throw new ConversionError(this, other, "initializer has too many or too few values");
}
int i = 0;
for (Map.Entry<String, SeaType> entry : s.fields.entrySet()) {
var name = entry.getKey();
var ty = entry.getValue();
var valueTy = initializer.values.get(i);
try {
valueTy.checkConversionTo(ty);
} catch (ConversionError error) {
throw new ConversionError(ty, valueTy,
"value cannot be assigned to " + ty.repr() + " for '" + name + "'", error);
}
i += 1;
}
return;
}
if (!this.equals(other)) {
throw new ConversionError(this, other, this.repr() + " is not convertible to " + other.repr());
}
}
class ConversionError extends Exception {
public final SeaType fromType, toType;
private ConversionError(SeaType fromType, SeaType toType, String message) {
super(message);
this.fromType = fromType;
this.toType = toType;
}
private ConversionError(SeaType fromType, SeaType toType, String message, ConversionError parent) {
super(message, parent);
this.fromType = fromType;
this.toType = toType;
}
}
default boolean isCastableTo(SeaType target) {
if (target.isVoid()) return true;
if (this.isAPointer() && target.isInt()) return true;
if (this.isArithmetic() && target.isArithmetic()) return true;
return this.equals(target);
}
default String repr() {
if (this instanceof Pointer p) {
if (p.baseType() instanceof Func(List<SeaType> params, boolean isVararg, SeaType result)) {
var s = new StringBuilder();
s.append(result.repr()).append("(*");
var x = p.component;
while (x instanceof Pointer p2) {
x = p2.component;
s.append("*");
}
s.append(")(");
int i = 0;
for (var param : params) {
if (i > 0) s.append(", ");
s.append(param.repr());
i = i + 1;
}
s.append(")");
return s.toString();
} else {
return p.component.repr() + "*";
}
}
if (this == CHAR) return "char";
if (this == INT) return "int";
if (this == VOID) return "void";
if (this instanceof Func(List<SeaType> params, boolean isVararg, SeaType result)) {
var s = new StringBuilder();
s.append(result.repr());
s.append("(");
int i = 0;
for (var param : params) {
if (i > 0) s.append(", ");
s.append(param.repr());
i = i + 1;
}
s.append(")");
return s.toString();
}
if (this instanceof Initializer(List<SeaType> values)) {
var s = new StringBuilder();
s.append("{");
for (int i = 0; i < values.size(); i++) {
if (i > 0) s.append(", ");
s.append(values.get(i).repr());
}
s.append("}");
return s.toString();
}
if (this instanceof Struct s) {
return "struct " + s.name;
}
throw new UnsupportedOperationException("unknown type " + this);
}
SeaType CHAR = new Primitive("char");
SeaType INT = new Primitive("int");
SeaType VOID = new Primitive("void");
record Pointer(SeaType component) implements SeaType {
SeaType baseType() {
var ty = component;
while (ty instanceof Pointer(SeaType c)) {
ty = c;
}
return ty;
}
}
final class Primitive implements SeaType {
// this is purely for debug info lmfao
public final String name;
private Primitive(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
record Func(List<SeaType> params, boolean isVararg, SeaType result) implements SeaType {
public Func(List<SeaType> params, SeaType result) {
this(params, false, result);
}
}
record Struct(String name, LinkedHashMap<String, SeaType> fields) implements SeaType {
public SeaType field(String name) {
return fields.get(name);
}
}
record Initializer(List<SeaType> values) implements SeaType {
}
}

View File

@@ -0,0 +1,59 @@
package mtmc.lang.sea;
import mtmc.lang.sea.ast.DeclarationFunc;
import mtmc.lang.sea.ast.DeclarationVar;
import mtmc.lang.sea.ast.StatementVar;
import mtmc.lang.sea.ast.TypeDeclaration;
public class Symbol {
public final String name;
public final SeaType type;
public final TypeDeclaration typeDecl;
public final boolean isParam, isGlobal;
public Symbol(DeclarationFunc.Param param) {
this.name = param.name.content();
this.type = param.type.type();
this.typeDecl = null;
this.isParam = true;
this.isGlobal = false;
}
public Symbol(DeclarationVar decl) {
this.name = decl.name();
this.type = decl.type.type();
this.typeDecl = null;
this.isParam = false;
this.isGlobal = true;
}
public Symbol(StatementVar stmt) {
this.name = stmt.name();
this.type = stmt.type.type();
this.typeDecl = null;
this.isParam = false;
this.isGlobal = false;
}
public Symbol(DeclarationFunc func) {
this.name = func.name.content();
this.type = func.type();
this.typeDecl = null;
this.isParam = false;
this.isGlobal = true;
}
public Symbol(TypeDeclaration declaration) {
this.name = declaration.name();
this.type = null;
this.typeDecl = declaration;
this.isParam = false;
this.isGlobal = false;
}
public boolean isAddressable() {
if (this.typeDecl != null) throw new IllegalStateException("cannot address non-data symbol");
if (this.isParam) return false; // parameters are not addressable!
return true;
}
}

View File

@@ -0,0 +1,426 @@
package mtmc.lang.sea;
import mtmc.lang.Location;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public record Token(
Type type,
@NotNull
String content,
int start,
int end
) implements mtmc.lang.Token {
public static final Token SOF = new Token(Type.SOF, "", 0, 0);
public static final Token EOF = new Token(Type.EOF, "", Integer.MAX_VALUE, Integer.MAX_VALUE);
public enum Type {
// Special
LIT_INT(null),
LIT_STR(null),
LIT_CHAR(null),
LIT_IDENT(null),
KW_TYPEDEF("typedef"),
KW_STRUCT("struct"),
KW_IF("if"),
KW_ELSE("else"),
KW_FOR("for"),
KW_WHILE("while"),
KW_DO("do"),
KW_GOTO("goto"),
KW_CONTINUE("continue"),
KW_BREAK("break"),
KW_RETURN("return"),
KW_SIZEOF("sizeof"),
KW_INT("int"),
KW_CHAR("char"),
KW_VOID("void"),
SOF(null),
EOF(null),
// Groups
LEFT_PAREN("("),
RIGHT_PAREN(")"),
LEFT_BRACKET("["),
RIGHT_BRACKET("]"),
LEFT_BRACE("{"),
RIGHT_BRACE("}"),
// Simple Punct
DOT3("..."),
DOT("."),
SEMICOLON(";"),
COMMA(","),
COLON(":"),
TILDE("~"),
QUESTION("?"),
PLUS2("++"),
PLUS_EQ("+="),
PLUS("+"),
DASH2("--"),
DASH_EQ("-="),
ARROW("->"),
DASH("-"),
STAR_EQ("*="),
STAR("*"),
SLASH_EQ("/="),
SLASH("/"),
PERCENT_EQ("%="),
PERCENT("%"),
AMPERSAND2("&&"),
AMPERSAND_EQ("&="),
AMPERSAND("&"),
BAR2("||"),
BAR_EQ("|="),
BAR("|"),
CARET("^"),
CARET_EQ("^="),
LEFT_ARROW2_EQ("<<="),
LEFT_ARROW2("<<"),
LEFT_ARROW_EQ("<="),
LEFT_ARROW("<"),
RIGHT_ARROW2_EQ(">>="),
RIGHT_ARROW2(">>"),
RIGHT_ARROW_EQ(">="),
RIGHT_ARROW(">"),
EQUAL2("=="),
EQUAL("="),
BANG_EQ("!="),
BANG("!");
public final String lex;
public static final Type[] PUNCT;
static {
List<Type> list = new ArrayList<>();
for (Type t : Type.values()) {
if (t.lex != null) {
list.add(t);
}
}
PUNCT = list.toArray(new Type[0]);
}
public static final Type[] KEYWORDS;
static {
List<Type> list = new ArrayList<>();
for (Type t : Type.values()) {
if (t.name().startsWith("KW_")) {
list.add(t);
}
}
KEYWORDS = list.toArray(new Type[0]);
}
Type(String lex) {
this.lex = lex;
}
}
public static int[] getLineAndOffset(String src, int index) {
int line = 1;
int column = 1;
for (int i = 0; i < index && i < src.length(); i++) {
char c = src.charAt(i);
if (c == '\n') {
line = line + 1;
column = 1;
} else {
column = column + 1;
}
}
return new int[]{line, column};
}
public static String getLineFor(String src, int index) {
int start = 0;
for (int i = Math.min(index, src.length() - 1); i >= 0; i--) {
if (src.charAt(i) == '\n') {
start = i + 1;
break;
}
}
int end = src.length();
for (int i = index; i < src.length(); i++) {
if (src.charAt(i) == '\n') {
break;
}
end = i + 1;
}
return src.substring(start, end);
}
public static String highlight(String src, int start, int end) {
var s = getLineAndOffset(src, start);
var e = getLineAndOffset(src, end);
int lineStart;
if (s[0] != e[0]) {
lineStart = 0;
} else {
lineStart = s[1] - 1;
}
int lineEnd = e[1] - 1;
String line = getLineFor(src, end);
StringBuilder result = new StringBuilder();
int off = 0;
if (lineStart > 10) {
result.append("... ");
off += 4;
result.append(line.substring(lineStart, lineEnd));
} else {
result.append(line.substring(0, lineEnd));
}
result.append('\n');
result.repeat(' ', off + lineStart);
if (start == Integer.MAX_VALUE) {
result.append("^ (at EOL)");
} else {
result.repeat('^', lineEnd - lineStart);
result.append(" (here)");
}
return result.toString();
}
public static List<Token> tokenize(String src) throws TokenizeException {
List<Token> tokens = new ArrayList<>();
int offset = 0;
do {
Token token = tokenizeOne(src, offset);
if (token == null) break;
tokens.add(token);
offset = token.end();
} while (true);
return tokens;
}
private static boolean match(String str, int start, String token) {
if (str == null) return false;
if (str.length() - start < token.length()) return false;
for (int i = 0; i < token.length(); i++) {
char c = str.charAt(start + i);
char d = token.charAt(i);
if (c != d) return false;
}
return true;
}
private static boolean match(String str, int start, char c) {
if (str == null) return false;
if (str.length() - start < Character.charCount(c)) return false;
return str.charAt(start) == c;
}
public static Token tokenizeOne(String src, int offset) throws TokenizeException {
while (offset < src.length()) {
if (Character.isWhitespace(src.charAt(offset))) {
offset += Character.charCount(src.charAt(offset));
} else if (match(src, offset, "//")) {
offset += 2;
while (offset < src.length()) {
char c = src.charAt(offset);
offset += Character.charCount(c);
if (c == '\n') {
break;
}
}
} else if (match(src, offset, "/*")) {
offset += 2;
while (offset < src.length()) {
if (match(src, offset, "*/")) {
offset += 2;
break;
} else {
offset += Character.charCount(src.charAt(offset));
}
}
} else {
break;
}
}
if (offset >= src.length()) return null;
int start = offset;
Type type;
String content = null;
char c = src.charAt(offset);
if (Character.isDigit(c)) {
do {
offset += Character.charCount(src.charAt(offset));
} while (offset < src.length() && Character.isDigit(src.charAt(offset)));
content = src.substring(start, offset);
type = Type.LIT_INT;
} else if (Character.isLetter(c) || c == '_') {
do {
offset += Character.charCount(src.charAt(offset));
} while (offset < src.length() && (Character.isLetter(src.charAt(offset)) || Character.isDigit(src.charAt(offset)) || src.charAt(offset) == '_'));
content = src.substring(start, offset);
type = Type.LIT_IDENT;
for (var ty : Type.KEYWORDS) {
if (content.equals(ty.lex)) {
type = ty;
break;
}
}
} else if (c == '\'') {
offset += Character.charCount(c);
char d = src.charAt(offset);
offset += Character.charCount(d);
if (d == '\\') {
if (offset >= src.length()) throw new TokenizeException("invalid character escape " + d, start, offset);
d = src.charAt(offset);
offset += Character.charCount(d);
content = switch (d) {
case 'n':
yield "\n";
case 'r':
yield "\r";
case 't':
yield "\t";
case '\\':
yield "\\";
case '\'':
yield "'";
case '"':
yield "\"";
case '?':
yield "?";
default:
throw new TokenizeException("invalid character escape " + d, start, offset);
};
} else {
content = String.valueOf(d);
}
if (offset >= src.length() || src.charAt(offset) != '\'') {
throw new TokenizeException("unterminated character literal", start, offset);
}
offset += Character.charCount('\'');
type = Type.LIT_CHAR;
} else if (c == '"') {
offset += Character.charCount(src.charAt(offset));
StringBuilder sb = new StringBuilder();
while (offset < src.length() && src.charAt(offset) != '"') {
char d = src.charAt(offset);
offset += Character.charCount(d);
if (d == '\\') {
d = src.charAt(offset);
offset += Character.charCount(d);
char s = switch (d) {
case 'n':
yield '\n';
case 'r':
yield '\r';
case 't':
yield '\t';
case '\\':
yield '\\';
case '\'':
yield '\'';
case '"':
yield '"';
case '?':
yield '?';
default:
throw new TokenizeException("invalid string escape " + d, start, offset);
};
sb.append(s);
} else if (d == '\n') {
break;
} else {
sb.append(d);
}
}
if (offset >= src.length() || src.charAt(offset) != '"') {
throw new TokenizeException("unterminated string literal", start, offset);
}
content = sb.toString();
offset += Character.charCount('\"');
type = Type.LIT_STR;
} else {
type = null;
for (Type t : Type.PUNCT) {
if (match(src, start, t.lex)) {
type = t;
content = t.lex;
offset += t.lex.length();
break;
}
}
if (type == null) {
throw new TokenizeException("unexpected character '" + src.charAt(start) + "'", start, offset);
}
}
Objects.requireNonNull(content);
return new Token(type, content, start, offset);
}
public static class TokenizeException extends IllegalArgumentException {
public final int start, end;
public TokenizeException(String msg, int start, int end) {
super(msg);
this.start = start;
this.end = end;
}
@Override
public String toString() {
return "TokenizeException at " + start + ":" + end + ", " + getLocalizedMessage();
}
}
@Override
public boolean equals(Object o) {
if (o instanceof String s) return Objects.equals(content, s);
if (!(o instanceof Token token)) return false;
return end == token.end && start == token.start && Objects.equals(content, token.content) && type == token.type;
}
@Override
public int hashCode() {
return Objects.hash(type, content, start, end);
}
@Override
public Location getStart() {
return new Location(start);
}
@Override
public Location getEnd() {
return new Location(end);
}
@Override
public String getContent() {
return content();
}
}

View File

@@ -0,0 +1,139 @@
package mtmc.lang.sea.ast;
import mtmc.lang.Span;
import mtmc.lang.sea.Token;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public sealed abstract class Ast permits Declaration, DeclarationFunc.Param, DeclarationStruct.Field, Expression, Statement, TypeExpr, Unit {
public final Token start, end;
public Ast(Token start, Token end) {
this.start = start;
this.end = end;
}
public Span span() {
return Span.of(start, end);
}
public Stream<Ast> getChildren() {
return switch (this) {
case DeclarationFunc declarationFunc -> {
Stream<Ast> out = Stream.of(declarationFunc.returnType);
out = Stream.concat(out, declarationFunc.params.params().stream());
if (declarationFunc.body != null) {
out = Stream.concat(out, Stream.of(declarationFunc.body));
}
yield out;
}
case DeclarationSyntaxError ignored -> Stream.empty();
case DeclarationTypedef declarationTypedef -> Stream.of(declarationTypedef.type);
case DeclarationVar declarationVar -> {
Stream<Ast> out = Stream.of(declarationVar.type);
if (declarationVar.initializer != null) {
out = Stream.concat(out, Stream.of(declarationVar.initializer));
}
yield out;
}
case DeclarationStruct struct -> struct.fields.stream().map(x -> x);
case DeclarationStruct.Field field -> Stream.of(field.type);
case DeclarationFunc.Param param -> Stream.of(param.type);
case ExpressionAccess expressionAccess -> Stream.of(expressionAccess.value);
case ExpressionBin expressionBin -> Stream.of(expressionBin.lhs, expressionBin.rhs);
case ExpressionCall expressionCall -> {
Stream<Ast> out = Stream.of(expressionCall.functor);
out = Stream.concat(out, expressionCall.args.stream());
yield out;
}
case ExpressionInitializer init -> init.values.stream().map(x -> x);
case ExpressionCast expressionCast -> Stream.of(expressionCast.type, expressionCast.value);
case ExpressionChar ignored -> Stream.empty();
case ExpressionIdent ignored -> Stream.empty();
case ExpressionIndex expressionIndex -> Stream.of(expressionIndex.array, expressionIndex.index);
case ExpressionInteger ignored -> Stream.empty();
case ExpressionParens expressionParens -> Stream.of(expressionParens.inner);
case ExpressionPostfix expressionPostfix -> Stream.of(expressionPostfix.inner);
case ExpressionPrefix expressionPrefix -> Stream.of(expressionPrefix.inner);
case ExpressionString ignored -> Stream.empty();
case ExpressionTypeError typeError -> Stream.of(typeError.inner);
case ExpressionSyntaxError expressionSyntaxError -> {
if (expressionSyntaxError.child != null) {
yield Stream.of(expressionSyntaxError.child);
} else {
yield Stream.empty();
}
}
case ExpressionTernary expressionTernary -> Stream.of(
expressionTernary.cond,
expressionTernary.then,
expressionTernary.otherwise
);
case StatementBlock statementBlock -> statementBlock.statements.stream().map(x -> x);
case StatementBreak ignored -> Stream.empty();
case StatementContinue ignored -> Stream.empty();
case StatementDoWhile statementDoWhile -> Stream.of(statementDoWhile.body, statementDoWhile.condition);
case StatementExpression statementExpression -> Stream.of(statementExpression.expression);
case StatementFor statementFor -> {
Stream<Ast> out = Stream.empty();
if (statementFor.initExpression != null) {
out = Stream.concat(out, Stream.of(statementFor.initExpression));
} else if (statementFor.initStatement != null) {
out = Stream.concat(out, Stream.of(statementFor.initStatement));
}
if (statementFor.condition != null) {
out = Stream.concat(out, Stream.of(statementFor.condition));
}
if (statementFor.inc != null) {
out = Stream.concat(out, Stream.of(statementFor.inc));
}
out = Stream.concat(out, Stream.of(statementFor.body));
yield out;
}
case StatementGoto ignored -> Stream.empty();
case StatementIf statementIf -> {
Stream<Ast> out = Stream.of(statementIf.condition, statementIf.body);
if (statementIf.elseBody != null) out = Stream.concat(out, Stream.of(statementIf.elseBody));
yield out;
}
case StatementReturn statementReturn -> {
if (statementReturn.value == null) {
yield Stream.empty();
} else {
yield Stream.of(statementReturn.value);
}
}
case StatementSyntaxError ignored -> Stream.empty();
case StatementVar statementVar -> {
Stream<Ast> out = Stream.of(statementVar.type);
if (statementVar.initValue != null) {
out = Stream.concat(out, Stream.of(statementVar.initValue));
}
yield out;
}
case StatementWhile statementWhile -> Stream.of(statementWhile.condition, statementWhile.body);
case TypeExprArray typeExprArray -> Stream.of(typeExprArray.inner);
case TypeExprChar ignored -> Stream.empty();
case TypeExprInt ignored -> Stream.empty();
case TypeExprRef ignored -> Stream.empty();
case TypeExprVoid ignored -> Stream.empty();
case TypePointer ignored -> Stream.empty();
case Unit unit -> unit.declarations.stream().map(x -> x);
};
}
public List<Error> collectErrors() {
var errors = new ArrayList<Error>();
collectErrors(errors);
return errors;
}
public void collectErrors(List<Error> errors) {
if (this instanceof Error e) {
errors.add(e);
}
getChildren().forEach(child -> child.collectErrors(errors));
}
}

View File

@@ -0,0 +1,9 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public abstract sealed class Declaration extends Ast permits DeclarationFunc, DeclarationStruct, DeclarationSyntaxError, DeclarationTypedef, DeclarationVar {
public Declaration(Token start, Token end) {
super(start, end);
}
}

View File

@@ -0,0 +1,59 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
import java.util.ArrayList;
import java.util.List;
public final class DeclarationFunc extends Declaration {
public final TypeExpr returnType;
public final Token name;
public final ParamList params;
public final StatementBlock body;
public DeclarationFunc(TypeExpr returnType, Token name, ParamList paramList, StatementBlock body, Token end) {
super(returnType.start, end);
this.returnType = returnType;
this.name = name;
this.params = paramList;
this.body = body;
}
public static final class Param extends Ast {
public final TypeExpr type;
public final Token name;
public Param(TypeExpr type, Token name) {
super(type.start, name.end() < type.end.end() ? type.end : name);
this.type = type;
this.name = name;
}
}
private SeaType.Func type;
public SeaType.Func type() {
if (type == null) {
var paramTypes = new ArrayList<SeaType>(params.size());
for (Param param : params.params) {
paramTypes.add(param.type.type());
}
type = new SeaType.Func(
paramTypes,
params.isVararg,
returnType.type()
);
}
return type;
}
public record ParamList(List<Param> params, boolean isVararg) {
public int size() {
return params.size();
}
public SeaType getParamType(int i) {
return params.get(i).type.type();
}
}
}

View File

@@ -0,0 +1,54 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
import java.util.LinkedHashMap;
import java.util.List;
public final class DeclarationStruct extends Declaration implements TypeDeclaration {
public final Token name;
public final List<Field> fields;
public DeclarationStruct(Token start, Token name, List<Field> fields, Token end) {
super(start, end);
this.name = name;
this.fields = List.copyOf(fields);
}
public String name() {
return name.content();
}
private SeaType type;
@Override
public SeaType type() {
if (type == null) {
var fields = new LinkedHashMap<String, SeaType>();
for (var field : this.fields) {
fields.put(field.name(), field.type());
}
type = new SeaType.Struct(name(), fields);
}
return type;
}
public static final class Field extends Ast {
public final TypeExpr type;
public final Token name;
public Field(TypeExpr type, Token name) {
super(type.start, name);
this.type = type;
this.name = name;
}
public String name() {
return name.content();
}
public SeaType type() {
return type.type();
}
}
}

View File

@@ -0,0 +1,18 @@
package mtmc.lang.sea.ast;
import mtmc.lang.ParseException;
import mtmc.lang.sea.Token;
public final class DeclarationSyntaxError extends Declaration implements SyntaxError {
public final ParseException exception;
public DeclarationSyntaxError(Token token, ParseException parseException) {
super(token, token);
this.exception = parseException;
}
@Override
public ParseException exception() {
return exception;
}
}

View File

@@ -0,0 +1,26 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class DeclarationTypedef extends Declaration implements TypeDeclaration {
public final TypeExpr type;
public final Token name;
public DeclarationTypedef(Token start, TypeExpr type, Token name, Token end) {
super(start, type.end);
this.type = type;
this.name = name;
}
@Override
public String name() {
return name.content();
}
@Override
public SeaType type() {
return type.type();
}
}

View File

@@ -0,0 +1,20 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class DeclarationVar extends Declaration {
public final TypeExpr type;
public final Token name;
public final Expression initializer;
public DeclarationVar(TypeExpr type, Token name, Expression initializer) {
super(type.start, initializer == null ? name : initializer.end);
this.type = type;
this.name = name;
this.initializer = initializer;
}
public String name() {
return this.name.content();
}
}

View File

@@ -0,0 +1,7 @@
package mtmc.lang.sea.ast;
import mtmc.lang.ParseException;
public interface Error {
ParseException exception();
}

View File

@@ -0,0 +1,23 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
import java.util.Objects;
public sealed abstract class Expression extends Ast permits ExpressionAccess, ExpressionBin, ExpressionCall, ExpressionCast, ExpressionChar, ExpressionIdent, ExpressionIndex, ExpressionInitializer, ExpressionInteger, ExpressionParens, ExpressionPostfix, ExpressionPrefix, ExpressionString, ExpressionSyntaxError, ExpressionTernary, ExpressionTypeError {
private final SeaType type;
public Expression(Token start, Token end, SeaType type) {
super(start, end);
this.type = Objects.requireNonNull(type, "'type' cannot be null");
}
public SeaType type() {
return type;
}
public enum ValueKind {
Addressable,
Immediate
}
}

View File

@@ -0,0 +1,17 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionAccess extends Expression {
public final Expression value;
public final Token access;
public final Token prop;
public ExpressionAccess(Expression value, Token access, Token prop, SeaType type) {
super(value.start, prop, type);
this.value = value;
this.access = access;
this.prop = prop;
}
}

View File

@@ -0,0 +1,21 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionBin extends Expression {
public final Expression lhs;
public final Token op;
public final Expression rhs;
public ExpressionBin(Expression lhs, Token op, Expression rhs, SeaType type) {
super(lhs.start, rhs.end, type);
this.lhs = lhs;
this.op = op;
this.rhs = rhs;
}
public String op() {
return op.content();
}
}

View File

@@ -0,0 +1,17 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
import java.util.List;
public final class ExpressionCall extends Expression {
public final Expression functor;
public final List<Expression> args;
public ExpressionCall(Expression functor, List<Expression> args, Token end, SeaType type) {
super(functor.start, end, type);
this.functor = functor;
this.args = args;
}
}

View File

@@ -0,0 +1,14 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class ExpressionCast extends Expression {
public final TypeExpr type;
public final Expression value;
public ExpressionCast(Token start, TypeExpr type, Expression value) {
super(start, value.end, type.type());
this.type = type;
this.value = value;
}
}

View File

@@ -0,0 +1,14 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionChar extends Expression {
public ExpressionChar(Token token) {
super(token, token, SeaType.CHAR);
}
public Character content() {
return start.content().charAt(0);
}
}

View File

@@ -0,0 +1,17 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionIdent extends Expression {
public final boolean isAddressable;
public ExpressionIdent(Token token, SeaType type, boolean isAddressable) {
super(token, token, type);
this.isAddressable = isAddressable;
}
public String name() {
return start.content();
}
}

View File

@@ -0,0 +1,14 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionIndex extends Expression {
public final Expression array, index;
public ExpressionIndex(Expression array, Expression index, Token end, SeaType type) {
super(array.start, end, type);
this.array = array;
this.index = index;
}
}

View File

@@ -0,0 +1,27 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
import java.util.ArrayList;
import java.util.List;
public final class ExpressionInitializer extends Expression {
public final List<Expression> values;
private static SeaType.Initializer blobType(List<Expression> values) {
var types = new ArrayList<SeaType>();
for (var value : values) {
types.add(value.type());
}
return new SeaType.Initializer(types);
}
public ExpressionInitializer(Token start, List<Expression> values, Token end) {
super(start, end, blobType(values));
this.values = List.copyOf(values);
}
}

View File

@@ -0,0 +1,13 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionInteger extends Expression {
public final int value;
public ExpressionInteger(Token start) {
super(start, start, SeaType.INT);
this.value = Integer.parseInt(start.content());
}
}

View File

@@ -0,0 +1,12 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class ExpressionParens extends Expression {
public final Expression inner;
public ExpressionParens(Token start, Expression inner, Token end) {
super(start, end, inner.type());
this.inner = inner;
}
}

View File

@@ -0,0 +1,17 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionPostfix extends Expression {
public Expression inner;
public ExpressionPostfix(Expression lhs, Token op, SeaType type) {
super(lhs.start, op, type);
this.inner = lhs;
}
public String op() {
return end.content();
}
}

View File

@@ -0,0 +1,17 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionPrefix extends Expression {
public final Expression inner;
public ExpressionPrefix(Token operator, Expression rhs, SeaType type) {
super(operator, rhs.end, type);
this.inner = rhs;
}
public String op() {
return start.content();
}
}

View File

@@ -0,0 +1,18 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class ExpressionString extends Expression {
public ExpressionString(Token token) {
super(token, token, new SeaType.Pointer(SeaType.CHAR));
}
public byte[] getBytes() {
return start.content().getBytes();
}
public String content() {
return start.content();
}
}

View File

@@ -0,0 +1,27 @@
package mtmc.lang.sea.ast;
import mtmc.lang.ParseException;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
import org.jetbrains.annotations.Nullable;
public final class ExpressionSyntaxError extends Expression implements SyntaxError {
@Nullable
public final Expression child;
public final ParseException exception;
public ExpressionSyntaxError(Token token, String message) {
this(null, token, message);
}
public ExpressionSyntaxError(@Nullable Expression child, Token token, String message) {
super(child == null ? token : child.start, token, SeaType.INT);
this.child = child;
this.exception = new ParseException(new ParseException.Message(token, message));
}
@Override
public ParseException exception() {
return exception;
}
}

View File

@@ -0,0 +1,16 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
public final class ExpressionTernary extends Expression {
public final Expression cond;
public final Expression then;
public final Expression otherwise;
public ExpressionTernary(Expression cond, Expression then, Expression otherwise, SeaType type) {
super(cond.start, otherwise.end, type);
this.cond = cond;
this.then = then;
this.otherwise = otherwise;
}
}

View File

@@ -0,0 +1,20 @@
package mtmc.lang.sea.ast;
import mtmc.lang.ParseException;
public final class ExpressionTypeError extends Expression implements Error {
public final Expression inner;
public final ParseException exception;
public ExpressionTypeError(Expression inner, String message) {
super(inner.start, inner.end, inner.type());
this.inner = inner;
this.exception = new ParseException(new ParseException.Message(inner.span(), message));
}
@Override
public ParseException exception() {
return exception;
}
}

View File

@@ -0,0 +1,25 @@
package mtmc.lang.sea.ast;
import mtmc.lang.ParseException;
import mtmc.lang.sea.Token;
public abstract sealed class Statement extends Ast permits StatementBlock, StatementBreak, StatementContinue, StatementDoWhile, StatementExpression, StatementFor, StatementGoto, StatementIf, StatementReturn, StatementSyntaxError, StatementVar, StatementWhile
{
private Token labelAnchor = null;
public Statement(Token start, Token end) {
super(start, end);
}
public void setLabelAnchor(Token labelAnchor) throws ParseException {
if (labelAnchor == null) return;
if (this.labelAnchor != null) {
throw new ParseException(new ParseException.Message(labelAnchor, "this statement has been labeled twice!!"));
}
this.labelAnchor = labelAnchor;
}
public Token getLabelAnchor() {
return labelAnchor;
}
}

View File

@@ -0,0 +1,14 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
import java.util.List;
public final class StatementBlock extends Statement {
public final List<Statement> statements;
public StatementBlock(Token start, List<Statement> children, Token end) {
super(start, end);
this.statements = List.copyOf(children);
}
}

View File

@@ -0,0 +1,9 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementBreak extends Statement {
public StatementBreak(Token breakToken) {
super(breakToken, breakToken);
}
}

View File

@@ -0,0 +1,9 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementContinue extends Statement {
public StatementContinue(Token continueToken) {
super(continueToken, continueToken);
}
}

View File

@@ -0,0 +1,14 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementDoWhile extends Statement {
public final Statement body;
public final Expression condition;
public StatementDoWhile(Token start, Statement body, Expression condition, Token end) {
super(start, end);
this.body = body;
this.condition = condition;
}
}

View File

@@ -0,0 +1,10 @@
package mtmc.lang.sea.ast;
public final class StatementExpression extends Statement {
public final Expression expression;
public StatementExpression(Expression expression) {
super(expression.start, expression.end);
this.expression = expression;
}
}

View File

@@ -0,0 +1,20 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementFor extends Statement {
public final Expression initExpression;
public final StatementVar initStatement;
public final Expression condition;
public final Expression inc;
public final Statement body;
public StatementFor(Token start, Expression initExpression, StatementVar initStatement, Expression condition, Expression inc, Statement body) {
super(start, body.end);
this.initExpression = initExpression;
this.initStatement = initStatement;
this.condition = condition;
this.inc = inc;
this.body = body;
}
}

View File

@@ -0,0 +1,12 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementGoto extends Statement {
public final Token label;
public StatementGoto(Token start, Token label) {
super(start, label);
this.label = label;
}
}

View File

@@ -0,0 +1,16 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementIf extends Statement {
public final Expression condition;
public final Statement body;
public final Statement elseBody;
public StatementIf(Token start, Expression condition, Statement body, Statement elseBody) {
super(start, elseBody == null ? body.end : elseBody.end);
this.condition = condition;
this.body = body;
this.elseBody = elseBody;
}
}

View File

@@ -0,0 +1,12 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementReturn extends Statement {
public final Expression value;
public StatementReturn(Token start, Expression value) {
super(start, value == null ? start : value.end);
this.value = value;
}
}

View File

@@ -0,0 +1,18 @@
package mtmc.lang.sea.ast;
import mtmc.lang.ParseException;
import mtmc.lang.sea.Token;
public final class StatementSyntaxError extends Statement implements SyntaxError {
public final ParseException exception;
public StatementSyntaxError(Token token, ParseException exception) {
super(token, token);
this.exception = exception;
}
@Override
public ParseException exception() {
return exception;
}
}

View File

@@ -0,0 +1,20 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementVar extends Statement {
public final TypeExpr type;
public final Token name;
public final Expression initValue;
public StatementVar(TypeExpr type, Token name, Expression initValue) {
super(type.start, initValue == null ? name : initValue.end);
this.type = type;
this.name = name;
this.initValue = initValue;
}
public String name() {
return name.content();
}
}

View File

@@ -0,0 +1,14 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class StatementWhile extends Statement {
public final Expression condition;
public final Statement body;
public StatementWhile(Token start, Expression condition, Statement body) {
super(start, body.end);
this.condition = condition;
this.body = body;
}
}

View File

@@ -0,0 +1,7 @@
package mtmc.lang.sea.ast;
import mtmc.lang.ParseException;
public interface SyntaxError extends Error {
ParseException exception();
}

View File

@@ -0,0 +1,8 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
public interface TypeDeclaration {
String name();
SeaType type();
}

View File

@@ -0,0 +1,19 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
import java.util.Objects;
public abstract sealed class TypeExpr extends Ast permits TypeExprArray, TypeExprChar, TypeExprInt, TypeExprRef, TypeExprVoid, TypePointer {
private final SeaType type;
public TypeExpr(Token start, Token end, SeaType type) {
super(start, end);
this.type = Objects.requireNonNull(type);
}
public SeaType type() {
return type;
}
}

View File

@@ -0,0 +1,13 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class TypeExprArray extends TypeExpr {
public final TypeExpr inner;
public TypeExprArray(TypeExpr inner, Token end) {
super(inner.start, end, new SeaType.Pointer(inner.type()));
this.inner = inner;
}
}

View File

@@ -0,0 +1,13 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class TypeExprChar extends TypeExpr {
public final Token token;
public TypeExprChar(Token token) {
super(token, token, SeaType.CHAR);
this.token = token;
}
}

View File

@@ -0,0 +1,10 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class TypeExprInt extends TypeExpr {
public TypeExprInt(Token token) {
super(token, token, SeaType.INT);
}
}

View File

@@ -0,0 +1,12 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Token;
public final class TypeExprRef extends TypeExpr {
public final TypeDeclaration decl;
public TypeExprRef(Token name, TypeDeclaration decl) {
super(name, name, decl.type());
this.decl = decl;
}
}

View File

@@ -0,0 +1,10 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class TypeExprVoid extends TypeExpr {
public TypeExprVoid(Token token) {
super(token, token, SeaType.VOID);
}
}

View File

@@ -0,0 +1,13 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.SeaType;
import mtmc.lang.sea.Token;
public final class TypePointer extends TypeExpr {
public final TypeExpr component;
public TypePointer(TypeExpr component, Token star) {
super(component.start, star, new SeaType.Pointer(component.type()));
this.component = component;
}
}

View File

@@ -0,0 +1,22 @@
package mtmc.lang.sea.ast;
import mtmc.lang.sea.Symbol;
import mtmc.lang.sea.Token;
import java.util.LinkedHashMap;
import java.util.List;
public final class Unit extends Ast {
public final String source;
public final String filename;
public final List<Declaration> declarations;
public final LinkedHashMap<String, Symbol> symbols;
public Unit(String filename, String source, List<Declaration> declarations, LinkedHashMap<String, Symbol> globals) {
super(Token.SOF, Token.EOF);
this.source = source;
this.filename = filename;
this.declarations = declarations;
this.symbols = globals;
}
}