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..d6fef756 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 diff --git a/src/CST2AST.rsc b/src/CST2AST.rsc index 052c97a9..1ea2258d 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) | Question q2 <- x0], [cst2ast(q2) | Question q2 <- x1], src=q@\loc); + } + + throw "Not yet implemented "; } AExpr cst2ast(Expr e) { switch (e) { + 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); - - // etc. + 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/Check.rsc b/src/Check.rsc index 3d001720..3df49bac 100644 --- a/src/Check.rsc +++ b/src/Check.rsc @@ -1,8 +1,9 @@ module Check - +import IO; import AST; import Resolve; import Message; // see standard library +import Set; data Type = tint() @@ -17,18 +18,62 @@ 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 \type("boolean"): return tbool(); + case \type("integer"): return tint(); + + default: {return tunknown();} + } } -set[Message] check(AForm f, TEnv tenv, UseDef useDef) { - return {}; +set[Message] check(AForm f, TEnv tenv, UseDef useDef){ + set[Message] msgs = {}; + for(/q:question(str _, AId _, AType _, list[AExpr] _) := f){ + msgs += check(q, tenv, 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 = {}; + // obtain label + str label; + 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 + list[str] labels = [label | /<_, _, label, _> := tenv]; + + if(labels != [label]){ + msgs += warning("" + "Duplicate label"); + } + + // same label, different types + set[Type] types = {t | <_, _, label, Type t> := tenv}; + if(types != {AType2Type(q.\type)}){ + msgs += error("" + "Same label, different type"); + } + + // 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; } // Check operand compatibility with operators. @@ -49,11 +94,66 @@ set[Message] check(AExpr e, TEnv tenv, UseDef useDef) { Type typeOf(AExpr e, TEnv tenv, UseDef useDef) { switch (e) { - case ref(id(_, src = loc u)): + + // etc. + case brackets(AExpr ex): + return typeOf(ex, tenv, useDef); + case not(AExpr ex): + return tbool(); + 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; } - // etc. + case integer(int n): + return tint(); + case boolean(str \bool): + return tbool(); + } return tunknown(); } diff --git a/src/Compile.rsc b/src/Compile.rsc index 1ef2d591..da63fc65 100644 --- a/src/Compile.rsc +++ b/src/Compile.rsc @@ -1,32 +1,291 @@ -module Compile - -import AST; -import Resolve; -import IO; -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, toString(form2html(f))); -} - -HTML5Node form2html(AForm f) { - return html(); -} - -str form2js(AForm f) { - return ""; -} +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/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 "; diff --git a/src/Resolve.rsc b/src/Resolve.rsc index 368dd9fd..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 {}; + return { | /ref(AId id) := f}; } Def defs(AForm f) { - return {}; + 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 a88f4ec1..22602959 100644 --- a/src/Syntax.rsc +++ b/src/Syntax.rsc @@ -8,29 +8,52 @@ 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 - = +syntax Question + = Str Id ":" Type ( "=" Expr )? + | "if" "(" Expr ")" "{" Question* "}" + | "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" + ; diff --git a/src/Transform.rsc b/src/Transform.rsc index c73e82cf..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 @@ -28,8 +30,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 +70,22 @@ 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) { + //locations -> usedef + //find all locations + //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; +}