{"action":"created","comment":{"url":"https://api.github.com/repos/oppia/oppia/pulls/comments/47422699","id":47422699,"diff_hunk":"@@ -0,0 +1,434 @@\n+// Copyright 2014 The Oppia Authors. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//      http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS-IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+oppia.factory('expressionSyntaxTreeService', ['$log', 'expressionParserService',\n+    function($log, expressionParserService) {\n+      // Exceptions that can be thrown from the evaluation of expressions.\n+      var ExpressionError = function() {\n+      };\n+      ExpressionError.prototype = new Error();\n+      ExpressionError.prototype.constructor = ExpressionError;\n+\n+      var ExprUndefinedVarError = function(varname, envs) {\n+        this.varname = varname;\n+        this.envs = envs;\n+      };\n+      ExprUndefinedVarError.prototype = new ExpressionError();\n+      ExprUndefinedVarError.prototype.constructor = ExprUndefinedVarError;\n+      ExprUndefinedVarError.prototype.name = 'ExprUndefinedVarError';\n+      ExprUndefinedVarError.prototype.toString = function() {\n+        return this.name + ': ' + this.varname + ' not found in ' + this.envs;\n+      };\n+\n+      var ExprWrongNumArgsError = function(args, expectedMin, expectedMax) {\n+        this.args = args;\n+        this.expectedMin = expectedMin;\n+        this.expectedMax = expectedMax;\n+      };\n+      ExprWrongNumArgsError.prototype = new ExpressionError();\n+      ExprWrongNumArgsError.prototype.constructor = ExprWrongNumArgsError;\n+      ExprWrongNumArgsError.prototype.name = 'ExprWrongNumArgsError';\n+      ExprWrongNumArgsError.prototype.toString = function() {\n+        return this.name + ': {' + this.args + '} not in range [' +\n+          this.expectedMin + ',' + this.expectedMax + ']';\n+      };\n+\n+      var ExprWrongArgTypeError = function(arg, actualType, expectedType) {\n+        this.arg = arg;\n+        this.actualType = actualType;\n+        this.expectedType = expectedType;\n+      };\n+      ExprWrongArgTypeError.prototype = new ExpressionError();\n+      ExprWrongArgTypeError.prototype.constructor = ExprWrongArgTypeError;\n+      ExprWrongArgTypeError.prototype.name = 'ExprWrongArgTypeError';\n+      ExprWrongArgTypeError.prototype.toString = function() {\n+        if (this.arg == null) {\n+          return this.name + ': Type ' + this.actualType +\n+          ' does not match expected type ' + this.expectedType;\n+        }\n+        return this.name + ': ' + this.arg + ' has type ' + this.actualType +\n+          ' which does not match expected type ' + this.expectedType;\n+      };\n+\n+      var getParamsUsedInExpression = function(expression) {\n+        var _findParams = function(parseTree) {\n+          var paramsFound = [];\n+\n+          if (parseTree instanceof Array) {\n+            if (parseTree[0] === '#') {\n+              paramsFound.push(parseTree[1]);\n+            } else {\n+              for (var i = 1; i < parseTree.length; i++) {\n+                paramsFound = paramsFound.concat(_findParams(parseTree[i]));\n+              }\n+            }\n+          }\n+\n+          var uniqueParams = [];\n+          for (var i = 0; i < paramsFound.length; i++) {\n+            if (uniqueParams.indexOf(paramsFound[i]) === -1) {\n+              uniqueParams.push(paramsFound[i]);\n+            }\n+          }\n+\n+          return uniqueParams.sort();\n+        };\n+\n+        var parsed = expressionParserService.parse(expression);\n+        return _findParams(parsed);\n+      };\n+\n+      // Checks if the args array has the expectedNum number of elements an\n+      // throws an error if not. If optional expectedMax is specified, it\n+      // verifies the number of args is in [expectedNum, expectedMax] range\n+      // inclusive.\n+      var verifyNumArgs = function(args, expectedNum, expectedMax) {\n+        if (expectedMax === undefined) {\n+          expectedMax = expectedNum;\n+        }\n+        if (args.length >= expectedNum && args.length <= expectedMax) {\n+          return;\n+        }\n+        throw new ExprWrongNumArgsError(args, expectedNum, expectedMax);\n+      };\n+\n+      var _verifyArgsHaveType = function(args, expectedType) {\n+        for (var i = 0; i < args.length; i++) {\n+          if (args[i] != expectedType) {\n+            throw new ExprWrongArgTypeError(null, args[i], expectedType);\n+          }\n+        }\n+        return true;\n+      };\n+\n+      var _verifyArgsHaveSameType = function(arg1, arg2) {\n+        if (arg1 != arg2) {\n+          throw new ExprWrongArgTypeError(null, arg1, arg2);\n+        }\n+        return true;\n+      };\n+\n+      var evaluateExpression = function(expression, envs, evaluate) {\n+        return evaluateParseTree(expressionParserService.parse(expression),\n+          envs, evaluate);\n+      };\n+\n+      var evaluateParseTree = function(parsed, envs, evaluate) {\n+        return evaluate(parsed, envs.concat(system));\n+      };\n+\n+      /**\n+       * Looks up a variable of the given name in the env. Here the variable\n+       * can be system or user defined functions and parameters, as well as\n+       * system operators.\n+       * @param {string} name The name to look up.\n+       * @param {!Array.<!Object>} envs Represents a nested name space\n+       *     environment to look up the name in. The first element is looked up\n+       *     first (i.e. has higher precedence).\n+       * @throws {ExprUndefinedVarError} The named variable was not found in\n+       *     the given environment.\n+       */\n+      var lookupEnvs = function(name, envs) {\n+        // Parameter value look up.\n+        var value;\n+        if (envs.some(function(env) {\n+          if (env.hasOwnProperty(name)) {\n+            value = env[name];\n+            return true;\n+          }\n+          return false;\n+        })) {\n+          return value;\n+        }\n+\n+        throw new ExprUndefinedVarError(name, envs);\n+      };\n+\n+      // Coerces the argument to a Number, and throws an error if the result\n+      // is NaN.\n+      var _coerceToNumber = function(originalValue) {\n+        var coercedValue = (+originalValue);\n+        if (!isNaN(coercedValue)) {\n+          return coercedValue;\n+        }\n+        throw new ExprWrongArgTypeError(\n+          originalValue, typeof originalValue, 'Number');\n+      };\n+\n+      // Coerces all values in the given argument array to Number, and throws\n+      // an error if the result is NaN.\n+      var _coerceAllArgsToNumber = function(args) {\n+        for (var i = 0; i < args.length; i++) {\n+          args[i] = _coerceToNumber(args[i]);\n+        }\n+        return args;\n+      };\n+\n+      // NOTE TO DEVELOPERS: When adding a new reserved word to this object,\n+      //   please first ensure that existing explorations do not use this\n+      //   parameter name. Also, to prevent future explorations using it,\n+      //   modify feconf.INVALID_PARAMETER_NAMES accordingly.\n+      // TODO(kashida): Document all operators input and output contracts.\n+      // Arguments:\n+      // args: the values that need to be evaluated by these operators","path":"core/templates/dev/head/expressions/expressionSyntaxTree.js","position":183,"original_position":183,"commit_id":"199c3c53a7e3395d857ab3a42641e4ceec576dea","original_commit_id":"199c3c53a7e3395d857ab3a42641e4ceec576dea","user":{"login":"sfederwisch","id":13838973,"avatar_url":"https://avatars.githubusercontent.com/u/13838973?v=3","gravatar_id":"","url":"https://api.github.com/users/sfederwisch","html_url":"https://github.com/sfederwisch","followers_url":"https://api.github.com/users/sfederwisch/followers","following_url":"https://api.github.com/users/sfederwisch/following{/other_user}","gists_url":"https://api.github.com/users/sfederwisch/gists{/gist_id}","starred_url":"https://api.github.com/users/sfederwisch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sfederwisch/subscriptions","organizations_url":"https://api.github.com/users/sfederwisch/orgs","repos_url":"https://api.github.com/users/sfederwisch/repos","events_url":"https://api.github.com/users/sfederwisch/events{/privacy}","received_events_url":"https://api.github.com/users/sfederwisch/received_events","type":"User","site_admin":false},"body":"They are slightly different. I've clarified the documentation.","created_at":"2015-12-12T00:52:39Z","updated_at":"2015-12-12T00:52:39Z","html_url":"https://github.com/oppia/oppia/pull/1134#discussion_r47422699","pull_request_url":"https://api.github.com/repos/oppia/oppia/pulls/1134","_links":{"self":{"href":"https://api.github.com/repos/oppia/oppia/pulls/comments/47422699"},"html":{"href":"https://github.com/oppia/oppia/pull/1134#discussion_r47422699"},"pull_request":{"href":"https://api.github.com/repos/oppia/oppia/pulls/1134"}}},"pull_request":{"url":"https://api.github.com/repos/oppia/oppia/pulls/1134","id":50713021,"html_url":"https://github.com/oppia/oppia/pull/1134","diff_url":"https://github.com/oppia/oppia/pull/1134.diff","patch_url":"https://github.com/oppia/oppia/pull/1134.patch","issue_url":"https://api.github.com/repos/oppia/oppia/issues/1134","number":1134,"state":"open","locked":false,"title":"Type parse validation","user":{"login":"sfederwisch","id":13838973,"avatar_url":"https://avatars.githubusercontent.com/u/13838973?v=3","gravatar_id":"","url":"https://api.github.com/users/sfederwisch","html_url":"https://github.com/sfederwisch","followers_url":"https://api.github.com/users/sfederwisch/followers","following_url":"https://api.github.com/users/sfederwisch/following{/other_user}","gists_url":"https://api.github.com/users/sfederwisch/gists{/gist_id}","starred_url":"https://api.github.com/users/sfederwisch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sfederwisch/subscriptions","organizations_url":"https://api.github.com/users/sfederwisch/orgs","repos_url":"https://api.github.com/users/sfederwisch/repos","events_url":"https://api.github.com/users/sfederwisch/events{/privacy}","received_events_url":"https://api.github.com/users/sfederwisch/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-11-14T01:56:22Z","updated_at":"2015-12-12T00:52:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2cca83f97829da00a6b90444b6e5c1ac3e5aa2d0","assignee":{"login":"seanlip","id":10575562,"avatar_url":"https://avatars.githubusercontent.com/u/10575562?v=3","gravatar_id":"","url":"https://api.github.com/users/seanlip","html_url":"https://github.com/seanlip","followers_url":"https://api.github.com/users/seanlip/followers","following_url":"https://api.github.com/users/seanlip/following{/other_user}","gists_url":"https://api.github.com/users/seanlip/gists{/gist_id}","starred_url":"https://api.github.com/users/seanlip/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seanlip/subscriptions","organizations_url":"https://api.github.com/users/seanlip/orgs","repos_url":"https://api.github.com/users/seanlip/repos","events_url":"https://api.github.com/users/seanlip/events{/privacy}","received_events_url":"https://api.github.com/users/seanlip/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/oppia/oppia/pulls/1134/commits","review_comments_url":"https://api.github.com/repos/oppia/oppia/pulls/1134/comments","review_comment_url":"https://api.github.com/repos/oppia/oppia/pulls/comments{/number}","comments_url":"https://api.github.com/repos/oppia/oppia/issues/1134/comments","statuses_url":"https://api.github.com/repos/oppia/oppia/statuses/199c3c53a7e3395d857ab3a42641e4ceec576dea","head":{"label":"sfederwisch:type-parse-validation","ref":"type-parse-validation","sha":"199c3c53a7e3395d857ab3a42641e4ceec576dea","user":{"login":"sfederwisch","id":13838973,"avatar_url":"https://avatars.githubusercontent.com/u/13838973?v=3","gravatar_id":"","url":"https://api.github.com/users/sfederwisch","html_url":"https://github.com/sfederwisch","followers_url":"https://api.github.com/users/sfederwisch/followers","following_url":"https://api.github.com/users/sfederwisch/following{/other_user}","gists_url":"https://api.github.com/users/sfederwisch/gists{/gist_id}","starred_url":"https://api.github.com/users/sfederwisch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sfederwisch/subscriptions","organizations_url":"https://api.github.com/users/sfederwisch/orgs","repos_url":"https://api.github.com/users/sfederwisch/repos","events_url":"https://api.github.com/users/sfederwisch/events{/privacy}","received_events_url":"https://api.github.com/users/sfederwisch/received_events","type":"User","site_admin":false},"repo":{"id":41565390,"name":"oppia","full_name":"sfederwisch/oppia","owner":{"login":"sfederwisch","id":13838973,"avatar_url":"https://avatars.githubusercontent.com/u/13838973?v=3","gravatar_id":"","url":"https://api.github.com/users/sfederwisch","html_url":"https://github.com/sfederwisch","followers_url":"https://api.github.com/users/sfederwisch/followers","following_url":"https://api.github.com/users/sfederwisch/following{/other_user}","gists_url":"https://api.github.com/users/sfederwisch/gists{/gist_id}","starred_url":"https://api.github.com/users/sfederwisch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sfederwisch/subscriptions","organizations_url":"https://api.github.com/users/sfederwisch/orgs","repos_url":"https://api.github.com/users/sfederwisch/repos","events_url":"https://api.github.com/users/sfederwisch/events{/privacy}","received_events_url":"https://api.github.com/users/sfederwisch/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sfederwisch/oppia","description":"Tool for creating interactive tutors.","fork":true,"url":"https://api.github.com/repos/sfederwisch/oppia","forks_url":"https://api.github.com/repos/sfederwisch/oppia/forks","keys_url":"https://api.github.com/repos/sfederwisch/oppia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sfederwisch/oppia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sfederwisch/oppia/teams","hooks_url":"https://api.github.com/repos/sfederwisch/oppia/hooks","issue_events_url":"https://api.github.com/repos/sfederwisch/oppia/issues/events{/number}","events_url":"https://api.github.com/repos/sfederwisch/oppia/events","assignees_url":"https://api.github.com/repos/sfederwisch/oppia/assignees{/user}","branches_url":"https://api.github.com/repos/sfederwisch/oppia/branches{/branch}","tags_url":"https://api.github.com/repos/sfederwisch/oppia/tags","blobs_url":"https://api.github.com/repos/sfederwisch/oppia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sfederwisch/oppia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sfederwisch/oppia/git/refs{/sha}","trees_url":"https://api.github.com/repos/sfederwisch/oppia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sfederwisch/oppia/statuses/{sha}","languages_url":"https://api.github.com/repos/sfederwisch/oppia/languages","stargazers_url":"https://api.github.com/repos/sfederwisch/oppia/stargazers","contributors_url":"https://api.github.com/repos/sfederwisch/oppia/contributors","subscribers_url":"https://api.github.com/repos/sfederwisch/oppia/subscribers","subscription_url":"https://api.github.com/repos/sfederwisch/oppia/subscription","commits_url":"https://api.github.com/repos/sfederwisch/oppia/commits{/sha}","git_commits_url":"https://api.github.com/repos/sfederwisch/oppia/git/commits{/sha}","comments_url":"https://api.github.com/repos/sfederwisch/oppia/comments{/number}","issue_comment_url":"https://api.github.com/repos/sfederwisch/oppia/issues/comments{/number}","contents_url":"https://api.github.com/repos/sfederwisch/oppia/contents/{+path}","compare_url":"https://api.github.com/repos/sfederwisch/oppia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sfederwisch/oppia/merges","archive_url":"https://api.github.com/repos/sfederwisch/oppia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sfederwisch/oppia/downloads","issues_url":"https://api.github.com/repos/sfederwisch/oppia/issues{/number}","pulls_url":"https://api.github.com/repos/sfederwisch/oppia/pulls{/number}","milestones_url":"https://api.github.com/repos/sfederwisch/oppia/milestones{/number}","notifications_url":"https://api.github.com/repos/sfederwisch/oppia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sfederwisch/oppia/labels{/name}","releases_url":"https://api.github.com/repos/sfederwisch/oppia/releases{/id}","created_at":"2015-08-28T19:32:51Z","updated_at":"2015-08-28T19:32:53Z","pushed_at":"2015-12-11T23:06:53Z","git_url":"git://github.com/sfederwisch/oppia.git","ssh_url":"git@github.com:sfederwisch/oppia.git","clone_url":"https://github.com/sfederwisch/oppia.git","svn_url":"https://github.com/sfederwisch/oppia","homepage":"https://www.oppia.org","size":38870,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"oppia:develop","ref":"develop","sha":"05da531383a0af5d28b1fdd5affd1dcaf2ae1798","user":{"login":"oppia","id":11620230,"avatar_url":"https://avatars.githubusercontent.com/u/11620230?v=3","gravatar_id":"","url":"https://api.github.com/users/oppia","html_url":"https://github.com/oppia","followers_url":"https://api.github.com/users/oppia/followers","following_url":"https://api.github.com/users/oppia/following{/other_user}","gists_url":"https://api.github.com/users/oppia/gists{/gist_id}","starred_url":"https://api.github.com/users/oppia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oppia/subscriptions","organizations_url":"https://api.github.com/users/oppia/orgs","repos_url":"https://api.github.com/users/oppia/repos","events_url":"https://api.github.com/users/oppia/events{/privacy}","received_events_url":"https://api.github.com/users/oppia/received_events","type":"Organization","site_admin":false},"repo":{"id":40687563,"name":"oppia","full_name":"oppia/oppia","owner":{"login":"oppia","id":11620230,"avatar_url":"https://avatars.githubusercontent.com/u/11620230?v=3","gravatar_id":"","url":"https://api.github.com/users/oppia","html_url":"https://github.com/oppia","followers_url":"https://api.github.com/users/oppia/followers","following_url":"https://api.github.com/users/oppia/following{/other_user}","gists_url":"https://api.github.com/users/oppia/gists{/gist_id}","starred_url":"https://api.github.com/users/oppia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oppia/subscriptions","organizations_url":"https://api.github.com/users/oppia/orgs","repos_url":"https://api.github.com/users/oppia/repos","events_url":"https://api.github.com/users/oppia/events{/privacy}","received_events_url":"https://api.github.com/users/oppia/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/oppia/oppia","description":"Tool for collaboratively building interactive tutors.","fork":false,"url":"https://api.github.com/repos/oppia/oppia","forks_url":"https://api.github.com/repos/oppia/oppia/forks","keys_url":"https://api.github.com/repos/oppia/oppia/keys{/key_id}","collaborators_url":"https://api.github.com/repos/oppia/oppia/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/oppia/oppia/teams","hooks_url":"https://api.github.com/repos/oppia/oppia/hooks","issue_events_url":"https://api.github.com/repos/oppia/oppia/issues/events{/number}","events_url":"https://api.github.com/repos/oppia/oppia/events","assignees_url":"https://api.github.com/repos/oppia/oppia/assignees{/user}","branches_url":"https://api.github.com/repos/oppia/oppia/branches{/branch}","tags_url":"https://api.github.com/repos/oppia/oppia/tags","blobs_url":"https://api.github.com/repos/oppia/oppia/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/oppia/oppia/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/oppia/oppia/git/refs{/sha}","trees_url":"https://api.github.com/repos/oppia/oppia/git/trees{/sha}","statuses_url":"https://api.github.com/repos/oppia/oppia/statuses/{sha}","languages_url":"https://api.github.com/repos/oppia/oppia/languages","stargazers_url":"https://api.github.com/repos/oppia/oppia/stargazers","contributors_url":"https://api.github.com/repos/oppia/oppia/contributors","subscribers_url":"https://api.github.com/repos/oppia/oppia/subscribers","subscription_url":"https://api.github.com/repos/oppia/oppia/subscription","commits_url":"https://api.github.com/repos/oppia/oppia/commits{/sha}","git_commits_url":"https://api.github.com/repos/oppia/oppia/git/commits{/sha}","comments_url":"https://api.github.com/repos/oppia/oppia/comments{/number}","issue_comment_url":"https://api.github.com/repos/oppia/oppia/issues/comments{/number}","contents_url":"https://api.github.com/repos/oppia/oppia/contents/{+path}","compare_url":"https://api.github.com/repos/oppia/oppia/compare/{base}...{head}","merges_url":"https://api.github.com/repos/oppia/oppia/merges","archive_url":"https://api.github.com/repos/oppia/oppia/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/oppia/oppia/downloads","issues_url":"https://api.github.com/repos/oppia/oppia/issues{/number}","pulls_url":"https://api.github.com/repos/oppia/oppia/pulls{/number}","milestones_url":"https://api.github.com/repos/oppia/oppia/milestones{/number}","notifications_url":"https://api.github.com/repos/oppia/oppia/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/oppia/oppia/labels{/name}","releases_url":"https://api.github.com/repos/oppia/oppia/releases{/id}","created_at":"2015-08-14T00:16:14Z","updated_at":"2015-12-09T15:20:42Z","pushed_at":"2015-12-11T23:34:51Z","git_url":"git://github.com/oppia/oppia.git","ssh_url":"git@github.com:oppia/oppia.git","clone_url":"https://github.com/oppia/oppia.git","svn_url":"https://github.com/oppia/oppia","homepage":"https://www.oppia.org","size":44455,"stargazers_count":63,"watchers_count":63,"language":"Python","has_issues":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":83,"mirror_url":null,"open_issues_count":237,"forks":83,"open_issues":237,"watchers":63,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/oppia/oppia/pulls/1134"},"html":{"href":"https://github.com/oppia/oppia/pull/1134"},"issue":{"href":"https://api.github.com/repos/oppia/oppia/issues/1134"},"comments":{"href":"https://api.github.com/repos/oppia/oppia/issues/1134/comments"},"review_comments":{"href":"https://api.github.com/repos/oppia/oppia/pulls/1134/comments"},"review_comment":{"href":"https://api.github.com/repos/oppia/oppia/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/oppia/oppia/pulls/1134/commits"},"statuses":{"href":"https://api.github.com/repos/oppia/oppia/statuses/199c3c53a7e3395d857ab3a42641e4ceec576dea"}}}}