feature/char-support #3
@@ -427,18 +427,21 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
int line = ctx.start.getLine();
|
||||
int col = ctx.start.getCharPositionInLine();
|
||||
var lhs = visit(ctx.lhs);
|
||||
var rhs = visit(ctx.rhs);
|
||||
var line = ctx.start.getLine();
|
||||
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 {
|
||||
lhs.type.combine(rhs.type);
|
||||
} catch (Exception e) {
|
||||
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.line = line;
|
||||
result.col = col;
|
||||
@@ -447,18 +450,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
int line = ctx.start.getLine();
|
||||
int col = ctx.start.getCharPositionInLine();
|
||||
var lhs = visit(ctx.lhs);
|
||||
var rhs = visit(ctx.rhs);
|
||||
var line = ctx.start.getLine();
|
||||
var col = ctx.start.getCharPositionInLine();
|
||||
|
||||
try {
|
||||
lhs.type.combine(rhs.type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
|
||||
var error = "Can only compare primitives.";
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||
}
|
||||
|
||||
NotEqualityExpression result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
|
||||
var result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
result.line = line;
|
||||
result.col = col;
|
||||
@@ -467,20 +469,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
int line = ctx.start.getLine();
|
||||
int col = ctx.start.getCharPositionInLine();
|
||||
var lhs = visit(ctx.lhs);
|
||||
var rhs = visit(ctx.rhs);
|
||||
var line = ctx.start.getLine();
|
||||
var col = ctx.start.getCharPositionInLine();
|
||||
|
||||
try {
|
||||
lhs.type.combine(rhs.type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
|
||||
var error = "Can only compare primitives.";
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||
}
|
||||
|
||||
checkNumeric(lhs, rhs, line, col);
|
||||
|
||||
LTExpression result = new LTExpression((Expression) lhs, (Expression) rhs);
|
||||
var result = new LTExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
result.line = line;
|
||||
result.col = col;
|
||||
@@ -489,23 +488,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
int line = ctx.start.getLine();
|
||||
int col = ctx.start.getCharPositionInLine();
|
||||
var lhs = visit(ctx.lhs);
|
||||
var rhs = visit(ctx.rhs);
|
||||
var line = ctx.start.getLine();
|
||||
var col = ctx.start.getCharPositionInLine();
|
||||
|
||||
try {
|
||||
lhs.type.combine(rhs.type);
|
||||
} 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.";
|
||||
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
|
||||
var error = "Can only compare primitives.";
|
||||
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.line = line;
|
||||
result.col = col;
|
||||
@@ -514,20 +507,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
int line = ctx.start.getLine();
|
||||
int col = ctx.start.getCharPositionInLine();
|
||||
var lhs = visit(ctx.lhs);
|
||||
var rhs = visit(ctx.rhs);
|
||||
var line = ctx.start.getLine();
|
||||
var col = ctx.start.getCharPositionInLine();
|
||||
|
||||
try {
|
||||
lhs.type.combine(rhs.type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
|
||||
var error = "Can only compare primitives.";
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||
}
|
||||
|
||||
checkNumeric(lhs, rhs, line, col);
|
||||
|
||||
LTEExpression result = new LTEExpression((Expression) lhs, (Expression) rhs);
|
||||
var result = new LTEExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
result.line = line;
|
||||
result.col = col;
|
||||
@@ -536,20 +526,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
int line = ctx.start.getLine();
|
||||
int col = ctx.start.getCharPositionInLine();
|
||||
var lhs = visit(ctx.lhs);
|
||||
var rhs = visit(ctx.rhs);
|
||||
var line = ctx.start.getLine();
|
||||
var col = ctx.start.getCharPositionInLine();
|
||||
|
||||
try {
|
||||
lhs.type.combine(rhs.type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||
if (!lhs.type.isPrimitiveType() || !rhs.type.isPrimitiveType()) {
|
||||
var error = "Can only compare primitives.";
|
||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||
}
|
||||
|
||||
checkNumeric(lhs, rhs, line, col);
|
||||
|
||||
GTEExpression result = new GTEExpression((Expression) lhs, (Expression) rhs);
|
||||
var result = new GTEExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
result.line = line;
|
||||
result.col = col;
|
||||
|
||||
@@ -20,11 +20,15 @@ public class CharType extends PrimitiveType {
|
||||
|
||||
@Override
|
||||
public Type combine(Type that) {
|
||||
if (!this.equals(that)) {
|
||||
throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + getName());
|
||||
if (this.equals(that)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return this;
|
||||
if (that.equals(Type.getIntegerType())) {
|
||||
return this;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + that.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,6 +35,10 @@ public class IntegerType extends NumericType {
|
||||
return Type.getFloatType();
|
||||
}
|
||||
|
||||
if (that.equals(Type.getCharType())) {
|
||||
return Type.getCharType();
|
||||
}
|
||||
|
||||
// Every remaining type will throw a RuntimeException
|
||||
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||
}
|
||||
|
||||
@@ -5,6 +5,44 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
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
|
||||
void shouldNotThrowIfCharLiteralIsAssignedToCharVariable() {
|
||||
// given
|
||||
@@ -57,8 +95,9 @@ public class CharTest {
|
||||
void shouldThrowIfCharIsAssignedToOtherTypedVariable() {
|
||||
// given
|
||||
var tree = Helper.prepareParser("""
|
||||
struct foo { f: float; }
|
||||
function bar(): int {
|
||||
let c: int = 'c';
|
||||
let c: foo = 'c';
|
||||
return 1;
|
||||
}
|
||||
bar();
|
||||
@@ -67,14 +106,14 @@ public class CharTest {
|
||||
|
||||
// when / then
|
||||
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
|
||||
void shouldThrowWhenReturningCharFromOtherTypedFunction() {
|
||||
// given
|
||||
var tree = Helper.prepareParser("""
|
||||
function bar(): int {
|
||||
function bar(): float {
|
||||
return 'c';
|
||||
}
|
||||
bar();
|
||||
@@ -83,7 +122,7 @@ public class CharTest {
|
||||
|
||||
// when / then
|
||||
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
|
||||
@@ -109,9 +148,9 @@ public class CharTest {
|
||||
void shouldThrowWhenAssigningCharToOtherTypedStructField() {
|
||||
// given
|
||||
var tree = Helper.prepareParser("""
|
||||
struct foo { a: int; }
|
||||
struct foo { a: bool; }
|
||||
function bar(): void {
|
||||
let x: foo = create foo(1);
|
||||
let x: foo = create foo(false);
|
||||
x.a = 'c';
|
||||
}
|
||||
bar();
|
||||
@@ -120,6 +159,6 @@ public class CharTest {
|
||||
|
||||
// when / then
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user