feature/char-support #3
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user