diff --git a/pom.xml b/pom.xml
index a16fbbb..19184a3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -95,6 +95,7 @@
Parser
SymbolConstants
true
+ 1
true
diff --git a/src/main/cup/parser.cup b/src/main/cup/parser.cup
index ef83e71..78e48fb 100644
--- a/src/main/cup/parser.cup
+++ b/src/main/cup/parser.cup
@@ -45,9 +45,9 @@ terminal String ID;
terminal PLUS, MINUS, TIMES, DIV, UMINUS;
terminal AND, OR;
terminal LPAREN, RPAREN;
-terminal COMMA, SEMICOLON;
+terminal COMMA, SEMICOLON, ASSIGN;
terminal VAR, EQ, COLON;
-terminal LET, IN;
+terminal LET, IN, IF, THEN, ELSE;
non terminal Exp program;
non terminal Exp exp;
@@ -84,6 +84,9 @@ exp ::=
| var:v {: RESULT = new ExpVar(loc(vxleft,vxright), v); :}
| LET:l decs:ds IN exp:b {: RESULT = new ExpLet(loc(lxleft,bxright), ds, b); :}
| LPAREN:l expseq:es RPAREN:r {: RESULT = new ExpSeq(loc(lxleft,rxright), es); :}
+| var:v ASSIGN exp:e {: RESULT = new ExpAssign(loc(vxleft,exright), v, e);:}
+| IF:i exp:t THEN exp:e1 {: RESULT = new ExpIf(loc(ixleft,e1xright), t, e1, null);:}
+| IF:i exp:t THEN exp:e1 ELSE exp:e2 {: RESULT = new ExpIf(loc(ixleft,e2xright), t, e1, e2);:}
;
exps ::=
diff --git a/src/main/java/absyn/ExpAssign.java b/src/main/java/absyn/ExpAssign.java
new file mode 100644
index 0000000..f1f780e
--- /dev/null
+++ b/src/main/java/absyn/ExpAssign.java
@@ -0,0 +1,37 @@
+package absyn;
+
+import env.Env;
+import javaslang.collection.Tree;
+import parse.Loc;
+import types.Type;
+
+import static semantic.SemanticHelper.typeMismatch;
+
+public class ExpAssign extends Exp {
+
+ public final Var variable;
+ public final Exp exp;
+
+ public ExpAssign(Loc loc, Var variable, Exp exp) {
+ super(loc);
+ this.variable = variable;
+ this.exp = exp;
+ }
+
+ @Override
+ public Tree.Node toTree() {
+ return Tree.of(annotateType("ExpAssign"),
+ variable.toTree(),
+ exp.toTree());
+ }
+
+ @Override
+ protected Type semantic_(Env env) {
+ Type t_var = variable.semantic(env);
+ Type t_exp = exp.semantic_(env);
+ if (!t_exp.is(t_var)) {
+ throw typeMismatch(exp.loc, t_exp, t_var);
+ }
+ return t_var;
+ }
+}
diff --git a/src/main/java/absyn/ExpIf.java b/src/main/java/absyn/ExpIf.java
new file mode 100644
index 0000000..ce1a321
--- /dev/null
+++ b/src/main/java/absyn/ExpIf.java
@@ -0,0 +1,60 @@
+package absyn;
+
+import env.Env;
+import javaslang.collection.List;
+import javaslang.collection.Tree;
+import parse.Loc;
+import types.BOOL;
+import types.Type;
+import types.UNIT;
+
+import static semantic.SemanticHelper.typeMismatch;
+
+public class ExpIf extends Exp {
+
+ public final Exp test;
+ public final Exp e1;
+ public final Exp e2;
+
+ public ExpIf(Loc loc, Exp test, Exp e1, Exp e2) {
+ super(loc);
+ this.test = test;
+ this.e1 = e1;
+ this.e2 = e2;
+ }
+
+ @Override
+ public Tree.Node toTree() {
+ List> children = List.of(test.toTree());
+ children = children.append(e1.toTree());
+ if (e2 != null)
+ children = children.append(e2.toTree());
+ return Tree.of("ExpIf", children);
+ }
+
+ @Override
+ protected Type semantic_(Env env) {
+ Type t_test = test.semantic(env);
+ if (!t_test.is(BOOL.T)) {
+ throw typeMismatch(test.loc, t_test, BOOL.T);
+ }
+
+ Type t_e1 = e1.semantic(env);
+ if (e2 != null) {
+ Type t_e2 = e2.semantic(env);
+ if (!t_e1.is(t_e2)) {
+ if (!t_e2.is(t_e1)) {
+ throw typeMismatch(e2.loc, t_e2, t_e1);
+ } else {
+ return t_e2;
+ }
+ }
+ return t_e1;
+ } else {
+ if (!t_e1.is(UNIT.T)) {
+ throw typeMismatch(e1.loc, t_e1, UNIT.T);
+ }
+ return t_e1;
+ }
+ }
+}
diff --git a/src/main/jflex/lexer.jflex b/src/main/jflex/lexer.jflex
index 0e40df5..4f7e0f0 100644
--- a/src/main/jflex/lexer.jflex
+++ b/src/main/jflex/lexer.jflex
@@ -80,6 +80,9 @@ id = [a-zA-Z][a-zA-Z0-9_]*
var { return tok(VAR); }
let { return tok(LET); }
in { return tok(IN); }
+if { return tok(IF); }
+then { return tok(THEN); }
+else { return tok(ELSE); }
{id} { return tok(ID, yytext().intern()); }
@@ -95,5 +98,6 @@ in { return tok(IN); }
";" { return tok(SEMICOLON); }
":" { return tok(COLON); }
"=" { return tok(EQ); }
+":=" { return tok(ASSIGN); }
. { throw error(Loc.loc(locLeft()), "unexpected char '%s'", yytext()); }
diff --git a/src/test/java/Test/SemantTest.java b/src/test/java/Test/SemantTest.java
index dd734fe..24c3a83 100644
--- a/src/test/java/Test/SemantTest.java
+++ b/src/test/java/Test/SemantTest.java
@@ -16,77 +16,101 @@
public class SemantTest {
- private Type runSemantic(String input) throws Exception {
- Lexer lexer = new Lexer(new StringReader(input), "unknown");
- Parser parser = new Parser(lexer);
- Symbol program = parser.parse();
- Exp parseTree = (Exp) program.value;
- return parseTree.semantic(new Env());
- }
+ private Type runSemantic(String input) throws Exception {
+ Lexer lexer = new Lexer(new StringReader(input), "unknown");
+ Parser parser = new Parser(lexer);
+ Symbol program = parser.parse();
+ Exp parseTree = (Exp) program.value;
+ return parseTree.semantic(new Env());
+ }
- private void trun(String input, Type type) {
- try {
- softly.assertThat(runSemantic(input))
- .as("%s", input)
- .isEqualTo(type);
- }
- catch (Exception e) {
- e.printStackTrace();
- }
- }
+ private void trun(String input, Type type) {
+ try {
+ softly.assertThat(runSemantic(input))
+ .as("%s", input)
+ .isEqualTo(type);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
- private void erun(String input, String message) throws IOException {
- softly.assertThatThrownBy(() -> runSemantic(input))
- .as("%s", input)
- .isInstanceOf(CompilerError.class)
- .hasToString(message);
- }
+ private void erun(String input, String message) throws IOException {
+ softly.assertThatThrownBy(() -> runSemantic(input))
+ .as("%s", input)
+ .isInstanceOf(CompilerError.class)
+ .hasToString(message);
+ }
- @Rule
- public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
+ @Rule
+ public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
- @Test
- public void testLiterals() throws Exception {
- trun("true", BOOL.T);
- trun("123", INT.T);
- trun("12.34", REAL.T);
- }
+ @Test
+ public void testLiterals() throws Exception {
+ trun("true", BOOL.T);
+ trun("123", INT.T);
+ trun("12.34", REAL.T);
+ }
- @Test
- public void testFunctionCall() throws Exception {
- erun("fat(9)",
- "error.CompilerError: 1/1-1/7 undefined function 'fat'");
- erun("fat(g(), h())",
- "error.CompilerError: 1/5-1/8 undefined function 'g'");
- trun("print_int(123)",
- UNIT.T);
- erun("print_int(true)",
- "error.CompilerError: 1/11-1/15 type mismatch: found bool but expected int");
- erun("print_int(123, true, f())",
- "error.CompilerError: 1/22-1/25 undefined function 'f'");
- erun("print_int()",
- "error.CompilerError: 1/1-1/12 too few arguments in call to 'print_int'");
- }
+ @Test
+ public void testFunctionCall() throws Exception {
+ erun("fat(9)",
+ "error.CompilerError: 1/1-1/7 undefined function 'fat'");
+ erun("fat(g(), h())",
+ "error.CompilerError: 1/5-1/8 undefined function 'g'");
+ trun("print_int(123)",
+ UNIT.T);
+ erun("print_int(true)",
+ "error.CompilerError: 1/11-1/15 type mismatch: found bool but expected int");
+ erun("print_int(123, true, f())",
+ "error.CompilerError: 1/22-1/25 undefined function 'f'");
+ erun("print_int()",
+ "error.CompilerError: 1/1-1/12 too few arguments in call to 'print_int'");
+ }
- @Test
- public void testSequence() throws Exception {
- trun("()", UNIT.T);
- trun("(true)", BOOL.T);
- trun("(print_int(23); 2.3)", REAL.T);
- }
+ @Test
+ public void testSequence() throws Exception {
+ trun("()", UNIT.T);
+ trun("(true)", BOOL.T);
+ trun("(print_int(23); 2.3)", REAL.T);
+ }
- @Test
- public void testSimpleVariableAndLet() throws Exception {
- erun("x",
- "error.CompilerError: 1/1-1/2 undefined variable 'x'");
- trun("let var x: int = 10 in x",
- INT.T);
- trun("let var x = 0.56 in x",
- REAL.T);
- erun("let var x: int = 3.4 in x",
- "error.CompilerError: 1/18-1/21 type mismatch: found real but expected int");
- erun("(let var x = 5 in print_int(x); x)",
- "error.CompilerError: 1/33-1/34 undefined variable 'x'");
- }
+ @Test
+ public void testSimpleVariableAndLet() throws Exception {
+ erun("x",
+ "error.CompilerError: 1/1-1/2 undefined variable 'x'");
+ trun("let var x: int = 10 in x",
+ INT.T);
+ trun("let var x = 0.56 in x",
+ REAL.T);
+ erun("let var x: int = 3.4 in x",
+ "error.CompilerError: 1/18-1/21 type mismatch: found real but expected int");
+ erun("(let var x = 5 in print_int(x); x)",
+ "error.CompilerError: 1/33-1/34 undefined variable 'x'");
+ }
+ @Test
+ public void testExpAssign() throws Exception {
+ trun("let var x = 5.2 in x:=6.6", REAL.T);
+ trun("let var x = 5 var y = 4 in x := 2*y", INT.T);
+ trun("let var x = 5.2 var y = 4.3 in x := 2*y", REAL.T);
+ trun("let var x = false in x := true", BOOL.T);
+ trun("let var x = 5 in x:=2*x", INT.T);
+ erun("let var x = 5.2 in z := x",
+ "error.CompilerError: 1/20-1/21 undefined variable 'z'");
+ erun("let var x = 5.2 in x:=true",
+ "error.CompilerError: 1/23-1/27 type mismatch: found bool but expected real");
+ }
+
+ @Test
+ public void testExpIf() throws Exception {
+ trun("if true then print_int(4)", UNIT.T);
+ trun("if false then 4/4 else 3/2", INT.T);
+ trun("if not(true && false) then print_int(5) else print_real(4.3)", UNIT.T);
+ erun("if false then true else 3/2",
+ "error.CompilerError: 1/25-1/28 type mismatch: found int but expected bool");
+ erun("if true then 4*5",
+ "error.CompilerError: 1/14-1/17 type mismatch: found int but expected unit");
+ erun("if 2+2 then print_int(4)",
+ "error.CompilerError: 1/4-1/7 type mismatch: found int but expected bool");
+ }
}