From 09baec778c58cfb8dee7cc59dd04aefd89086453 Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Mon, 25 Nov 2019 15:02:15 +0100 Subject: [PATCH 01/16] Finished Syntax during practical --- src/Syntax.rsc | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Syntax.rsc b/src/Syntax.rsc index a88f4ec1..e383623c 100644 --- a/src/Syntax.rsc +++ b/src/Syntax.rsc @@ -11,26 +11,48 @@ start syntax Form = "form" Id "{" Question* "}"; // TODO: question, computed question, block, if-then-else, if-then -syntax Question - = +syntax Question + = Str Id ":" Type ( "=" Expr )? + | "if" "(" Expr ")" "{" Question* "}" ( "else" "{" Question* "}" )? ; // TODO: +, -, *, /, &&, ||, !, >, <, <=, >=, ==, !=, literals (bool, int, str) // Think about disambiguation using priorities and associativity // and use C/Java style precedence rules (look it up on the internet) syntax Expr - = Id \ "true" \ "false" // true/false are reserved keywords. + = "(" Expr ")" + | "!" Expr + > non-assoc (Expr "/" Expr + | Expr "*" Expr) + > non-assoc (Expr "+" Expr + | Expr "-" Expr) + > non-assoc (Expr "\<" Expr + | Expr "\>" Expr + | Expr "\<=" Expr + | Expr "\>=" Expr) + > non-assoc (Expr "==" Expr + | Expr "!=" Expr) + > left Expr "&&" Expr + > left Expr "||" Expr + > Id \ "true" \ "false" // true/false are reserved keywords. + | Int + | Bool ; syntax Type - = ; + = "boolean" + | "integer" + ; -lexical Str = ; +lexical Str = "\""([0-9 A-Z _ a-z]|" ")*[? :]? "\"" ; lexical Int - = ; + = [0-9]*; -lexical Bool = ; +lexical Bool + = "true" + | "false" + ; From f8408b01837972a4a6aaec5521d07cc598c57189 Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Tue, 26 Nov 2019 15:09:37 +0100 Subject: [PATCH 02/16] AST --- .gitignore | 2 +- src/AST.rsc | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index ee3b8a8c..ba3791e9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ /.settings /examples/*.html /examples/*.js - +/.metadata/ \ No newline at end of file diff --git a/src/AST.rsc b/src/AST.rsc index 767e19ed..16786e48 100644 --- a/src/AST.rsc +++ b/src/AST.rsc @@ -9,16 +9,36 @@ module AST data AForm(loc src = |tmp:///|) = form(str name, list[AQuestion] questions) - ; + ; data AQuestion(loc src = |tmp:///|) - ; + = question(str q, AId id, AType type_, list[AExpr] expr) + | cond(AExpr c, list[AQuestion] if_, list[AQuestion] else_) + ; -data AExpr(loc src = |tmp:///|) - = ref(AId id) + data AExpr(loc src = |tmp:///|) + = brackets(AExpr expr) + | not(AExpr expr) + | divide(AExpr expr1, AExpr expr2) + | multiply(AExpr expr1, AExpr expr2) + | add(AExpr expr1, AExpr expr2) + | subtract(AExpr expr1, AExpr expr2) + | less(AExpr expr1, AExpr expr2) + | gtr(AExpr expr1, AExpr expr2) + | leq(AExpr expr1, AExpr expr2) + | geq(AExpr expr1, AExpr expr2) + | eq(AExpr expr1, AExpr expr2) + | neq(AExpr expr1, AExpr expr2) + | and(AExpr expr1, AExpr expr2) + | or(AExpr expr1, AExpr expr2) + | ref(AId id) + | integer(int n) + | boolean(str bool_) ; data AId(loc src = |tmp:///|) = id(str name); -data AType(loc src = |tmp:///|); +data AType(loc src = |tmp:///|) + = type_(str type_); + \ No newline at end of file From 69d520f294ff4d7c5b1e0858bd534b55bf41704d Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Thu, 28 Nov 2019 14:37:40 +0100 Subject: [PATCH 03/16] CST2AST practical w3 --- src/AST.rsc | 8 ++++---- src/CST2AST.rsc | 42 ++++++++++++++++++++++++++++++++++++------ src/Syntax.rsc | 5 +++-- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/AST.rsc b/src/AST.rsc index 16786e48..d6fef756 100644 --- a/src/AST.rsc +++ b/src/AST.rsc @@ -12,8 +12,8 @@ data AForm(loc src = |tmp:///|) ; data AQuestion(loc src = |tmp:///|) - = question(str q, AId id, AType type_, list[AExpr] expr) - | cond(AExpr c, list[AQuestion] if_, list[AQuestion] else_) + = question(str q, AId id, AType \type, list[AExpr] expr) + | cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else) ; data AExpr(loc src = |tmp:///|) @@ -33,12 +33,12 @@ data AQuestion(loc src = |tmp:///|) | or(AExpr expr1, AExpr expr2) | ref(AId id) | integer(int n) - | boolean(str bool_) + | boolean(str \bool) ; data AId(loc src = |tmp:///|) = id(str name); data AType(loc src = |tmp:///|) - = type_(str type_); + = \type(str \type); \ No newline at end of file diff --git a/src/CST2AST.rsc b/src/CST2AST.rsc index 3e9c5692..bada9084 100644 --- a/src/CST2AST.rsc +++ b/src/CST2AST.rsc @@ -5,6 +5,7 @@ import AST; import ParseTree; import String; +import IO; /* * Implement a mapping from concrete syntax trees (CSTs) to abstract syntax trees (ASTs) @@ -18,23 +19,52 @@ import String; AForm cst2ast(start[Form] sf) { Form f = sf.top; // remove layout before and after form - return form("", [], src=f@\loc); + return form("", [cst2ast(q) | q <- f.questions], src=f@\loc); } AQuestion cst2ast(Question q) { - throw "Not yet implemented"; + switch(q) { + case (Question)` : `: + return question("", id("", src=i@\loc), cst2ast(t), [], src=q@\loc); + case (Question)` : = `: + return question("", id("", src=i@\loc), cst2ast(t), [cst2ast(e)], src=q@\loc); + case (Question)`if ( ) { }`: + return cond(cst2ast(expr), [cst2ast(q2) | q2 <- x0], [], src=q@\loc); + case (Question)`if ( ) { } else { }`: + return cond(cst2ast(expr), [cst2ast(q2) | q2 <- x0], [cst2ast(q2) |Question q2 <- x1], src=q@\loc); + } + + throw "Not yet implemented "; } AExpr cst2ast(Expr e) { switch (e) { - case (Expr)``: return ref("", src=x@\loc); - - // etc. + case (Expr)`()`: return brackets(cst2ast(expr), src=e@\loc); + case (Expr)`! ` : return not(cst2ast(expr), src=e@\loc); + case (Expr)` / `: return divide(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` * `: return multiply(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` + `: return add(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` - `: return subtract(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` \< `: return less(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` \> `: return gtr(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` \<= `: return leq(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` \>= `: return geq(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` == `: return eq(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` != `: return neq(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` && `: return and(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)` || `: return or(cst2ast(expr1), cst2ast(expr2), src=e@\loc); + case (Expr)``: return ref(id("", src=x@\loc), src=x@\loc); + case (Expr)``: return integer(toInt(""), src=e@\loc); + case (Expr)``: return boolean(b, src=e@\loc); default: throw "Unhandled expression: "; } } AType cst2ast(Type t) { + switch(t) { + case (Type)`boolean`: return \type("boolean", src = t@\loc); + case (Type)`integer`: return \type("integer", src = t@\loc); + } throw "Not yet implemented"; -} +} \ No newline at end of file diff --git a/src/Syntax.rsc b/src/Syntax.rsc index e383623c..238d3622 100644 --- a/src/Syntax.rsc +++ b/src/Syntax.rsc @@ -8,12 +8,13 @@ extend lang::std::Id; */ start syntax Form - = "form" Id "{" Question* "}"; + = "form" Id name "{" Question* questions "}"; // TODO: question, computed question, block, if-then-else, if-then syntax Question = Str Id ":" Type ( "=" Expr )? - | "if" "(" Expr ")" "{" Question* "}" ( "else" "{" Question* "}" )? + | "if" "(" Expr ")" "{" Question* "}" + | "if" "(" Expr ")" "{" Question* "}" "else" "{" Question* "}" ; // TODO: +, -, *, /, &&, ||, !, >, <, <=, >=, ==, !=, literals (bool, int, str) From 9cbe21478ebd42235fbf96f6a78455a5bbc47bee Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Wed, 4 Dec 2019 11:41:46 +0100 Subject: [PATCH 04/16] implemented defs() and uses() --- src/Resolve.rsc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resolve.rsc b/src/Resolve.rsc index 368dd9fd..ba299190 100644 --- a/src/Resolve.rsc +++ b/src/Resolve.rsc @@ -26,9 +26,9 @@ RefGraph resolve(AForm f) = when Use us := uses(f), Def ds := defs(f); Use uses(AForm f) { - return {}; + return {Use() | /ref(AId id) := f}; } Def defs(AForm f) { - return {}; + return {Def() | /question(str _, AId id, AType _, list[AExpr] _) := f}; } \ No newline at end of file From ec41799e6fbaf9bec7caff127801751fa7f825c1 Mon Sep 17 00:00:00 2001 From: mamameen Date: Thu, 5 Dec 2019 19:19:16 +0530 Subject: [PATCH 05/16] Lab 3 end --- src/CST2AST.rsc | 2 +- src/Check.rsc | 38 +++++++++++++++++++++++++++++++++----- src/Resolve.rsc | 4 ++-- src/Syntax.rsc | 2 +- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/CST2AST.rsc b/src/CST2AST.rsc index bada9084..1ea2258d 100644 --- a/src/CST2AST.rsc +++ b/src/CST2AST.rsc @@ -31,7 +31,7 @@ AQuestion cst2ast(Question q) { case (Question)`if ( ) { }`: return cond(cst2ast(expr), [cst2ast(q2) | q2 <- x0], [], src=q@\loc); case (Question)`if ( ) { } else { }`: - return cond(cst2ast(expr), [cst2ast(q2) | q2 <- x0], [cst2ast(q2) |Question q2 <- x1], src=q@\loc); + return cond(cst2ast(expr), [cst2ast(q2) | Question q2 <- x0], [cst2ast(q2) | Question q2 <- x1], src=q@\loc); } throw "Not yet implemented "; diff --git a/src/Check.rsc b/src/Check.rsc index de63e0c7..4bf68bb7 100644 --- a/src/Check.rsc +++ b/src/Check.rsc @@ -1,5 +1,5 @@ module Check - +import IO; import AST; import Resolve; import Message; // see standard library @@ -17,18 +17,37 @@ alias TEnv = rel[loc def, str name, str label, Type \type]; // To avoid recursively traversing the form, use the `visit` construct // or deep match (e.g., `for (/question(...) := f) {...}` ) TEnv collect(AForm f) { - return {}; + return {< q.src, id.name, label, AType2Type(t)> | /q:question(str label, AId id, AType t, _) := f}; +} + +Type AType2Type(AType at){ + switch(at){ + case (AType)`type(integer)`: return tint(); + case (AType)`type(boolean)`: return tbool(); + default: {return tint();} + } } -set[Message] check(AForm f, TEnv tenv, UseDef useDef) { - return {}; +set[Message] check(AForm f, TEnv tenv, UseDef useDef){ + set[Message] msgs = {}; + //Undefined State + //msgs += {error("Undefined State", u) | + // <- useDef.uses, + // u notin useDef}; + return msgs; } // - produce an error if there are declared questions with the same name but different types. // - duplicate labels should trigger a warning // - the declared type computed questions should match the type of the expression. + set[Message] check(AQuestion q, TEnv tenv, UseDef useDef) { - return {}; + set[Message]msgs = {}; + + msgs += { error("questions with the same name but different types", d) + | <_, loc d> <- useDef.defs + , d notin useDef}; + return msgs; } // Check operand compatibility with operators. @@ -54,6 +73,15 @@ Type typeOf(AExpr e, TEnv tenv, UseDef useDef) { return t; } // etc. + case brackets(AExpr ex): + return typeOf(ex, tenv, useDef); + case not(AExpr ex): + return tbool(); + case add(AExpr ex1, AExpr ex2): { + t1 = typeOf(ex1, tenv, useDef); + t2 = typeOf(ex1, tenv, useDef); + } + } return tunknown(); } diff --git a/src/Resolve.rsc b/src/Resolve.rsc index ba299190..bbc84e9e 100644 --- a/src/Resolve.rsc +++ b/src/Resolve.rsc @@ -26,9 +26,9 @@ RefGraph resolve(AForm f) = when Use us := uses(f), Def ds := defs(f); Use uses(AForm f) { - return {Use() | /ref(AId id) := f}; + return { | /ref(AId id) := f}; } Def defs(AForm f) { - return {Def() | /question(str _, AId id, AType _, list[AExpr] _) := f}; + return { | /question(str _, AId id, AType _, list[AExpr] _) := f}; } \ No newline at end of file diff --git a/src/Syntax.rsc b/src/Syntax.rsc index 238d3622..22602959 100644 --- a/src/Syntax.rsc +++ b/src/Syntax.rsc @@ -14,7 +14,7 @@ start syntax Form syntax Question = Str Id ":" Type ( "=" Expr )? | "if" "(" Expr ")" "{" Question* "}" - | "if" "(" Expr ")" "{" Question* "}" "else" "{" Question* "}" + | "if" "(" Expr ")" "{" Question* "}" "else" "{" Question* "}" ; // TODO: +, -, *, /, &&, ||, !, >, <, <=, >=, ==, !=, literals (bool, int, str) From 3202d9bdef25f90064819c5e256cfc5e9bc7e8c4 Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Mon, 9 Dec 2019 14:50:02 +0100 Subject: [PATCH 06/16] meeting dec 9 --- src/Check.rsc | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Check.rsc b/src/Check.rsc index 4bf68bb7..ab1e8e68 100644 --- a/src/Check.rsc +++ b/src/Check.rsc @@ -3,6 +3,7 @@ import IO; import AST; import Resolve; import Message; // see standard library +import Set; data Type = tint() @@ -21,19 +22,20 @@ TEnv collect(AForm f) { } Type AType2Type(AType at){ + // print(at); switch(at){ - case (AType)`type(integer)`: return tint(); - case (AType)`type(boolean)`: return tbool(); - default: {return tint();} + case \type("boolean"): return tbool(); + case \type("integer"): return tint(); + + default: {return tunknown();} } } set[Message] check(AForm f, TEnv tenv, UseDef useDef){ - set[Message] msgs = {}; - //Undefined State - //msgs += {error("Undefined State", u) | - // <- useDef.uses, - // u notin useDef}; + set[Message] msgs = {}; + for(/q:question(str _, AId _, AType _, list[AExpr] _) := f){ + msgs += check(q, tenv, useDef); + } return msgs; } @@ -42,11 +44,13 @@ set[Message] check(AForm f, TEnv tenv, UseDef useDef){ // - the declared type computed questions should match the type of the expression. set[Message] check(AQuestion q, TEnv tenv, UseDef useDef) { - set[Message]msgs = {}; + set[Message] msgs = {}; + // msgs += error("test", q.src); + set[Type] types = {}; + - msgs += { error("questions with the same name but different types", d) - | <_, loc d> <- useDef.defs - , d notin useDef}; + // idea: pattern match q.name on tenv + // throw error if more than one result return msgs; } @@ -78,9 +82,17 @@ Type typeOf(AExpr e, TEnv tenv, UseDef useDef) { case not(AExpr ex): return tbool(); case add(AExpr ex1, AExpr ex2): { - t1 = typeOf(ex1, tenv, useDef); - t2 = typeOf(ex1, tenv, useDef); + if((typeOf(ex1, tenv, useDef) == tint()) + && (typeOf(ex2, tenv, useDef) == tint())){ + return tint(); + } else { + return tunknown(); + } } + case integer(int n): + return tint(); + case boolean(str \bool): + return tbool(); } return tunknown(); From 3749623ccedd5d39ce55963b17dc885a11a852d9 Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Thu, 12 Dec 2019 09:11:58 +0100 Subject: [PATCH 07/16] finished Check except for one error --- src/Check.rsc | 84 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/src/Check.rsc b/src/Check.rsc index ab1e8e68..860241fa 100644 --- a/src/Check.rsc +++ b/src/Check.rsc @@ -22,7 +22,6 @@ TEnv collect(AForm f) { } Type AType2Type(AType at){ - // print(at); switch(at){ case \type("boolean"): return tbool(); case \type("integer"): return tint(); @@ -46,7 +45,29 @@ set[Message] check(AForm f, TEnv tenv, UseDef useDef){ set[Message] check(AQuestion q, TEnv tenv, UseDef useDef) { set[Message] msgs = {}; // msgs += error("test", q.src); - set[Type] types = {}; + // same label different + // obtain label + str label; + for(/question(str l, _, _, _) := q){ + label = l; + } + + // same label twice + i = 0; + for(/<_, _, label, _> := tenv){ + i += 1; + } + if(i > 1){ + msgs += warning("" + "Duplicate label"); + } + + // same label, different types + set[Type] types = {AType2Type(t) | /question(label, _, AType t, _) := q}; + if(types != {AType2Type(q.\type)}){ + msgs += error("" + "Same label, different type"); + } + // TODO: type of question does not match type of expression + // set[Type] types = {}; // idea: pattern match q.name on tenv @@ -72,23 +93,60 @@ set[Message] check(AExpr e, TEnv tenv, UseDef useDef) { Type typeOf(AExpr e, TEnv tenv, UseDef useDef) { switch (e) { - case ref(str x, src = loc u): - if ( <- useDef, <- tenv) { - return t; - } + // etc. case brackets(AExpr ex): return typeOf(ex, tenv, useDef); case not(AExpr ex): return tbool(); - case add(AExpr ex1, AExpr ex2): { - if((typeOf(ex1, tenv, useDef) == tint()) - && (typeOf(ex2, tenv, useDef) == tint())){ - return tint(); - } else { - return tunknown(); - } + case divide(AExpr expr1, AExpr expr2): { + return tunknown(); + } + case multiply(AExpr expr1, AExpr expr2): { + return tint(); + } + case add(AExpr expr1, AExpr expr2): { + return tint(); } + // case add(AExpr ex1, AExpr ex2): { + // if((typeOf(ex1, tenv, useDef) == tint()) + // && (typeOf(ex2, tenv, useDef) == tint())){ + // return tint(); + // } else { + // return tunknown(); + // } + //} + case subtract(AExpr expr1, AExpr expr2): { + return tint(); + } + case less(AExpr expr1, AExpr expr2): { + return tbool(); + } + case gtr(AExpr expr1, AExpr expr2): { + return tbool(); + } + case leq(AExpr expr1, AExpr expr2): { + return tbool(); + } + case geq(AExpr expr1, AExpr expr2): { + return tbool(); + } + case eq(AExpr expr1, AExpr expr2): { + return tbool(); + } + case neq(AExpr expr1, AExpr expr2): { + return tbool(); + } + case and(AExpr expr1, AExpr expr2): { + return tbool(); + } + case or(AExpr expr1, AExpr expr2): { + return tbool(); + } + case ref(str x, src = loc u): + if ( <- useDef, <- tenv) { + return t; + } case integer(int n): return tint(); case boolean(str \bool): From 7b101703b2bdf9bc1d0848f01a66705f39893309 Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Thu, 12 Dec 2019 10:44:44 +0100 Subject: [PATCH 08/16] implemented Check --- src/Check.rsc | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Check.rsc b/src/Check.rsc index 860241fa..b0dd074f 100644 --- a/src/Check.rsc +++ b/src/Check.rsc @@ -44,20 +44,19 @@ set[Message] check(AForm f, TEnv tenv, UseDef useDef){ set[Message] check(AQuestion q, TEnv tenv, UseDef useDef) { set[Message] msgs = {}; - // msgs += error("test", q.src); - // same label different // obtain label str label; - for(/question(str l, _, _, _) := q){ + Type tp; + list[AExpr] aexpr = []; + for(/question(str l, _, AType t, list[AExpr] axprs) := q){ label = l; + tp = AType2Type(t); + aexpr = axprs; } - // same label twice - i = 0; - for(/<_, _, label, _> := tenv){ - i += 1; - } - if(i > 1){ + list[str] labels = [label | /<_, _, label, _> := tenv]; + + if(labels != [label]){ msgs += warning("" + "Duplicate label"); } @@ -66,12 +65,14 @@ set[Message] check(AQuestion q, TEnv tenv, UseDef useDef) { if(types != {AType2Type(q.\type)}){ msgs += error("" + "Same label, different type"); } - // TODO: type of question does not match type of expression - // set[Type] types = {}; - - // idea: pattern match q.name on tenv - // throw error if more than one result + // type of expression != type of question + if(aexpr != []){ + Type etp = typeOf(aexpr[0], tenv, useDef); + if(etp != tunknown() && etp != tp){ + msgs += error("" + "Type of expression does not match type of question"); + } + } return msgs; } From 6011f58a0ed6f77181c117a870587c0016d9ecc4 Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Thu, 12 Dec 2019 14:51:07 +0100 Subject: [PATCH 09/16] Eval + Check --- src/Check.rsc | 2 +- src/Eval.rsc | 90 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/Check.rsc b/src/Check.rsc index b0dd074f..fabd5426 100644 --- a/src/Check.rsc +++ b/src/Check.rsc @@ -61,7 +61,7 @@ set[Message] check(AQuestion q, TEnv tenv, UseDef useDef) { } // same label, different types - set[Type] types = {AType2Type(t) | /question(label, _, AType t, _) := q}; + set[Type] types = {t | <_, _, label, Type t> := tenv}; if(types != {AType2Type(q.\type)}){ msgs += error("" + "Same label, different type"); } diff --git a/src/Eval.rsc b/src/Eval.rsc index 69df5cbb..553c3f9e 100644 --- a/src/Eval.rsc +++ b/src/Eval.rsc @@ -2,6 +2,8 @@ module Eval import AST; import Resolve; +import Check; +import IO; /* * Implement big-step semantics for QL @@ -27,7 +29,18 @@ data Input // produce an environment which for each question has a default value // (e.g. 0 for int, "" for str etc.) VEnv initialEnv(AForm f) { - return (); + VEnv venv = (); + for(/q:question(str _, AId id, AType at, list[AExpr] _) := f){ + t = AType2Type(at); + if(t == tint()){ + venv += (id.name: vint(0)); + } else if (t == tbool()) { + venv += (id.name: vbool(false)); + } else { + assert false; + } + } + return venv; } @@ -40,19 +53,88 @@ VEnv eval(AForm f, Input inp, VEnv venv) { } VEnv evalOnce(AForm f, Input inp, VEnv venv) { - return (); + // + + + // for(/aq:question(inp.question, AId id, AType _, list[AExpr] _) := f) { + // venv[aq.id] = input.\value; + //} + + + // + + + for(question <- f.questions){ + venv = eval(question, inp, venv); + } + return venv; } VEnv eval(AQuestion q, Input inp, VEnv venv) { // evaluate conditions for branching, // evaluate inp and computed questions to return updated VEnv + switch(q){ + case question(str q, AId id, AType \type, list[AExpr] e): { + if(e != []){ + venv[id.name] = eval(e, venv); + } + return venv; + } + case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else):{ + b = eval(c, venv); + if(b==vbool(true)){ + for(question <- \if){ + venv = eval(question, inp, venv); + } + } else { + for(question <- \else){ + venv = eval(question, inp, venv); + } + } + return venv; + } + } return (); } Value eval(AExpr e, VEnv venv) { switch (e) { - case ref(str x): return venv[x]; - + case brackets(AExpr expr): return eval(expr, venv); + case not(AExpr expr): return eval(expr, venv)==vbool(true)?vbool(false):vbool(true); + case divide(AExpr expr1, AExpr expr2) : + return vint(eval(expr1, venv).n / eval(expr2, venv).n); + case multiply(AExpr expr1, AExpr expr2) : + return vint(eval(expr1, venv).n * eval(expr2, venv).n); + case add(AExpr expr1, AExpr expr2) : + return vint(eval(expr1, venv).n + eval(expr2, venv).n); + case subtract(AExpr expr1, AExpr expr2) : + return vint(eval(expr1, venv).n - eval(expr2, venv).n); + case less(AExpr expr1, AExpr expr2) : + return vbool(eval(expr1, venv).n < eval(expr2, venv).n); + case gtr(AExpr expr1, AExpr expr2) : + return vbool(eval(expr1, venv).n > eval(expr2, venv).n); + case leq(AExpr expr1, AExpr expr2) : + return vbool(eval(expr1, venv).n <= eval(expr2, venv).n); + case geq(AExpr expr1, AExpr expr2) : + return vbool(eval(expr1, venv).n >= eval(expr2, venv).n); + case eq(AExpr expr1, AExpr expr2) : + return vbool(eval(expr1, venv).n == eval(expr2, venv).n); + case neq(AExpr expr1, AExpr expr2) : + return vbool(eval(expr1, venv).n != eval(expr2, venv).n); + case and(AExpr expr1, AExpr expr2) : { + return vbool(eval(expr1, venv).b && eval(expr2, venv).b); + } + case or(AExpr expr1, AExpr expr2) : + return vbool(eval(expr1, venv).b || eval(expr2, venv).b); + case ref(AId id): return venv[id.name]; + case integer(int n): return vint(n); + case boolean(str \bool): { + if(\bool == "true"){ + return vbool(true); + } else { + return vbool(false); + } + } // etc. default: throw "Unsupported expression "; From c75b71120c30fc7ffaf13aabd68d05cc535e5362 Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Thu, 19 Dec 2019 14:49:37 +0100 Subject: [PATCH 10/16] Started form2html() in Compile --- src/Compile.rsc | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Compile.rsc b/src/Compile.rsc index 1ef2d591..95a86a96 100644 --- a/src/Compile.rsc +++ b/src/Compile.rsc @@ -20,11 +20,42 @@ import lang::html5::DOM; // see standard library void compile(AForm f) { writeFile(f.src[extension="js"].top, form2js(f)); - writeFile(f.src[extension="html"].top, toString(form2html(f))); + writeFile(f.src[extension="html"].top, "\\n" + toString(form2html(f))); } HTML5Node form2html(AForm f) { - return html(); + HEAD = head(title(f.name)); + BODY = body( + form([form2html(question) | question <- f.questions] + [input(\type("submit"), \value("Submit"))])); + return html([HEAD, BODY]); +} + +HTML5Node form2html(AQuestion q) { + switch(q){ + case question(str question, AId identifier, AType t, list[AExpr] expr): { + divargs = [class("question"), id(identifier.name)] + [ + question]; + if(expr != []){ + divargs += [hidden("true")]; + } + + if(t.\type == "boolean"){ + divargs += [input(\type("radio"), name(identifier.name), \value("true"), checked("true")), + "True", + input(\type("radio"), name(identifier.name), \value("false")), + "False" + ]; + } else if (t.\type == "integer"){ + divargs += [input(\type("number"), name(identifier.name))]; + } else if (t.\type == "string"){ + divargs += [input(\type("text"), name(identifier.name))]; + } + return div(divargs); + } + case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { + return div([class("conditition")] + [form2html(h) | h <- \if] + [form2html(h) | h <- \else]); + } + }; } str form2js(AForm f) { From 07451b6cc301336fba4d58b71e13042ce2a7e28a Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Mon, 6 Jan 2020 14:38:49 +0100 Subject: [PATCH 11/16] worked on Compile together --- src/Compile.rsc | 124 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 8 deletions(-) diff --git a/src/Compile.rsc b/src/Compile.rsc index 95a86a96..b9141df6 100644 --- a/src/Compile.rsc +++ b/src/Compile.rsc @@ -24,7 +24,7 @@ void compile(AForm f) { } HTML5Node form2html(AForm f) { - HEAD = head(title(f.name)); + HEAD = head([title(f.name), script(src(f.name + ".js"))]); BODY = body( form([form2html(question) | question <- f.questions] + [input(\type("submit"), \value("Submit"))])); return html([HEAD, BODY]); @@ -33,22 +33,21 @@ HTML5Node form2html(AForm f) { HTML5Node form2html(AQuestion q) { switch(q){ case question(str question, AId identifier, AType t, list[AExpr] expr): { - divargs = [class("question"), id(identifier.name)] + [ + divargs = [class("question")] + [ question]; if(expr != []){ divargs += [hidden("true")]; } - if(t.\type == "boolean"){ - divargs += [input(\type("radio"), name(identifier.name), \value("true"), checked("true")), + divargs += [input(\type("radio"), id(identifier.name), \value("true"), checked("true")), "True", - input(\type("radio"), name(identifier.name), \value("false")), + input(\type("radio"), id(identifier.name), \value("false")), "False" ]; } else if (t.\type == "integer"){ - divargs += [input(\type("number"), name(identifier.name))]; + divargs += [input(\type("number"), id(identifier.name))]; } else if (t.\type == "string"){ - divargs += [input(\type("text"), name(identifier.name))]; + divargs += [input(\type("text"), id(identifier.name))]; } return div(divargs); } @@ -58,6 +57,115 @@ HTML5Node form2html(AQuestion q) { }; } +//- What do we want the js to look like? + str form2js(AForm f) { - return ""; + result = ""; + // generate list of question ids + result += genIds(f); + result += "\n"; + + // generate onSubmit() logic + result += genOnSubmit(f); + result += "\n"; + + // generate onChange() logic + result += genOnChange(f); + result += "\n"; + + return result; +} + +//- global list of question ids +str genIds(AForm f){ + ids = "ids = ["; + for(/question(_, id, _, _) := f){ + ids += id.name + ","; + } + return ids + "];"; +} + +//- function onSubmit() +// - send logic +// - create results list +// - iterate over question ids +// - add value if question not hidden +// - send results +str genOnSubmit(AForm f){ + return ""; +} + +//- function onChange() +// - copy list of question ids +// - run compiled js +// - remove question from copylist if: +// - it's id is found +// - hide all questions in your list +str genOnChange(AForm f){ + result = "list = ids;\n"; + // compile conditions here + result += foo(f.questions); + + result += "function onChange(){\n" + + "\tfor(id in list){\n" + + "\t\tdocument.getElementById(id).display=\"none\";\n" + + "\t}\n" + + "}"; + return result; +} + +str foo(list[AQuestion] li){ + result = ""; + for(q <- li){ + switch(q){ + case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { + // TODO: make pretty printer for expressions + result += "if ( " + "" + ") {\n"; + result += foo(\if) + "\n"; + result += "}\n"; + if(\else != []){ + result += "else {"; + result += foo(\else) + "\n"; + result += "}"; + } + } + default :; + }; + } + + return result; } + +// OLD CODE + +// str form2js(AForm f) { +// super_long_string = ""; +// for(question <- f.questions){ +// super_long_string += form2js(question); +// } +// return super_long_string; +//} + +//str form2js(AQuestion q) { +// some_unique_name = ""; +// switch(q){ +// case question(str question, AId identifier, AType t, list[AExpr] expr): { +// ; +// } +// case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { +// // return div([class("conditition")] + [form2html(h) | h <- \if] + [form2html(h) | h <- \else]); +// some_unique_name += "if(" + "" + ") {\n" ; +// for (question_foo <- \if) { +// // recursively hide each question in else +// some_unique_name += form2js(question_foo); +// } +// some_unique_name += "}\n else {\n"; +// for (question_bar <- \else) { +// // recursively hide each question in if +// some_unique_name += form2js(question_bar); +// } +// some_unique_name += "}"; +// } +// }; +// return some_unique_name + "boop"; +//} From 78df8b4f6430bbcc11c36790095ae7ea364fed4e Mon Sep 17 00:00:00 2001 From: Tjaart de Vries Date: Thu, 9 Jan 2020 12:45:49 +0100 Subject: [PATCH 12/16] more work on compile --- src/Compile.rsc | 249 ++++++++++++++++++++++++++++-------------------- 1 file changed, 148 insertions(+), 101 deletions(-) diff --git a/src/Compile.rsc b/src/Compile.rsc index b9141df6..2b9866cc 100644 --- a/src/Compile.rsc +++ b/src/Compile.rsc @@ -20,34 +20,45 @@ import lang::html5::DOM; // see standard library void compile(AForm f) { writeFile(f.src[extension="js"].top, form2js(f)); - writeFile(f.src[extension="html"].top, "\\n" + toString(form2html(f))); + writeFile(f.src[extension="html"].top, "\\n" + toString(form2html(f))); } HTML5Node form2html(AForm f) { - HEAD = head([title(f.name), script(src(f.name + ".js"))]); + // documentation is great, so grabbing filename this way + // will break horribly if file structure is changed :) + filename = src(f.src[extension="js"].top.path[10..]); + + HEAD = head([meta(charset("UTF-8")), title(f.name), script(filename)]); BODY = body( - form([form2html(question) | question <- f.questions] + [input(\type("submit"), \value("Submit"))])); + form([onsubmit("return false")] + [form2html(question) | question <- f.questions] + + [input(\type("submit"), \value("Submit"), onclick("onSubmit();"))]), + p(id("output")) + ); return html([HEAD, BODY]); } HTML5Node form2html(AQuestion q) { switch(q){ case question(str question, AId identifier, AType t, list[AExpr] expr): { - divargs = [class("question")] + [ - question]; + divargs = [class("question")]; if(expr != []){ - divargs += [hidden("true")]; - } - if(t.\type == "boolean"){ - divargs += [input(\type("radio"), id(identifier.name), \value("true"), checked("true")), - "True", - input(\type("radio"), id(identifier.name), \value("false")), - "False" - ]; - } else if (t.\type == "integer"){ - divargs += [input(\type("number"), id(identifier.name))]; - } else if (t.\type == "string"){ - divargs += [input(\type("text"), id(identifier.name))]; + divargs += [id(identifier.name), html5attr("expr", pretty_print(expr))]; + if(t.\type == "boolean"){ + divargs += [html5attr("data-value", "false")]; + } else if (t.\type == "integer"){ + divargs += [html5attr("data-value", 0)]; + } else if (t.\type == "string"){ + divargs += [html5attr("data-value", "")]; + } + } else { + divargs += [question]; + if(t.\type == "boolean"){ + divargs += [input(\type("checkbox"), id(identifier.name), \value("false"), onchange("ev(); updateVisibility();"))]; + } else if (t.\type == "integer"){ + divargs += [input(\type("number"), id(identifier.name), \value(0), onchange("ev(); updateVisibility();"))]; + } else if (t.\type == "string"){ + divargs += [input(\type("text"), id(identifier.name), \value(""), onchange("ev(); updateVisibility();"))]; + } } return div(divargs); } @@ -57,115 +68,151 @@ HTML5Node form2html(AQuestion q) { }; } -//- What do we want the js to look like? +str pretty_print(AExpr c){ + switch(c){ + case brackets(AExpr ex): return "()"; + case not(AExpr ex): return "!" + "()"; + case divide(AExpr ex1, AExpr ex2): return "() / ()"; + case multiply(AExpr ex1, AExpr ex2): return "() * ()"; + case add(AExpr ex1, AExpr ex2): return "() + ()"; + case subtract(AExpr ex1, AExpr ex2): return "() - ()"; + case less(AExpr ex1, AExpr ex2): return "() \< ()"; + case gtr(AExpr ex1, AExpr ex2): return "() \> ()"; + case leq(AExpr ex1, AExpr ex2): return "() \<= ()"; + case geq(AExpr ex1, AExpr ex2): return "() \>= ()"; + case eq(AExpr ex1, AExpr ex2): return "() == ()"; + case neq(AExpr ex1, AExpr ex2): return "() != ()"; + case and(AExpr ex1, AExpr ex2): return "() && ()"; + case or(AExpr ex1, AExpr ex2): return "() || ()"; + case ref(AId id): return "(document.getElementById().value)"; + case integer(int n): return toString(n); + case boolean(str \bool): return \bool; + } +} -str form2js(AForm f) { - result = ""; - // generate list of question ids - result += genIds(f); - result += "\n"; - - // generate onSubmit() logic - result += genOnSubmit(f); - result += "\n"; - - // generate onChange() logic - result += genOnChange(f); - result += "\n"; - - return result; +str form2js(AForm f) { + return "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; } //- global list of question ids str genIds(AForm f){ - ids = "ids = ["; + ids = "var ids = ["; for(/question(_, id, _, _) := f){ - ids += id.name + ","; + ids += "\"" + id.name + "\"" + ","; } - return ids + "];"; + return ids + "];\n"; +} + +str onSubmit(AForm f){ + result = "function onSubmit(){\n" + + "\tvar result = \"\";\n" + + "\tfor(var i in ids){\n" + + "\t\tvar em = document.getElementById(ids[i]);\n" + + "\t\t// handle computed questions before normal questions\n" + + "\t\tif(em.hasAttribute(\"data-value\") && em.visible){\n" + + "\t\t\tresult += ids[i] + \" : \" + em.getAttribute(\"data-value\") + \"\\n\";\n" + + "\t\t} else if(em.type == \"checkbox\"){\n" + + "\t\t\tresult += ids[i] + \" : \" + em.checked + \"\\n\"\n" + + "\t\t} else if((! em.hasAttribute(\"class\")) && em.visible){\n" + + "\t\t\tresult += ids[i] + \" : \" + em.value + \"\\n\";\n" + + "\t\t}\n" + + "\t}\n" + + "\tdocument.getElementById(\"output\").innerHTML = result;\n" + + "}\n"; + return result; } -//- function onSubmit() -// - send logic -// - create results list -// - iterate over question ids -// - add value if question not hidden -// - send results -str genOnSubmit(AForm f){ - return ""; +str evaluate(){ + return "function ev(){\n" + + "\tvar r = evOnce();\n" + + "\tvar s;\n" + + "\twhile(true){\n" + + "\t\ts = evOnce();\n" + + "\t\tif(r == s){\n" + + "\t\t\tbreak;\n" + + "\t\t}\n" + + "\t\tr = s;\n" + + "\t}\n" + + "\treturn r\n" + + "}\n"; } -//- function onChange() -// - copy list of question ids -// - run compiled js -// - remove question from copylist if: -// - it's id is found -// - hide all questions in your list -str genOnChange(AForm f){ - result = "list = ids;\n"; - // compile conditions here - result += foo(f.questions); +str evaluateOnce(){ + return "function evOnce(){\n" + + "\tvar result = \"\";\n" + + "\tfor(var i in ids){\n" + + "\t\tresult += ids[i] + \" : \" + recalculate(ids[i]) + \"\\n\";\n" + + "\t}\n" + + "\treturn result;\n" + + "}\n"; +} + +str getValue(){ + return "function recalculate(id){\n" + + "\tvar em = document.getElementById(id);\n" + + "\tif(em.hasAttribute(\"expr\")) {\n" + + "\t\tvar value = eval(em.expr);\n" + + "\t\tem.setAttribute(\"data-value\", value);\n" + + "\t\treturn value;\n" + + "\t}\n"+ + "\tif(em.type == \"checkbox\"){\n" + + "\t\treturn em.checked;\n" + + "\t}\n" + + "\treturn em.value;\n" + + "}\n"; +} + +str updateVisibility(AForm f){ + return "function updateVisibility(){\n" + + "\t// mark every element as not visible\n" + + "\tfor(i in ids){\n" + + "\t\tdocument.getElementById(ids[i]).visible = false;\n" + + "\t}\n\n" + - result += "function onChange(){\n" + - "\tfor(id in list){\n" + - "\t\tdocument.getElementById(id).display=\"none\";\n" + - "\t}\n" + - "}"; - return result; + "\t// mark visible elements as visible\n" + + "" + + + "\t// hide invisible elements, show visible elements\n" + + "\tfor(i in ids){\n" + + "\t\tvar em = document.getElementById(ids[i]);\n" + + "\t\tif(!em.hasAttribute(\"expr\")){\n" + + "\t\t\tif(em.visible){\n" + + "\t\t\t\tif(em.parentElement.hasAttribute(\"hidden\")){\n" + + "\t\t\t\t\tem.parentElement.removeAttribute(\"hidden\");\n" + + "\t\t\t\t}\n" + + "\t\t\t} else {\n" + + "\t\t\t\tem.parentElement.setAttribute(\"hidden\", \"true\");\n" + + "\t\t\t}\n" + + "\t\t}\n" + + "\t}\n" + + "}\n"; } -str foo(list[AQuestion] li){ + +str visibilityHelper(list[AQuestion] li){ result = ""; for(q <- li){ switch(q){ case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { - // TODO: make pretty printer for expressions - result += "if ( " + "" + ") {\n"; - result += foo(\if) + "\n"; + result += "if ( eval(" + "" + ")) {\n"; + result += "\n"; result += "}\n"; if(\else != []){ result += "else {"; - result += foo(\else) + "\n"; + result += "\n"; result += "}"; } } + case question(str question, AId identifier, AType t, list[AExpr] expr): { + result += "document.getElementById(\"\").visible = true;\n"; + } default :; }; } - return result; -} - -// OLD CODE - -// str form2js(AForm f) { -// super_long_string = ""; -// for(question <- f.questions){ -// super_long_string += form2js(question); -// } -// return super_long_string; -//} - -//str form2js(AQuestion q) { -// some_unique_name = ""; -// switch(q){ -// case question(str question, AId identifier, AType t, list[AExpr] expr): { -// ; -// } -// case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { -// // return div([class("conditition")] + [form2html(h) | h <- \if] + [form2html(h) | h <- \else]); -// some_unique_name += "if(" + "" + ") {\n" ; -// for (question_foo <- \if) { -// // recursively hide each question in else -// some_unique_name += form2js(question_foo); -// } -// some_unique_name += "}\n else {\n"; -// for (question_bar <- \else) { -// // recursively hide each question in if -// some_unique_name += form2js(question_bar); -// } -// some_unique_name += "}"; -// } -// }; -// return some_unique_name + "boop"; -//} +} \ No newline at end of file From 2d8bdc6545756ed0c52b8505e41239bdda3162e4 Mon Sep 17 00:00:00 2001 From: mamameen Date: Thu, 9 Jan 2020 18:41:12 +0530 Subject: [PATCH 13/16] lab session - transform --- src/Transform.rsc | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Transform.rsc b/src/Transform.rsc index c73e82cf..8a55a229 100644 --- a/src/Transform.rsc +++ b/src/Transform.rsc @@ -28,8 +28,37 @@ import AST; * */ + AForm flatten(AForm f) { - return f; + list[AQuestion] aqs = []; + for(question <- f.questions){ + aqs += flatten(question, boolean("true")); + } + return form(f.name, aqs); +} + +list[AQuestion] flatten(AQuestion question, AExpr condition){ + switch(question){ + case question(str q, AId id, AType \type, list[AExpr] expr):{ + if(boolean("true") == condition){ + return [question]; + } + else{ + return [cond(condition, [question], [])]; + } + } + case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else) :{ + AExpr ifcon = and(c, condition); + AExpr elsecon = and(not(c), condition); + list[list[AQuestion]] result = [flatten(qu, ifcon) | qu <- \if] + + [flatten(qu, elsecon) | qu <- \else]; + list[AQuestion] final = []; + for(res <- result){ + final += res; + } + return final; + } + } } /* Rename refactoring: @@ -39,9 +68,10 @@ AForm flatten(AForm f) { * */ - start[Form] rename(start[Form] f, loc useOrDef, str newName, UseDef useDef) { - return f; - } +start[Form] rename(start[Form] f, loc useOrDef, str newName, UseDef useDef) { + + return f; +} From 5315c9992721c51cc3267e3d8fffbbc22e11ce2b Mon Sep 17 00:00:00 2001 From: mamameen Date: Thu, 9 Jan 2020 19:22:08 +0530 Subject: [PATCH 14/16] Compile fixes --- src/Compile.rsc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Compile.rsc b/src/Compile.rsc index 2b9866cc..bb0554d4 100644 --- a/src/Compile.rsc +++ b/src/Compile.rsc @@ -3,6 +3,7 @@ module Compile import AST; import Resolve; import IO; +import util::Math; import lang::html5::DOM; // see standard library /* @@ -42,7 +43,7 @@ HTML5Node form2html(AQuestion q) { case question(str question, AId identifier, AType t, list[AExpr] expr): { divargs = [class("question")]; if(expr != []){ - divargs += [id(identifier.name), html5attr("expr", pretty_print(expr))]; + divargs += [id(identifier.name), html5attr("expr", pretty_print(expr[0]))]; if(t.\type == "boolean"){ divargs += [html5attr("data-value", "false")]; } else if (t.\type == "integer"){ @@ -84,7 +85,7 @@ str pretty_print(AExpr c){ case neq(AExpr ex1, AExpr ex2): return "() != ()"; case and(AExpr ex1, AExpr ex2): return "() && ()"; case or(AExpr ex1, AExpr ex2): return "() || ()"; - case ref(AId id): return "(document.getElementById().value)"; + case ref(AId id): return "(document.getElementById(\"\").value)"; case integer(int n): return toString(n); case boolean(str \bool): return \bool; } @@ -199,7 +200,7 @@ str visibilityHelper(list[AQuestion] li){ for(q <- li){ switch(q){ case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { - result += "if ( eval(" + "" + ")) {\n"; + result += "if ( eval()) {\n"; result += "\n"; result += "}\n"; if(\else != []){ From 44aab1cd176ee51db723c68d878b1980a83bac7c Mon Sep 17 00:00:00 2001 From: mamameen Date: Mon, 13 Jan 2020 17:56:07 +0530 Subject: [PATCH 15/16] commit final version --- src/Compile.rsc | 508 ++++++++++++++++++++++++++-------------------- src/Transform.rsc | 6 + 2 files changed, 296 insertions(+), 218 deletions(-) diff --git a/src/Compile.rsc b/src/Compile.rsc index bb0554d4..da63fc65 100644 --- a/src/Compile.rsc +++ b/src/Compile.rsc @@ -1,219 +1,291 @@ -module Compile - -import AST; -import Resolve; -import IO; -import util::Math; -import lang::html5::DOM; // see standard library - -/* - * Implement a compiler for QL to HTML and Javascript - * - * - assume the form is type- and name-correct - * - separate the compiler in two parts form2html and form2js producing 2 files - * - use string templates to generate Javascript - * - use the HTML5Node type and the `str toString(HTML5Node x)` function to format to string - * - use any client web framework (e.g. Vue, React, jQuery, whatever) you like for event handling - * - map booleans to checkboxes, strings to textfields, ints to numeric text fields - * - be sure to generate uneditable widgets for computed questions! - * - if needed, use the name analysis to link uses to definitions - */ - -void compile(AForm f) { - writeFile(f.src[extension="js"].top, form2js(f)); - writeFile(f.src[extension="html"].top, "\\n" + toString(form2html(f))); -} - -HTML5Node form2html(AForm f) { - // documentation is great, so grabbing filename this way - // will break horribly if file structure is changed :) - filename = src(f.src[extension="js"].top.path[10..]); - - HEAD = head([meta(charset("UTF-8")), title(f.name), script(filename)]); - BODY = body( - form([onsubmit("return false")] + [form2html(question) | question <- f.questions] + - [input(\type("submit"), \value("Submit"), onclick("onSubmit();"))]), - p(id("output")) - ); - return html([HEAD, BODY]); -} - -HTML5Node form2html(AQuestion q) { - switch(q){ - case question(str question, AId identifier, AType t, list[AExpr] expr): { - divargs = [class("question")]; - if(expr != []){ - divargs += [id(identifier.name), html5attr("expr", pretty_print(expr[0]))]; - if(t.\type == "boolean"){ - divargs += [html5attr("data-value", "false")]; - } else if (t.\type == "integer"){ - divargs += [html5attr("data-value", 0)]; - } else if (t.\type == "string"){ - divargs += [html5attr("data-value", "")]; - } - } else { - divargs += [question]; - if(t.\type == "boolean"){ - divargs += [input(\type("checkbox"), id(identifier.name), \value("false"), onchange("ev(); updateVisibility();"))]; - } else if (t.\type == "integer"){ - divargs += [input(\type("number"), id(identifier.name), \value(0), onchange("ev(); updateVisibility();"))]; - } else if (t.\type == "string"){ - divargs += [input(\type("text"), id(identifier.name), \value(""), onchange("ev(); updateVisibility();"))]; - } - } - return div(divargs); - } - case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { - return div([class("conditition")] + [form2html(h) | h <- \if] + [form2html(h) | h <- \else]); - } - }; -} - -str pretty_print(AExpr c){ - switch(c){ - case brackets(AExpr ex): return "()"; - case not(AExpr ex): return "!" + "()"; - case divide(AExpr ex1, AExpr ex2): return "() / ()"; - case multiply(AExpr ex1, AExpr ex2): return "() * ()"; - case add(AExpr ex1, AExpr ex2): return "() + ()"; - case subtract(AExpr ex1, AExpr ex2): return "() - ()"; - case less(AExpr ex1, AExpr ex2): return "() \< ()"; - case gtr(AExpr ex1, AExpr ex2): return "() \> ()"; - case leq(AExpr ex1, AExpr ex2): return "() \<= ()"; - case geq(AExpr ex1, AExpr ex2): return "() \>= ()"; - case eq(AExpr ex1, AExpr ex2): return "() == ()"; - case neq(AExpr ex1, AExpr ex2): return "() != ()"; - case and(AExpr ex1, AExpr ex2): return "() && ()"; - case or(AExpr ex1, AExpr ex2): return "() || ()"; - case ref(AId id): return "(document.getElementById(\"\").value)"; - case integer(int n): return toString(n); - case boolean(str \bool): return \bool; - } -} - -str form2js(AForm f) { - return "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n"; -} - -//- global list of question ids -str genIds(AForm f){ - ids = "var ids = ["; - for(/question(_, id, _, _) := f){ - ids += "\"" + id.name + "\"" + ","; - } - return ids + "];\n"; -} - -str onSubmit(AForm f){ - result = "function onSubmit(){\n" + - "\tvar result = \"\";\n" + - "\tfor(var i in ids){\n" + - "\t\tvar em = document.getElementById(ids[i]);\n" + - "\t\t// handle computed questions before normal questions\n" + - "\t\tif(em.hasAttribute(\"data-value\") && em.visible){\n" + - "\t\t\tresult += ids[i] + \" : \" + em.getAttribute(\"data-value\") + \"\\n\";\n" + - "\t\t} else if(em.type == \"checkbox\"){\n" + - "\t\t\tresult += ids[i] + \" : \" + em.checked + \"\\n\"\n" + - "\t\t} else if((! em.hasAttribute(\"class\")) && em.visible){\n" + - "\t\t\tresult += ids[i] + \" : \" + em.value + \"\\n\";\n" + - "\t\t}\n" + - "\t}\n" + - "\tdocument.getElementById(\"output\").innerHTML = result;\n" + - "}\n"; - return result; -} - -str evaluate(){ - return "function ev(){\n" + - "\tvar r = evOnce();\n" + - "\tvar s;\n" + - "\twhile(true){\n" + - "\t\ts = evOnce();\n" + - "\t\tif(r == s){\n" + - "\t\t\tbreak;\n" + - "\t\t}\n" + - "\t\tr = s;\n" + - "\t}\n" + - "\treturn r\n" + - "}\n"; -} - -str evaluateOnce(){ - return "function evOnce(){\n" + - "\tvar result = \"\";\n" + - "\tfor(var i in ids){\n" + - "\t\tresult += ids[i] + \" : \" + recalculate(ids[i]) + \"\\n\";\n" + - "\t}\n" + - "\treturn result;\n" + - "}\n"; -} - -str getValue(){ - return "function recalculate(id){\n" + - "\tvar em = document.getElementById(id);\n" + - "\tif(em.hasAttribute(\"expr\")) {\n" + - "\t\tvar value = eval(em.expr);\n" + - "\t\tem.setAttribute(\"data-value\", value);\n" + - "\t\treturn value;\n" + - "\t}\n"+ - "\tif(em.type == \"checkbox\"){\n" + - "\t\treturn em.checked;\n" + - "\t}\n" + - "\treturn em.value;\n" + - "}\n"; -} - -str updateVisibility(AForm f){ - return "function updateVisibility(){\n" + - "\t// mark every element as not visible\n" + - "\tfor(i in ids){\n" + - "\t\tdocument.getElementById(ids[i]).visible = false;\n" + - "\t}\n\n" + - - "\t// mark visible elements as visible\n" + - "" + - - "\t// hide invisible elements, show visible elements\n" + - "\tfor(i in ids){\n" + - "\t\tvar em = document.getElementById(ids[i]);\n" + - "\t\tif(!em.hasAttribute(\"expr\")){\n" + - "\t\t\tif(em.visible){\n" + - "\t\t\t\tif(em.parentElement.hasAttribute(\"hidden\")){\n" + - "\t\t\t\t\tem.parentElement.removeAttribute(\"hidden\");\n" + - "\t\t\t\t}\n" + - "\t\t\t} else {\n" + - "\t\t\t\tem.parentElement.setAttribute(\"hidden\", \"true\");\n" + - "\t\t\t}\n" + - "\t\t}\n" + - "\t}\n" + - "}\n"; -} - - -str visibilityHelper(list[AQuestion] li){ - result = ""; - for(q <- li){ - switch(q){ - case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { - result += "if ( eval()) {\n"; - result += "\n"; - result += "}\n"; - if(\else != []){ - result += "else {"; - result += "\n"; - result += "}"; - } - } - case question(str question, AId identifier, AType t, list[AExpr] expr): { - result += "document.getElementById(\"\").visible = true;\n"; - } - default :; - }; - } - return result; +module Compile + +import AST; +import Resolve; +import IO; +import util::Math; +import lang::html5::DOM; // see standard library +import math; +import String; + +/* + * Implement a compiler for QL to HTML and Javascript + * + * - assume the form is type- and name-correct + * - separate the compiler in two parts form2html and form2js producing 2 files + * - use string templates to generate Javascript + * - use the HTML5Node type and the `str toString(HTML5Node x)` function to format to string + * - use any client web framework (e.g. Vue, React, jQuery, whatever) you like for event handling + * - map booleans to checkboxes, strings to textfields, ints to numeric text fields + * - be sure to generate uneditable widgets for computed questions! + * - if needed, use the name analysis to link uses to definitions + */ + +void compile(AForm f) { + writeFile(f.src[extension="js"].top, form2js(f)); + writeFile(f.src[extension="html"].top, "\\n" + toString(form2html(f))); +} + +HTML5Node form2html(AForm f) { + // documentation is great, so grabbing filename this way + // will break horribly if file structure is changed :) + filename = src(f.src[extension="js"].top.path[10..]); + + HEAD = head([meta(charset("UTF-8")), title(f.name), script(filename)]); + BODY = body(onload("ev(); updateVisibility();"), + form([onsubmit("return false")] + [form2html(question) | question <- f.questions] + + [input(\type("submit"), \value("Submit"), onclick("onSubmit();"))]), + p(id("output")) + ); + return html([HEAD, BODY]); +} + +HTML5Node form2html(AQuestion q) { + switch(q){ + case question(str question, AId identifier, AType t, list[AExpr] expr): { + divargs = [class("question")]; + if(expr != []){ + divargs += [id(identifier.name), html5attr("expr", escape_quotes(pretty_print(expr[0])))]; + + if(t.\type == "boolean"){ + divargs += [html5attr("data-value", "false")]; + } else if (t.\type == "integer"){ + divargs += [html5attr("data-value", 0)]; + } else if (t.\type == "string"){ + divargs += [html5attr("data-value", "")]; + } + } else { + divargs += [question]; + if(t.\type == "boolean"){ + divargs += [input(\type("checkbox"), id(identifier.name), onchange("ev(); updateVisibility();"))]; + } else if (t.\type == "integer"){ + divargs += [input(\type("number"), id(identifier.name), \value(0), onchange("ev(); updateVisibility();"))]; + } else if (t.\type == "string"){ + divargs += [input(\type("text"), id(identifier.name), \value(""), onchange("ev(); updateVisibility();"))]; + } + } + return div(divargs); + } + case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { + return div([class("conditition")] + [form2html(h) | h <- \if] + [form2html(h) | h <- \else]); + } + }; +} + +str pretty_print(AExpr c){ + switch(c){ + case brackets(AExpr ex): return "()"; + case not(AExpr ex): return "!" + "()"; + case divide(AExpr ex1, AExpr ex2): return "() / ()"; + case multiply(AExpr ex1, AExpr ex2): return "() * ()"; + case add(AExpr ex1, AExpr ex2): return "() + ()"; + case subtract(AExpr ex1, AExpr ex2): return "() - ()"; + case less(AExpr ex1, AExpr ex2): return "() \< ()"; + case gtr(AExpr ex1, AExpr ex2): return "() \> ()"; + case leq(AExpr ex1, AExpr ex2): return "() \<= ()"; + case geq(AExpr ex1, AExpr ex2): return "() \>= ()"; + case eq(AExpr ex1, AExpr ex2): return "() == ()"; + case neq(AExpr ex1, AExpr ex2): return "() != ()"; + case and(AExpr ex1, AExpr ex2): return "() && ()"; + case or(AExpr ex1, AExpr ex2): return "() || ()"; + + case ref(AId id): return "(getValue(\"\"))"; + case integer(int n): { + + return intToStr(n); + //return toString(n); + // toString(1) seems to bug out, defining own function + } + + case ref(AId id): return "(document.getElementById(\"\").value)"; + case integer(int n): return toString(n); + case boolean(str \bool): return \bool; + } +} + +str intToStr(int n){ + if(n >= 10){ + return intToStr(n / 10) + intToStr(n % 10); + } + if(n == 0){ + return "0"; + } + if(n == 1){ + return "1"; + } + if(n == 2){ + return "2"; + } + if(n == 3){ + return "3"; + } + if(n == 4){ + return "4"; + } + if(n == 5){ + return "5"; + } + if(n == 6){ + return "6"; + } + if(n == 7){ + return "7"; + } + if(n == 8){ + return "8"; + } + if(n == 9){ + return "9"; + } + return ""; +} + +str escape_quotes(str code){ + str result = ""; + for(i<-[0..size(code)]){ + if(code[i] == "\""){ + result += """; + } else { + result += code[i]; + } + } + return result; +} + +str form2js(AForm f) { + return "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; +} + +//- global list of question ids +str gen_ids(AForm f){ + ids = "var ids = ["; + for(/question(_, id, _, _) := f){ + ids += "\"" + id.name + "\"" + ","; + } + return ids + "];\n"; +} + +str on_submit(AForm f){ + result = "function onSubmit(){\n" + + "\tvar result = \"\";\n" + + "\tfor(var i in ids){\n" + + "\t\tvar em = document.getElementById(ids[i]);\n" + + "\t\t// handle computed questions before normal questions\n" + + "\t\tif(em.hasAttribute(\"data-value\") && em.visible){\n" + + "\t\t\tresult += ids[i] + \" : \" + em.getAttribute(\"data-value\") + \"\\n\";\n" + + "\t\t} else if(em.type == \"checkbox\"){\n" + + "\t\t\tresult += ids[i] + \" : \" + em.checked + \"\\n\"\n" + + "\t\t} else if((! em.hasAttribute(\"class\")) && em.visible){\n" + + "\t\t\tresult += ids[i] + \" : \" + getValue(ids[i]) + \"\\n\";\n" + + "\t\t}\n" + + "\t}\n" + + "\tdocument.getElementById(\"output\").innerHTML = result;\n" + + "}\n"; + return result; +} + +str evaluate(){ + return "function ev(){\n" + + "\tvar r = evOnce();\n" + + "\tvar s;\n" + + "\twhile(true){\n" + + "\t\ts = evOnce();\n" + + "\t\tif(r == s){\n" + + "\t\t\tbreak;\n" + + "\t\t}\n" + + "\t\tr = s;\n" + + "\t}\n" + + "\treturn r\n" + + "}\n"; +} + +str evaluateOnce(){ + return "function evOnce(){\n" + + "\tvar result = \"\";\n" + + "\tfor(var i in ids){\n" + + "\t\tresult += ids[i] + \" : \" + recalculate(ids[i]) + \"\\n\";\n" + + "\t}\n" + + "\treturn result;\n" + + "}\n"; +} + +str recalculate(){ + return "function recalculate(id){\n" + + "\tvar em = document.getElementById(id);\n" + + "\tif(em.hasAttribute(\"expr\")) {\n" + + "\t\tvar value = eval(em.getAttribute(\"expr\"));\n" + + "\t\tem.setAttribute(\"data-value\", value);\n" + + "\t\treturn value;\n" + + "\t}\n"+ + "\treturn getValue(id);\n" + + "}\n"; +} + +str get_value(){ + return "function getValue(id){\n" + + "\tem = document.getElementById(id);\n" + + "\tif(em.hasAttribute(\"data-value\")){\n" + + "\t\treturn em.getAttribute(\"data-value\");\n" + + "\t} else if(em.hasAttribute(\"type\") && em.type == \"checkbox\"){\n" + + "\t\treturn em.checked;\n" + + "\t} else {\n" + + "\t\treturn em.value;\n" + + "\t}\n" + + "}\n"; +} + +str update_visibility(AForm f){ + return "function updateVisibility(){\n" + + "\t// mark every element as not visible\n" + + "\tfor(i in ids){\n" + + "\t\tdocument.getElementById(ids[i]).visible = false;\n" + + "\t}\n\n" + + + "\t// mark visible elements as visible\n" + + "" + + + "\t// hide invisible elements, show visible elements\n" + + "\tfor(i in ids){\n" + + "\t\tvar em = document.getElementById(ids[i]);\n" + + "\t\tif(!em.hasAttribute(\"expr\")){\n" + + "\t\t\tif(em.visible){\n" + + "\t\t\t\tif(em.parentElement.hasAttribute(\"hidden\")){\n" + + "\t\t\t\t\tem.parentElement.removeAttribute(\"hidden\");\n" + + "\t\t\t\t}\n" + + "\t\t\t} else {\n" + + "\t\t\t\tem.parentElement.setAttribute(\"hidden\", \"true\");\n" + + "\t\t\t}\n" + + "\t\t}\n" + + "\t}\n" + + "}\n"; +} + + +str visibilityHelper(list[AQuestion] li){ + result = ""; + for(q <- li){ + switch(q){ + case cond(AExpr c, list[AQuestion] \if, list[AQuestion] \else): { + result += "if ( eval()) {\n"; + result += "\n"; + result += "}\n"; + if(\else != []){ + result += "else {"; + result += "\n"; + result += "}"; + } + } + case question(str question, AId identifier, AType t, list[AExpr] expr): { + result += "document.getElementById(\"\").visible = true;\n"; + } + default :; + }; + } + return result; } \ No newline at end of file diff --git a/src/Transform.rsc b/src/Transform.rsc index 8a55a229..36842497 100644 --- a/src/Transform.rsc +++ b/src/Transform.rsc @@ -69,6 +69,12 @@ list[AQuestion] flatten(AQuestion question, AExpr condition){ */ start[Form] rename(start[Form] f, loc useOrDef, str newName, UseDef useDef) { + //locations -> usedef + //find all locations + UseDef required = { | ud <- useDef && (ud.use == useOrDef || ud.def == useOrDef)}; + + + //create a new form with the respective locations renamed return f; } From 97526c87d78d2ceff44122c1bb5abf64b9f83170 Mon Sep 17 00:00:00 2001 From: mamameen Date: Mon, 13 Jan 2020 19:13:52 +0530 Subject: [PATCH 16/16] Changes in transform --- src/Transform.rsc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Transform.rsc b/src/Transform.rsc index 36842497..59d91b4f 100644 --- a/src/Transform.rsc +++ b/src/Transform.rsc @@ -3,6 +3,8 @@ module Transform import Syntax; import Resolve; import AST; +import IO; +import Tree; /* * Transforming QL forms @@ -71,11 +73,17 @@ list[AQuestion] flatten(AQuestion question, AExpr condition){ start[Form] rename(start[Form] f, loc useOrDef, str newName, UseDef useDef) { //locations -> usedef //find all locations - UseDef required = { | ud <- useDef && (ud.use == useOrDef || ud.def == useOrDef)}; - + //print("\n"); + set[loc] locations = {}; + locations += {useOrDef}; + locations += {ud.use | ud <- useDef && ud.def == useOrDef}; + locations += {ud.def | ud <- useDef && ud.use == useOrDef}; + //print(locations); + print("\n..............."); //create a new form with the respective locations renamed - + print(f.top.questions); + print("\n"); return f; }