Skip to content
Snippets Groups Projects
Commit c9dc24d4 authored by Nathan PERIER's avatar Nathan PERIER
Browse files

Added a way to explicitly set the return type of a function (close #22)

parent a31d2647
No related branches found
No related tags found
No related merge requests found
......@@ -12,11 +12,13 @@ import java.util.List;
public class FunctionDef extends VariableDef {
private final List<VarParam> params;
private final boolean recursive;
private Type returnType;
public FunctionDef(String n, Context c, SymbolPosition p, boolean isRecursive) {
super(n, c, p);
params = new ArrayList<>();
recursive = isRecursive;
if(isRecursive) {
ctx.addVariable(this);
}
......@@ -28,6 +30,10 @@ public class FunctionDef extends VariableDef {
return this;
}
public boolean isRecursive() {
return recursive;
}
public int getNbParams() {
return params.size();
}
......@@ -51,7 +57,21 @@ public class FunctionDef extends VariableDef {
return false;
}
FunctionDef fd = (FunctionDef) o;
return name.equals(fd.name) && params.equals(fd.params);
return name.equals(fd.name) && params.equals(fd.params) && returnType.equals(fd.returnType);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder().append("let ");
if(recursive) {
builder.append("rec ");
}
builder.append(name).append(" ");
for(VarParam p : params) {
builder.append("(").append(p.getVar()).append(": ").append(p.getType()).append(") ");
}
builder.append(returnType);
return builder.toString();
}
}
......@@ -41,10 +41,7 @@ public class DefinitionParser {
if(it.peekIs(TokenType.NAME)) {
String name = ((NameToken) it.next()).getName();
Type t;
if(it.peekIs(TokenType.COLON)) {
if(!canDefineType) {
throw new ParsingException("Cannot put an explicit type here", it.peek());
}
if(it.peekIs(TokenType.COLON) && canDefineType) {
it.junk();
t = TypeParser.parse(it, ctx);
} else {
......@@ -55,6 +52,9 @@ public class DefinitionParser {
if(it.junkIf(TokenType.LPAREN)) {
return parseInParen(it, ctx, count);
}
if(it.junkIf(TokenType.UNIT)) {
return new VarParam("()", BuiltinType.unit());
}
throw new ParsingException("Expected parameter definition", it.peek());
}
......@@ -67,8 +67,14 @@ public class DefinitionParser {
public static FunctionDef parseFunctionDef(TokenPeekIterator it, Context ctx, String name, SymbolPosition pos, boolean recursive) {
FunctionDef res = new FunctionDef(name, ctx, pos, recursive);
Counter count = new Counter();
while(it.peekIs(TokenType.NAME) || it.peekIs(TokenType.LPAREN)) {
res.addParam(parseParam(it, ctx, count, true));
while(it.peekIs(TokenType.NAME) || it.peekIs(TokenType.LPAREN) || it.peekIs(TokenType.UNIT)) {
res.addParam(parseParam(it, ctx, count, false));
}
if(it.junkIf(TokenType.COLON)) {
Type t = TypeParser.parse(it, ctx);
res.setReturnType(t);
} else {
res.setReturnType(GenericType.forNumber(count.next()));
}
if(res.getNbParams() == 0) {
throw new ParsingException("Expected parameters for function " + res.getName(), it.peek());
......@@ -91,9 +97,6 @@ public class DefinitionParser {
public static VarParam parseInParen(TokenPeekIterator it, Context ctx, Counter count) {
VarParam res;
if(it.junkIf(TokenType.RPAREN)) {
return new VarParam("()", BuiltinType.unit());
}
if(it.junkIf(TokenType.LPAREN)) {
res = parseInParen(it, ctx, count);
} else {
......
......@@ -13,6 +13,7 @@ import fr.insarennes.nperier.minichamo.lexing.Lexer;
import fr.insarennes.nperier.minichamo.lexing.tokens.TokenType;
import fr.insarennes.nperier.minichamo.utils.SymbolPosition;
import fr.insarennes.nperier.minichamo.utils.TokenPeekIterator;
import org.apache.logging.log4j.core.util.Assert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
......@@ -53,7 +54,8 @@ public class DefinitionParserTest {
TokenPeekIterator it = new Lexer("let f a;;").wrap();
Assertions.assertTrue(it.junkIf(TokenType.LET));
FunctionDef exp = new FunctionDef("f", ctx, SymbolPosition.begin(), false)
.addParam(new VarParam("a", new GenericType("a")));
.addParam(new VarParam("a", new GenericType("a")))
.setReturnType(new GenericType("b"));
VariableDef res = DefinitionParser.parse(it, ctx);
Assertions.assertEquals(exp, res);
Assertions.assertTrue(it.junkIf(TokenType.TERMINATION));
......@@ -67,7 +69,8 @@ public class DefinitionParserTest {
FunctionDef exp = new FunctionDef("f", ctx, SymbolPosition.begin(), false)
.addParam(new VarParam("uno", new GenericType("a")))
.addParam(new VarParam("dos", new GenericType("b")))
.addParam(new VarParam("tres", new GenericType("c")));
.addParam(new VarParam("tres", new GenericType("c")))
.setReturnType(new GenericType("d"));
VariableDef res = DefinitionParser.parse(it, ctx);
Assertions.assertEquals(exp, res);
Assertions.assertTrue(it.junkIf(TokenType.TERMINATION));
......@@ -81,7 +84,8 @@ public class DefinitionParserTest {
FunctionDef exp = new FunctionDef("f", ctx, SymbolPosition.begin(), false)
.addParam(new VarParam("uno", BuiltinType.integer()))
.addParam(new VarParam("dos", BuiltinType.list(BuiltinType.integer())))
.addParam(new VarParam("tres", new GenericType("a")));
.addParam(new VarParam("tres", new GenericType("a")))
.setReturnType(new GenericType("b"));
VariableDef res = DefinitionParser.parse(it, ctx);
Assertions.assertEquals(exp, res);
Assertions.assertTrue(it.junkIf(TokenType.TERMINATION));
......@@ -90,7 +94,7 @@ public class DefinitionParserTest {
@Test
public void testReturnTypeFunction() {
TokenPeekIterator it = new Lexer("let f uno (dos: int list) tres : string;;").wrap();
TokenPeekIterator it = new Lexer("let f uno (dos: int list) (tres) : string;;").wrap();
Assertions.assertTrue(it.junkIf(TokenType.LET));
FunctionDef exp = new FunctionDef("f", ctx, SymbolPosition.begin(), false)
.addParam(new VarParam("uno", new GenericType("a")))
......@@ -110,6 +114,49 @@ public class DefinitionParserTest {
Assertions.assertThrows(ParsingException.class, () -> DefinitionParser.parse(it, ctx));
}
@Test
public void testBadParenthesis() {
TokenPeekIterator it = new Lexer("let f uno dos: int list tres : string;;").wrap();
Assertions.assertTrue(it.junkIf(TokenType.LET));
Assertions.assertThrows(ParsingException.class, () -> DefinitionParser.parse(it, ctx));
}
@Test
public void testRecursiveFunction() {
TokenPeekIterator it = new Lexer("let rec f a;;").wrap();
Assertions.assertTrue(it.junkIf(TokenType.LET));
FunctionDef exp = new FunctionDef("f", ctx, SymbolPosition.begin(), true)
.addParam(new VarParam("a", new GenericType("a")))
.setReturnType(new GenericType("b"));
VariableDef res = DefinitionParser.parse(it, ctx);
Assertions.assertEquals(exp, res);
Assertions.assertTrue(res.getContext().hasVariable("f"));
Assertions.assertEquals(exp, res.getContext().getVariable("f"));
Assertions.assertTrue(it.junkIf(TokenType.TERMINATION));
Assertions.assertFalse(it.hasNext());
}
@Test
public void testMissingArgs() {
TokenPeekIterator it = new Lexer("let rec f;;").wrap();
Assertions.assertTrue(it.junkIf(TokenType.LET));
Assertions.assertThrows(ParsingException.class, () -> DefinitionParser.parse(it, ctx));
}
@Test
public void testUnitParam() {
TokenPeekIterator it = new Lexer("let f (a: unit) ();;").wrap();
Assertions.assertTrue(it.junkIf(TokenType.LET));
FunctionDef exp = new FunctionDef("f", ctx, SymbolPosition.begin(), false)
.addParam(new VarParam("a", BuiltinType.unit()))
.addParam(new VarParam("()", BuiltinType.unit()))
.setReturnType(new GenericType("a"));
VariableDef res = DefinitionParser.parse(it, ctx);
Assertions.assertEquals(exp, res);
Assertions.assertTrue(it.junkIf(TokenType.TERMINATION));
Assertions.assertFalse(it.hasNext());
}
// @Test
// public void testNestedParamsFuncAssign() {
// TokenPeekIterator it = new Lexer("let f (un, ((deux, trois), quatre));;").wrap();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment