feature/char-support #3

Merged
nitrix merged 5 commits from feature/char-support into master 2023-03-24 17:03:24 +01:00
4 changed files with 104 additions and 70 deletions
Showing only changes of commit 259ac49981 - Show all commits

View File

@@ -427,18 +427,21 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) { public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); var lhs = visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); var rhs = visit(ctx.rhs);
int line = ctx.start.getLine(); var line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
// Since there are countless combinations of types that are comparable
// to each other we use Type::combine to figure out if two types
// may be compared to each other
try { try {
lhs.type.combine(rhs.type); lhs.type.combine(rhs.type);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage()); throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
} }
EqualityExpression result = new EqualityExpression((Expression) lhs, (Expression) rhs); var result = new EqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line; result.line = line;
result.col = col; result.col = col;
@@ -447,18 +450,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) { public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); var lhs = visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); var rhs = visit(ctx.rhs);
int line = ctx.start.getLine(); var line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
try { if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
lhs.type.combine(rhs.type); var error = "Can only compare primitives.";
} catch (Exception e) { throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
} }
NotEqualityExpression result = new NotEqualityExpression((Expression) lhs, (Expression) rhs); var result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line; result.line = line;
result.col = col; result.col = col;
@@ -467,20 +469,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) { public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); var lhs = visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); var rhs = visit(ctx.rhs);
int line = ctx.start.getLine(); var line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
try { if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
lhs.type.combine(rhs.type); var error = "Can only compare primitives.";
} catch (Exception e) { throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
} }
checkNumeric(lhs, rhs, line, col); var result = new LTExpression((Expression) lhs, (Expression) rhs);
LTExpression result = new LTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line; result.line = line;
result.col = col; result.col = col;
@@ -489,23 +488,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) { public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); var lhs = visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); var rhs = visit(ctx.rhs);
int line = ctx.start.getLine(); var line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
try { if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
lhs.type.combine(rhs.type); var error = "Can only compare primitives.";
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
if (!lhs.type.isNumericType() || !rhs.type.isNumericType()) {
String error = "Only numeric types are allowed for this expression.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
GTExpression result = new GTExpression((Expression) lhs, (Expression) rhs); var result = new GTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line; result.line = line;
result.col = col; result.col = col;
@@ -514,20 +507,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) { public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); var lhs = visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); var rhs = visit(ctx.rhs);
int line = ctx.start.getLine(); var line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
try { if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
lhs.type.combine(rhs.type); var error = "Can only compare primitives.";
} catch (Exception e) { throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
} }
checkNumeric(lhs, rhs, line, col); var result = new LTEExpression((Expression) lhs, (Expression) rhs);
LTEExpression result = new LTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line; result.line = line;
result.col = col; result.col = col;
@@ -536,20 +526,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) { public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); var lhs = visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); var rhs = visit(ctx.rhs);
int line = ctx.start.getLine(); var line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
try { if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
lhs.type.combine(rhs.type); var error = "Can only compare primitives.";
} catch (Exception e) { throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
} }
checkNumeric(lhs, rhs, line, col); var result = new GTEExpression((Expression) lhs, (Expression) rhs);
GTEExpression result = new GTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line; result.line = line;
result.col = col; result.col = col;

View File

@@ -20,11 +20,15 @@ public class CharType extends PrimitiveType {
@Override @Override
public Type combine(Type that) { public Type combine(Type that) {
if (!this.equals(that)) { if (this.equals(that)) {
throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + getName()); return this;
} }
return this; if (that.equals(Type.getIntegerType())) {
return this;
}
throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + that.getName());
} }
@Override @Override

View File

@@ -35,6 +35,10 @@ public class IntegerType extends NumericType {
return Type.getFloatType(); return Type.getFloatType();
} }
if (that.equals(Type.getCharType())) {
return Type.getCharType();
}
// Every remaining type will throw a RuntimeException // Every remaining type will throw a RuntimeException
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
} }

View File

@@ -5,6 +5,44 @@ import static org.junit.jupiter.api.Assertions.*;
public class CharTest { public class CharTest {
@Test
void shouldNotThrowIfComparingCharsEquality() {
// given
var tree = Helper.prepareParser("""
function bar(): int {
let b: char = 'b';
if ('a' == b) {
return 1;
}
return -1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfComparingCharsInequality() {
// given
var tree = Helper.prepareParser("""
function bar(): int {
let b: char = 'b';
if ('a' > b) {
return 1;
}
return -1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test @Test
void shouldNotThrowIfCharLiteralIsAssignedToCharVariable() { void shouldNotThrowIfCharLiteralIsAssignedToCharVariable() {
// given // given
@@ -57,8 +95,9 @@ public class CharTest {
void shouldThrowIfCharIsAssignedToOtherTypedVariable() { void shouldThrowIfCharIsAssignedToOtherTypedVariable() {
// given // given
var tree = Helper.prepareParser(""" var tree = Helper.prepareParser("""
struct foo { f: float; }
function bar(): int { function bar(): int {
let c: int = 'c'; let c: foo = 'c';
return 1; return 1;
} }
bar(); bar();
@@ -67,14 +106,14 @@ public class CharTest {
// when / then // when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 2:4 Type mismatch: cannot combine int and char", e.getMessage()); assertEquals("Error in line 3:4 Type mismatch: cannot combine foo and char", e.getMessage());
} }
@Test @Test
void shouldThrowWhenReturningCharFromOtherTypedFunction() { void shouldThrowWhenReturningCharFromOtherTypedFunction() {
// given // given
var tree = Helper.prepareParser(""" var tree = Helper.prepareParser("""
function bar(): int { function bar(): float {
return 'c'; return 'c';
} }
bar(); bar();
@@ -83,7 +122,7 @@ public class CharTest {
// when / then // when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 2:4 Type mismatch: cannot combine int and char", e.getMessage()); assertEquals("Error in line 2:4 Type mismatch: cannot combine float and char", e.getMessage());
} }
@Test @Test
@@ -109,9 +148,9 @@ public class CharTest {
void shouldThrowWhenAssigningCharToOtherTypedStructField() { void shouldThrowWhenAssigningCharToOtherTypedStructField() {
// given // given
var tree = Helper.prepareParser(""" var tree = Helper.prepareParser("""
struct foo { a: int; } struct foo { a: bool; }
function bar(): void { function bar(): void {
let x: foo = create foo(1); let x: foo = create foo(false);
x.a = 'c'; x.a = 'c';
} }
bar(); bar();
@@ -120,6 +159,6 @@ public class CharTest {
// when / then // when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 4:4 Type mismatch: cannot combine int and char", e.getMessage()); assertEquals("Error in line 4:4 Type mismatch: cannot combine bool and char", e.getMessage());
} }
} }