From 4c12337fc26cf59e0d4d4b16da600582c453fd9a Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Tue, 26 Apr 2022 09:57:56 +0530 Subject: [PATCH 01/12] Add return statement to ifStmt template --- lib/estemplate.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/estemplate.js b/lib/estemplate.js index d6ec7ee..3b52b91 100644 --- a/lib/estemplate.js +++ b/lib/estemplate.js @@ -220,12 +220,14 @@ estemplate.ifthenelse = (condition, result1, result2) => ({ } }) -estemplate.ifStmt = (predicate, consequent) => ({ +estemplate.ifStmt = (predicate, consequent, isReturn = false) => ({ type: 'IfStatement', test: predicate, consequent: { type: 'BlockStatement', - body: [consequent, estemplate.returnStmt(null)] + body: isReturn + ? [estemplate.returnStmt(consequent)] + : [consequent, estemplate.returnStmt(null)] }, alternate: null }) From 2dac8328fa8bf935988f83ea1a09bf112484f56a Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Tue, 26 Apr 2022 10:00:08 +0530 Subject: [PATCH 02/12] Add funcBlockDeclaration template --- lib/estemplate.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/estemplate.js b/lib/estemplate.js index 3b52b91..c17e110 100644 --- a/lib/estemplate.js +++ b/lib/estemplate.js @@ -96,6 +96,32 @@ estemplate.funcDeclaration = (id, params, body) => ({ kind: 'const' }) +estemplate.funcBlockDeclaration = (id, params, body) => { + return { + type: 'VariableDeclaration', + declarations: [ + { + type: 'VariableDeclarator', + id, + init: { + type: 'ArrowFunctionExpression', + id: null, + params: params, + body: { + type: 'BlockStatement', + body: [ + extractExpr(body) || '' + ] + }, + generator: false, + expression: false + } + } + ], + kind: 'const' + } +} + estemplate.lambdaCall = (params, args, body) => ({ type: 'CallExpression', callee: { From 943bfda57638ea92f480fabdcc02ed6a3823c8e6 Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Tue, 26 Apr 2022 10:16:03 +0530 Subject: [PATCH 03/12] Add basic parsers, vBar, otherwise, where --- lib/basicParsers.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/basicParsers.js b/lib/basicParsers.js index 5489843..529727d 100644 --- a/lib/basicParsers.js +++ b/lib/basicParsers.js @@ -33,6 +33,8 @@ const openParensRegex = () => /^(\()((.|\n)*)$/ const closeParensRegex = () => /^(\))((.|\n)*)$/ const commaRegex = () => /^(,)((.|\n)*)$/ const colonRegex = () => /^(:)((.|\n)*)$/ +const vBarRegex = () => /^(\|\s+)((.|\n)*)$/ +const otherwiseRegex = () => /^(otherwise)((.|\n)*)$/ const singleLineCommentRegex = () => /^((\/\/)(.*)(?=\n|))((.|\n)*)$/ const multiLineCommentRegex = () => /^((\/\*)((.|\n)*?)(\*\/))((.|\n)*)$/ @@ -50,6 +52,7 @@ const dotRegex = () => /^(\.)((.|\n)*)$/ const ifRegex = () => /^(if\s+)((.|\n)*)$/ const thenRegex = () => /^(then\s+)((.|\n)*)$/ const elseRegex = () => /^(else\s+)((.|\n)*)$/ +const whereRegex = () => /^(where\s+)((.|\n)*)$/ const doRegex = () => /^(do)((.|\n)*)$/ const returnKeywordRegex = () => /^(return)((.|\n)*)$/ const includeKeywordRegex = () => /^(include)((.|\n)*)$/ @@ -84,6 +87,8 @@ const ifParser = (input) => parser.regex(ifRegex)(input) const thenParser = (input) => parser.regex(thenRegex)(input) const elseParser = (input) => parser.regex(elseRegex)(input) +const whereParser = (input) => parser.regex(whereRegex)(input) + const doParser = (input) => parser.regex(doRegex)(input) const returnKeywordParser = (input) => @@ -222,6 +227,14 @@ const colonParser = (input) => rest ]) +const vBarParser = (input) => + maybe(parser.regex(vBarRegex)(input), (bar, rest) => [ + bar, + rest + ]) + +const otherwiseParser = (input) => parser.regex(otherwiseRegex)(input) + const singleLineCommentParser = (input) => maybe(singleLineCommentRegex().exec(input.str), (...vals) => { const [, , , val, rest] = vals @@ -335,6 +348,8 @@ module.exports = { closeSquareBracketParser, commaParser, colonParser, + vBarParser, + otherwiseParser, singleLineCommentParser, multiLineCommentParser, binaryOperatorParser, @@ -347,6 +362,7 @@ module.exports = { ifParser, thenParser, elseParser, + whereParser, doParser, equalSignParser, slashParser, From 55a59d600cd15a6c6f055beebc1889a2954be71e Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Tue, 26 Apr 2022 10:27:29 +0530 Subject: [PATCH 04/12] Implement guards parser --- lib/parser.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/lib/parser.js b/lib/parser.js index af76912..4f5c0a6 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -65,6 +65,7 @@ const { nonReservedIdParser, nullParser, numberParser, + otherwiseParser, openCurlyBraceParser, openParensParser, openSquareBracketParser, @@ -78,7 +79,9 @@ const { stringParser, thenParser, thinArrowParser, - unaryOperatorParser + unaryOperatorParser, + vBarParser + // whereParser, } = base const unaryExprParser = (input) => @@ -1133,6 +1136,63 @@ const funcParamsParser = (input, paramArray = []) => } ) || [paramArray, input] +const combineGuards = (guards) => { + if (!guards.length) return null + let combinedGuards = guards[guards.length - 1] + for (let i = guards.length - 2; i >= 0; i--) { + guards[i].alternate = combinedGuards + combinedGuards = guards[i] + } + return combinedGuards +} + +const guardsParser = (input) => { + const guards = [] + let guardParsed + do { + guardParsed = maybe( + parser.all( + maybeNewLineAndIndent, + vBarParser, + binaryExprParser, + equalSignParser, + valueParser + )(input), + (val, rest) => { + const [, , predicate, , consequent] = val + return [estemplate.ifStmt(predicate, consequent, true), rest] + } + ) + if (guardParsed) { + guards.push(guardParsed[0]) + input = guardParsed[1] + } + } while (guardParsed) + const defaultCase = maybe( + parser.all( + maybeNewLineAndIndent, + vBarParser, + otherwiseParser, + equalSignParser, + valueParser + )(input), + (val, rest) => { + const [, , , , defaultCase] = val + return [estemplate.ifStmt(estemplate.boolLiteral('true'), defaultCase, true), rest] + } + ) + if (defaultCase) { + guards.push( + defaultCase[0] + ) + const rest = defaultCase[1] + return [combineGuards(guards), rest] + } else if (guards.length) { + return [combineGuards(guards), input] + } + return null +} + const fnDeclParser = (input) => maybe( parser.all( @@ -1147,6 +1207,20 @@ const fnDeclParser = (input) => } ) +const fnWithGuardsParser = (input) => + maybe( + parser.all( + nonReservedIdParser, + funcParamsParser, + maybeNewLineAndIndent, + guardsParser + )(input), + (val, rest) => { + const [funcID, paramsArr, , body] = val + return [estemplate.funcBlockDeclaration(funcID, paramsArr, body), rest] + } + ) + const statementParser = (input) => parser.any( multiLineCommentParser, @@ -1158,6 +1232,7 @@ const statementParser = (input) => declParser, ifExprParser, fnDeclParser, + fnWithGuardsParser, defineStmtParser, fnCallParser, lambdaParser, From 983b28349f2a40b19bf141f5c03e2df6d609f9df Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Tue, 26 Apr 2022 10:51:09 +0530 Subject: [PATCH 05/12] Add test for guards --- test/assert/guards.json | 402 ++++++++++++++++++++++++++++++++++++++++ test/src/guards.cl | 16 ++ 2 files changed, 418 insertions(+) create mode 100644 test/assert/guards.json create mode 100644 test/src/guards.cl diff --git a/test/assert/guards.json b/test/assert/guards.json new file mode 100644 index 0000000..a11c31d --- /dev/null +++ b/test/assert/guards.json @@ -0,0 +1,402 @@ +{ + "type": "Program", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "bmiTell" + }, + "init": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [ + { + "type": "Identifier", + "name": "bmi" + } + ], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": "<=", + "sType": "bool", + "left": { + "type": "Identifier", + "name": "bmi" + }, + "right": { + "type": "Literal", + "value": 18.5, + "raw": "18.5", + "sType": "number" + } + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": "Underweight", + "raw": "Underweight", + "sType": "string" + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": "<=", + "sType": "bool", + "left": { + "type": "Identifier", + "name": "bmi" + }, + "right": { + "type": "Literal", + "value": 25, + "raw": "25.0", + "sType": "number" + } + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": "Normal", + "raw": "Normal", + "sType": "string" + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": "<=", + "sType": "bool", + "left": { + "type": "Identifier", + "name": "bmi" + }, + "right": { + "type": "Literal", + "value": 30, + "raw": "30.0", + "sType": "number" + } + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": "Fatty", + "raw": "Fatty", + "sType": "string" + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "test": { + "type": "Literal", + "value": true, + "raw": "true", + "sType": "bool" + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": "Index overflow", + "raw": "Index overflow", + "sType": "string" + } + } + ] + }, + "alternate": null + } + } + } + } + ] + }, + "generator": false, + "expression": false + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "add" + }, + "init": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [ + { + "type": "Identifier", + "name": "x" + }, + { + "type": "Identifier", + "name": "y" + } + ], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "IfStatement", + "test": { + "type": "Literal", + "value": true, + "raw": "true", + "sType": "bool" + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "BinaryExpression", + "operator": "+", + "sType": "number", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Identifier", + "name": "y" + } + } + } + ] + }, + "alternate": null + } + ] + }, + "generator": false, + "expression": false + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "isZero" + }, + "init": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [ + { + "type": "Identifier", + "name": "x" + } + ], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": "===", + "sType": "bool", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Literal", + "value": 0, + "raw": "0", + "sType": "number" + } + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": true, + "raw": "true", + "sType": "bool" + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "test": { + "type": "Literal", + "value": true, + "raw": "true", + "sType": "bool" + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": false, + "raw": "false", + "sType": "bool" + } + } + ] + }, + "alternate": null + } + } + ] + }, + "generator": false, + "expression": false + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "sign" + }, + "init": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [ + { + "type": "Identifier", + "name": "x" + } + ], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": "<", + "sType": "bool", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Literal", + "value": 0, + "raw": "0", + "sType": "number" + } + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": "Negative", + "raw": "Negative", + "sType": "string" + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": ">", + "sType": "bool", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Literal", + "value": 0, + "raw": "0", + "sType": "number" + } + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": "Positive", + "raw": "Positive", + "sType": "string" + } + } + ] + }, + "alternate": null + } + } + ] + }, + "generator": false, + "expression": false + } + } + ], + "kind": "const" + } + ], + "sourceType": "script" +} diff --git a/test/src/guards.cl b/test/src/guards.cl new file mode 100644 index 0000000..bccb701 --- /dev/null +++ b/test/src/guards.cl @@ -0,0 +1,16 @@ +bmiTell bmi + | bmi <= 18.5 = 'Underweight' + | bmi <= 25.0 = 'Normal' + | bmi <= 30.0 = 'Fatty' + | otherwise = 'Index overflow' + +add x y + | otherwise = x + y + +isZero x + | x == 0 = true + | otherwise = false + +sign x + | x < 0 = 'Negative' + | x > 0 = 'Positive' From 9a5480ac908250562cbae5e7ffc3fdfcc97116bd Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Tue, 26 Apr 2022 15:22:22 +0530 Subject: [PATCH 06/12] Refactor: Separate reserved function calls --- lib/estemplate.js | 17 +++++++++-------- lib/languageConstructs.js | 10 ++++++++-- lib/parser.js | 5 ++++- lib/utilityFunctions.js | 4 ++-- test/basicAssertion.json | 1 - 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/estemplate.js b/lib/estemplate.js index c17e110..a4104d8 100644 --- a/lib/estemplate.js +++ b/lib/estemplate.js @@ -186,14 +186,15 @@ estemplate.printexpression = (args) => ({ sType: 'IO' }) -estemplate.fnCall = (val, args) => - val.name === 'print' - ? estemplate.printexpression(args) - : { - type: 'CallExpression', - callee: extractExpr(val), - arguments: args.map(extractExpr) - } +estemplate.reservedCalls = { + print: (args) => estemplate.printexpression(args) +} + +estemplate.fnCall = (val, args) => ({ + type: 'CallExpression', + callee: extractExpr(val), + arguments: args.map(extractExpr) +}) estemplate.lambda = (params, body) => ({ type: 'ExpressionStatement', diff --git a/lib/languageConstructs.js b/lib/languageConstructs.js index 59a07e1..eea587b 100644 --- a/lib/languageConstructs.js +++ b/lib/languageConstructs.js @@ -18,10 +18,16 @@ const languageConstructs = { false: true, null: true, do: true, - console: true, return: true, delete: true, defineProp: true } -module.exports = languageConstructs +const reservedFunctionCalls = { + print: true +} + +module.exports = { + languageConstructs, + reservedFunctionCalls +} diff --git a/lib/parser.js b/lib/parser.js index 4f5c0a6..ac815a7 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -22,6 +22,7 @@ const parser = require('./parserObject') const base = require('./basicParsers') const utils = require('./utilityFunctions') const errorMsg = require('./errors') +const { reservedFunctionCalls } = require('./languageConstructs') /* Utility Functions */ const { @@ -275,7 +276,9 @@ const fnCallParser = (input) => parser.all(calleeParser, spaceParser, fnArgsParser)(input), (val, rest) => { const [callee, , args] = val - const callExpr = estemplate.fnCall(callee, args) + const callExpr = reservedFunctionCalls[callee.name] + ? estemplate.reservedCalls[callee.name](args) + : estemplate.fnCall(callee, args) const expr = rest.str.startsWith('\n') || /^()()$/.test(rest.str) ? estemplate.expression(callExpr) diff --git a/lib/utilityFunctions.js b/lib/utilityFunctions.js index 6b28c4b..264624a 100644 --- a/lib/utilityFunctions.js +++ b/lib/utilityFunctions.js @@ -3,11 +3,11 @@ const { ioMethods, domMethods } = require('./ioMethods') -const languageConstruct = require('./languageConstructs') +const { languageConstructs } = require('./languageConstructs') const opSpec = require('./operatorPrecedence') /* Utility functions */ const maybe = (value, func) => (value === null ? null : func(...value)) -const isLanguageConstruct = (id) => languageConstruct[id] +const isLanguageConstruct = (id) => languageConstructs[id] const isStaticIOMethod = (id) => staticIOMethods[id] const isIOMethod = (id) => ioMethods[id] const isDOMmethod = (id) => domMethods[id] diff --git a/test/basicAssertion.json b/test/basicAssertion.json index 59522fa..1b4b546 100644 --- a/test/basicAssertion.json +++ b/test/basicAssertion.json @@ -131,7 +131,6 @@ "false": null, "null": null, "do": null, - "console": null, "return": null, "delete": null, "defineProp": null, From 833d81307cb2153bad4021366233e63762463c14 Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Tue, 26 Apr 2022 16:25:28 +0530 Subject: [PATCH 07/12] Fix fnWithguradsParser with non reserved ID and Function call --- lib/basicParsers.js | 13 +++++++++++++ lib/languageConstructs.js | 4 ++-- lib/parser.js | 17 ++++++++++------- lib/utilityFunctions.js | 4 +++- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/basicParsers.js b/lib/basicParsers.js index 529727d..59c4ddc 100644 --- a/lib/basicParsers.js +++ b/lib/basicParsers.js @@ -5,6 +5,7 @@ const estemplate = require('./estemplate') const { maybe, isLanguageConstruct, + isReservedFnCall, isStaticIOMethod, isIOMethod, isDOMmethod, @@ -124,6 +125,17 @@ const nonReservedIdParser = (input) => : [estemplate.identifier(name), rest] ) +const nonReservedIdFnCallParser = (input) => + maybe(idParser(input), (name, rest) => + isLanguageConstruct(name) || + isReservedFnCall(name) || + isStaticIOMethod(name) || + isIOMethod(name) || + isDOMmethod(name) + ? null + : [estemplate.identifier(name), rest] + ) + const identifierParser = (input) => maybe(idParser(input), (name, rest) => [ estemplate.identifier(name), @@ -332,6 +344,7 @@ module.exports = { maybeNewLineAndIndent, numberParser, nonReservedIdParser, + nonReservedIdFnCallParser, identifierParser, domMethodParser, ioFuncNameParser, diff --git a/lib/languageConstructs.js b/lib/languageConstructs.js index eea587b..6ae72aa 100644 --- a/lib/languageConstructs.js +++ b/lib/languageConstructs.js @@ -23,11 +23,11 @@ const languageConstructs = { defineProp: true } -const reservedFunctionCalls = { +const reservedFnCalls = { print: true } module.exports = { languageConstructs, - reservedFunctionCalls + reservedFnCalls } diff --git a/lib/parser.js b/lib/parser.js index ac815a7..fff4897 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -22,7 +22,7 @@ const parser = require('./parserObject') const base = require('./basicParsers') const utils = require('./utilityFunctions') const errorMsg = require('./errors') -const { reservedFunctionCalls } = require('./languageConstructs') +const { reservedFnCalls } = require('./languageConstructs') /* Utility Functions */ const { @@ -64,6 +64,7 @@ const { maybeSpace, multiLineCommentParser, nonReservedIdParser, + nonReservedIdFnCallParser, nullParser, numberParser, otherwiseParser, @@ -276,7 +277,7 @@ const fnCallParser = (input) => parser.all(calleeParser, spaceParser, fnArgsParser)(input), (val, rest) => { const [callee, , args] = val - const callExpr = reservedFunctionCalls[callee.name] + const callExpr = reservedFnCalls[callee.name] ? estemplate.reservedCalls[callee.name](args) : estemplate.fnCall(callee, args) const expr = @@ -1107,7 +1108,8 @@ const valueParser = (input) => binaryExprParser, fnCallParser, lambdaCallParser, - expressionParser + expressionParser, + guardsParser )(input) const declParser = (input) => @@ -1210,19 +1212,20 @@ const fnDeclParser = (input) => } ) -const fnWithGuardsParser = (input) => - maybe( +const fnWithGuardsParser = (input) => { + return maybe( parser.all( - nonReservedIdParser, + nonReservedIdFnCallParser, funcParamsParser, maybeNewLineAndIndent, - guardsParser + valueParser )(input), (val, rest) => { const [funcID, paramsArr, , body] = val return [estemplate.funcBlockDeclaration(funcID, paramsArr, body), rest] } ) +} const statementParser = (input) => parser.any( diff --git a/lib/utilityFunctions.js b/lib/utilityFunctions.js index 264624a..7f81d7f 100644 --- a/lib/utilityFunctions.js +++ b/lib/utilityFunctions.js @@ -3,11 +3,12 @@ const { ioMethods, domMethods } = require('./ioMethods') -const { languageConstructs } = require('./languageConstructs') +const { languageConstructs, reservedFnCalls } = require('./languageConstructs') const opSpec = require('./operatorPrecedence') /* Utility functions */ const maybe = (value, func) => (value === null ? null : func(...value)) const isLanguageConstruct = (id) => languageConstructs[id] +const isReservedFnCall = (id) => reservedFnCalls[id] const isStaticIOMethod = (id) => staticIOMethods[id] const isIOMethod = (id) => ioMethods[id] const isDOMmethod = (id) => domMethods[id] @@ -68,6 +69,7 @@ const associativity = (operator) => opSpec[operator].assoc module.exports = { maybe, isLanguageConstruct, + isReservedFnCall, isStaticIOMethod, isIOMethod, isDOMmethod, From aab8f597f5ab50120da0e35261859537ea092861 Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Wed, 27 Apr 2022 11:59:30 +0530 Subject: [PATCH 08/12] Remove funcBlockDeclaration and use funcDeclaration with BlockStmt --- lib/estemplate.js | 26 -------------------------- lib/parser.js | 2 +- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/lib/estemplate.js b/lib/estemplate.js index a4104d8..af4ce50 100644 --- a/lib/estemplate.js +++ b/lib/estemplate.js @@ -96,32 +96,6 @@ estemplate.funcDeclaration = (id, params, body) => ({ kind: 'const' }) -estemplate.funcBlockDeclaration = (id, params, body) => { - return { - type: 'VariableDeclaration', - declarations: [ - { - type: 'VariableDeclarator', - id, - init: { - type: 'ArrowFunctionExpression', - id: null, - params: params, - body: { - type: 'BlockStatement', - body: [ - extractExpr(body) || '' - ] - }, - generator: false, - expression: false - } - } - ], - kind: 'const' - } -} - estemplate.lambdaCall = (params, args, body) => ({ type: 'CallExpression', callee: { diff --git a/lib/parser.js b/lib/parser.js index fff4897..c73bf5d 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1222,7 +1222,7 @@ const fnWithGuardsParser = (input) => { )(input), (val, rest) => { const [funcID, paramsArr, , body] = val - return [estemplate.funcBlockDeclaration(funcID, paramsArr, body), rest] + return [estemplate.funcDeclaration(funcID, paramsArr, estemplate.blockStmt([body])), rest] } ) } From c4b1e5f30759244b17811ea1c40027a2302ea005 Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Wed, 27 Apr 2022 12:38:38 +0530 Subject: [PATCH 09/12] Implement where clause --- lib/astupdate.js | 2 +- lib/parser.js | 66 ++++++++++++++++++++++++++++++++++++++++---- lib/typeInference.js | 6 ++-- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/lib/astupdate.js b/lib/astupdate.js index 58f9790..4c83c36 100644 --- a/lib/astupdate.js +++ b/lib/astupdate.js @@ -70,9 +70,9 @@ const formMultiPatternAst = (funcdecl, patterndecls) => { const isArrowFuncDecl = (decl) => { const declarations = decl.declarations - if ( declarations !== undefined && + declarations[0].init && declarations[0].init.type === 'ArrowFunctionExpression' ) { return true } return false diff --git a/lib/parser.js b/lib/parser.js index c73bf5d..ff6c26b 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -82,8 +82,8 @@ const { thenParser, thinArrowParser, unaryOperatorParser, - vBarParser - // whereParser, + vBarParser, + whereParser } = base const unaryExprParser = (input) => @@ -1103,6 +1103,58 @@ const parenthesesParser = (input) => } ) +const whereClauseParser = (input) => { + const whereDeclarations = [] + const whereParsed = whereParser(input) + if (!whereParsed) { + return null + } + const [, rest] = whereParsed + const whereDeclCol = rest.column + input = rest + do { + const declParsed = maybe( + parser.all( + parser.any( + declParser, + fnDeclParser, + fnWithGuardsParser + ), + maybeNewLineAndIndent + )(input), + (val, rest) => { + const [decl] = val + return [decl, rest] + } + ) + if (!declParsed) { + break + } + whereDeclarations.push(declParsed[0]) + input = declParsed[1] + } while (input.column === whereDeclCol) + return [whereDeclarations, input] +} + +const valueAndWhereClauseParser = (input) => + maybe( + parser.all( + valueParser, + maybeNewLineAndIndent, + whereClauseParser + )(input), + (val, rest) => { + const [v, , whereDecls] = val + const block = estemplate.blockStmt([]) + const nonReturnStatements = ['IfStatement'] + block.body = [ + ...whereDecls, + nonReturnStatements.includes(v.type) ? v : estemplate.returnStmt(v) + ] + return [block, rest] + } + ) + const valueParser = (input) => parser.any( binaryExprParser, @@ -1114,7 +1166,11 @@ const valueParser = (input) => const declParser = (input) => maybe( - parser.all(nonReservedIdParser, equalSignParser, valueParser)(input), + parser.all( + nonReservedIdParser, + equalSignParser, + valueParser + )(input), (val, rest) => { const [id, , value] = val return [estemplate.declaration(id, value), rest] @@ -1204,7 +1260,7 @@ const fnDeclParser = (input) => nonReservedIdParser, funcParamsParser, equalSignParser, - valueParser + parser.any(valueAndWhereClauseParser, valueParser) )(input), (val, rest) => { const [funcID, paramsArr, , body] = val @@ -1218,7 +1274,7 @@ const fnWithGuardsParser = (input) => { nonReservedIdFnCallParser, funcParamsParser, maybeNewLineAndIndent, - valueParser + parser.any(valueAndWhereClauseParser, valueParser) )(input), (val, rest) => { const [funcID, paramsArr, , body] = val diff --git a/lib/typeInference.js b/lib/typeInference.js index b3d2609..3b41d88 100644 --- a/lib/typeInference.js +++ b/lib/typeInference.js @@ -535,8 +535,8 @@ const blockStatementType = (stmnt, localTypeObj, id) => { } const exprType = (expr, localTypeObj = {}, id = null) => { - if (expr !== null && expr.sType !== undefined && expr.sType === 'IO') { return 'IO' } - if (expr === null) return 'needsInference' + if (!expr) return 'needsInference' + if (expr.sType !== undefined && expr.sType === 'IO') { return 'IO' } if (expr.type === 'ExpressionStatement') expr = expr.expression const type = expr.type switch (type) { @@ -572,7 +572,7 @@ const exprType = (expr, localTypeObj = {}, id = null) => { const declTypeExtract = (stmnt) => { const [decl] = stmnt.declarations const [id, exp] = [decl.id.name, decl.init] - if ((exp.sType !== undefined && exp.sType === 'IO') || id === 'IO') { + if ((exp && exp.sType !== undefined && exp.sType === 'IO') || id === 'IO') { globalTypesObj[id] = 'IO' } else { const type = exprType(exp, {}, id) From 5d9ae8d86673ab63148cd51656aa674985d8ee95 Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Wed, 27 Apr 2022 12:43:24 +0530 Subject: [PATCH 10/12] Implement where clause for declaration statements --- lib/parser.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index ff6c26b..659dfd7 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1169,11 +1169,20 @@ const declParser = (input) => parser.all( nonReservedIdParser, equalSignParser, - valueParser + parser.any(valueAndWhereClauseParser, valueParser) )(input), (val, rest) => { const [id, , value] = val - return [estemplate.declaration(id, value), rest] + const nonReturnStatements = ['BlockStatement'] + return [ + nonReturnStatements.includes(value.type) + ? estemplate.declaration( + id, + estemplate.lambdaCall([], [], value) + ) + : estemplate.declaration(id, value), + rest + ] } ) From 2435b9631ccb306a018b6a4bd94c0fb2eedcfaee Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Wed, 27 Apr 2022 13:00:13 +0530 Subject: [PATCH 11/12] Add test for where clause --- lib/parser.js | 265 ++++++++++++++++++++------- test/assert/whereClause.json | 336 +++++++++++++++++++++++++++++++++++ test/src/whereClause.cl | 15 ++ 3 files changed, 552 insertions(+), 64 deletions(-) create mode 100644 test/assert/whereClause.json create mode 100644 test/src/whereClause.cl diff --git a/lib/parser.js b/lib/parser.js index 659dfd7..12361c2 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -86,6 +86,8 @@ const { whereParser } = base +const nonExprStatements = ['BlockStatement'] + const unaryExprParser = (input) => parser.bind( parser.bind( @@ -119,7 +121,12 @@ const formBinaryExpr = (opStack, operandStack, input, rest) => { opStack.pop() ] const expr = estemplate.binaryExpression(left, op, right) - return formBinaryExpr(opStack, operandStack.concat(expr), input, rest) + return formBinaryExpr( + opStack, + operandStack.concat(expr), + input, + rest + ) } const handlePrecAssoc = (opStack, operandStack, current, rest) => { @@ -127,8 +134,10 @@ const handlePrecAssoc = (opStack, operandStack, current, rest) => { const currentHasHigher = precedence(current) > precedence(opStackTop) const currentHasLower = precedence(current) < precedence(opStackTop) const currentHasEqual = !currentHasLower && !currentHasHigher - const isRightAssociative = currentHasEqual && associativity(current) === 'R' - const isLeftAssociative = currentHasEqual && associativity(current) === 'L' + const isRightAssociative = + currentHasEqual && associativity(current) === 'R' + const isLeftAssociative = + currentHasEqual && associativity(current) === 'L' if (currentHasHigher || isRightAssociative) { return binaryExprParser( @@ -164,8 +173,13 @@ const binaryExprParser = ( let [current, rest] = [null, null] // initialize current and rest of the string to null switch (expect) { case 'operand': { - const maybeOperand = parser.any(fnCallParser, expressionParser)(input); - [current, rest] = notNull(maybeOperand) ? maybeOperand : [null, null] + const maybeOperand = parser.any( + fnCallParser, + expressionParser + )(input); + [current, rest] = notNull(maybeOperand) + ? maybeOperand + : [null, null] return isNull(current) ? null : binaryExprParser( @@ -177,7 +191,9 @@ const binaryExprParser = ( } case 'operator': { const maybeOperator = binaryOperatorParser(input); - [current, rest] = notNull(maybeOperator) ? maybeOperator : [null, null] + [current, rest] = notNull(maybeOperator) + ? maybeOperator + : [null, null] return notNull(current) ? handlePrecAssoc(opStack, operandStack, current, rest) : formBinaryExpr(opStack, operandStack, input, rest) @@ -200,7 +216,10 @@ const ifExprParser = (input) => )(input), (val, rest) => { const [, condition, , , consequent, , , alternate] = val - return [estemplate.ifthenelse(condition, consequent, alternate), rest] + return [ + estemplate.ifthenelse(condition, consequent, alternate), + rest + ] } ) @@ -225,7 +244,12 @@ const letParamsParser = (str, letIdArray = [], letLiteralArray = []) => const letExpressionParser = (input) => maybe( - parser.all(letParser, letParamsParser, inParser, valueParser)(input), + parser.all( + letParser, + letParamsParser, + inParser, + valueParser + )(input), (val, rest) => { const [, [letIdArray, letLiteralArray], , expr] = val const letExpr = estemplate.letExpression( @@ -239,14 +263,22 @@ const letExpressionParser = (input) => /* Helper functions for lambdaParser */ const paramsParser = (input, idArray = []) => - maybe(parser.all(nonReservedIdParser, spaceParser)(input), (val, rest) => { - const [val_] = val - return paramsParser(rest, idArray.concat(val_)) - }) || [idArray, input] + maybe( + parser.all(nonReservedIdParser, spaceParser)(input), + (val, rest) => { + const [val_] = val + return paramsParser(rest, idArray.concat(val_)) + } + ) || [idArray, input] const lambdaParser = (input) => maybe( - parser.all(slashParser, paramsParser, thinArrowParser, valueParser)(input), + parser.all( + slashParser, + paramsParser, + thinArrowParser, + valueParser + )(input), (val, rest) => { const [, params, , expr] = val return [estemplate.lambda(params, expr), rest] @@ -270,7 +302,8 @@ const argsParser = (input, argArray = []) => { const calleeParser = (input) => parser.any(memberExprParser, nonReservedIdParser)(input) -const fnArgsParser = (input) => parser.any(emptyArgsParser, argsParser)(input) +const fnArgsParser = (input) => + parser.any(emptyArgsParser, argsParser)(input) const fnCallParser = (input) => maybe( @@ -416,7 +449,11 @@ const objectParser = (input) => { /* Helper functions for memberExprParser */ const formMemberExpression = (input, obj) => { - const prop = parser.any(dotParser, subscriptParser, identifierParser)(input) + const prop = parser.any( + dotParser, + subscriptParser, + identifierParser + )(input) if (isNull(prop)) return [obj, input] const [exp, rest] = prop if (exp.isSubscript) { @@ -456,14 +493,22 @@ const subscriptParser = (input) => const memberExprParser = (input) => maybe( - parser.any(arrayParser, nonReservedIdParser, parenthesesParser)(input), + parser.any( + arrayParser, + nonReservedIdParser, + parenthesesParser + )(input), (val, rest) => { const obj = val const result = formMemberExpression(rest, obj) let [memExpr, _rest] = result memExpr = - memExpr.type === 'ExpressionStatement' ? memExpr.expression : memExpr - return memExpr.type === 'MemberExpression' ? [memExpr, _rest] : null + memExpr.type === 'ExpressionStatement' + ? memExpr.expression + : memExpr + return memExpr.type === 'MemberExpression' + ? [memExpr, _rest] + : null } ) @@ -480,7 +525,12 @@ const defineStmtParser = (input) => )(input), (val, rest) => { const [, , objID, , key, , value] = val - const definePropStmt = estemplate.defineProp(objID, key, value, false) + const definePropStmt = estemplate.defineProp( + objID, + key, + value, + false + ) return [definePropStmt, rest] } ) @@ -490,14 +540,19 @@ const defineStmtParser = (input) => const makeBlock = (mapBody, cbBody) => estemplate.blockStmt(mapBody.stmts.concat(cbBody)) -const makeReturnArray = (val) => estemplate.returnStmt(estemplate.array(val)) +const makeReturnArray = (val) => + estemplate.returnStmt(estemplate.array(val)) /* IO parsers */ // DOM statements inside parenthesis const parensDOMStmt = (input) => maybe( - parser.all(openParensParser, maybeDOMStmt, closeParensParser)(input), + parser.all( + openParensParser, + maybeDOMStmt, + closeParensParser + )(input), (val, rest) => { const [, expr] = val return [expr, rest] @@ -567,12 +622,20 @@ const ioStmtParser = ( makeBlock( mapBody, makeReturnArray( - parentObj.nextParams.concat(mapBody.propagate.concat(ioFunc)) + parentObj.nextParams.concat( + mapBody.propagate.concat(ioFunc) + ) ) ) ) - parentObj.nextParams = parentObj.nextParams.concat(mapBody.propagate) - const val_ = estemplate.ioBind(parentObj, callBack, parentObj.nextParams) + parentObj.nextParams = parentObj.nextParams.concat( + mapBody.propagate + ) + const val_ = estemplate.ioBind( + parentObj, + callBack, + parentObj.nextParams + ) return [val_, rest] }) @@ -583,7 +646,11 @@ const ioStmtParser = ( const noArgsCallParser = (input) => maybe( parser.all( - parser.any(memberExprParser, parenthesesParser, nonReservedIdParser), + parser.any( + memberExprParser, + parenthesesParser, + nonReservedIdParser + ), spaceParser, openParensParser, closeParensParser @@ -605,7 +672,9 @@ const maybeDOMStmt = (input) => parser.all(domMethodParser, spaceParser, argsParser)(input), (val, rest) => { const [domFunc, , args] = val - const callExpr = estemplate.expression(estemplate.fnCall(domFunc, args)) + const callExpr = estemplate.expression( + estemplate.fnCall(domFunc, args) + ) const expr = rest.str.startsWith('\n') || /^()()$/.test(rest.str) ? estemplate.expression(callExpr) @@ -624,7 +693,12 @@ const maybeBindStmt = (input) => parser.all( bindIDParser, reverseBindParser, - parser.any(fnCallParser, maybeDOMStmt, ioFuncName, nonReservedIdParser) + parser.any( + fnCallParser, + maybeDOMStmt, + ioFuncName, + nonReservedIdParser + ) )(input) const isIOorFuncCall = (maybeIOFunc) => { @@ -674,12 +748,17 @@ const formBindStmt = ( ) => { const cbParams = parentObj.nextParams const callBack = isEmptyArr(mapBody.stmts) - ? estemplate.lambda(cbParams, estemplate.array(cbParams.concat(cbBody))) + ? estemplate.lambda( + cbParams, + estemplate.array(cbParams.concat(cbBody)) + ) : estemplate.lambda( cbParams, makeBlock( mapBody, - makeReturnArray(cbParams.concat(mapBody.propagate).concat(cbBody)) + makeReturnArray( + cbParams.concat(mapBody.propagate).concat(cbBody) + ) ) ) parentObj = estemplate.ioBind(parentObj, callBack, cbParams) @@ -725,7 +804,14 @@ const bindStmt = (maybeBind, parentObj, bindBody, mapBody) => { args, nextParams ) - return formBindStmt(cbBody, parentObj, nextParams, rest, bindBody, mapBody) + return formBindStmt( + cbBody, + parentObj, + nextParams, + rest, + bindBody, + mapBody + ) } /* letStmtParser in IO */ @@ -752,16 +838,19 @@ const letParamsIOParser = (str, letStmtsArray = [], nextParams = []) => let var1 = val1, var2 = val2 */ const maybeLetStmt = (input, parentObj, bindBody, mapBody) => - maybe(parser.all(letParser, letParamsIOParser)(input), (val, rest) => { - const [, [letDeclarations, propagatedVals]] = val - mapBody.stmts = mapBody.stmts.concat(letDeclarations) - mapBody.propagate = - !isEmptyArr(mapBody.propagate) && - mapBody.propagate[0].name === propagatedVals[0].name - ? mapBody.propagate.concat(propagatedVals.slice(1)) - : mapBody.propagate.concat(propagatedVals) - return ioBodyParser(rest, parentObj, bindBody, mapBody) - }) + maybe( + parser.all(letParser, letParamsIOParser)(input), + (val, rest) => { + const [, [letDeclarations, propagatedVals]] = val + mapBody.stmts = mapBody.stmts.concat(letDeclarations) + mapBody.propagate = + !isEmptyArr(mapBody.propagate) && + mapBody.propagate[0].name === propagatedVals[0].name + ? mapBody.propagate.concat(propagatedVals.slice(1)) + : mapBody.propagate.concat(propagatedVals) + return ioBodyParser(rest, parentObj, bindBody, mapBody) + } + ) /* letStmtParser ends here */ @@ -829,7 +918,9 @@ const maybeStmt = (maybeVal, rest, parentObj, bindBody, mapBody) => { if (Array.isArray(handler)) { const [ioFunc, , args] = handler handler = ioFunc - handlerBody = estemplate.defaultIOThen(estemplate.ioCall(handler, args)) + handlerBody = estemplate.defaultIOThen( + estemplate.ioCall(handler, args) + ) } const val = getPredicate(methodName, value)(handlerBody) mapBody.stmts = mapBody.stmts.concat(val) @@ -840,7 +931,9 @@ const maybeStmt = (maybeVal, rest, parentObj, bindBody, mapBody) => { /* Handler function for delete and defineProp */ const makeMap = (parentObj, mapBody) => { const nextParams = parentObj.nextParams - const returnVal = makeReturnArray(nextParams.concat(mapBody.propagate)) + const returnVal = makeReturnArray( + nextParams.concat(mapBody.propagate) + ) const cbBody = makeBlock(mapBody, returnVal) const callBack = estemplate.lambda(nextParams, cbBody) const val = estemplate.ioMap(parentObj, callBack, nextParams) @@ -862,7 +955,12 @@ const maybeDefineStmt = (input, parentObj, bindBody, mapBody) => )(input), (val, rest) => { const [, , objID, , key, , value] = val - const definePropTmpl = estemplate.defineProp(objID, key, value, true) + const definePropTmpl = estemplate.defineProp( + objID, + key, + value, + true + ) mapBody.stmts = mapBody.stmts.concat(definePropTmpl) return ioBodyParser(rest, parentObj, bindBody, mapBody) } @@ -877,7 +975,10 @@ const maybeDeleteStmt = (input, parentObj, bindBody, mapBody) => )(input), (val, rest) => { const [deleteKeyword, , objProp] = val - const deleteTmpl = estemplate.unaryExpression(deleteKeyword, objProp) + const deleteTmpl = estemplate.unaryExpression( + deleteKeyword, + objProp + ) mapBody.stmts = mapBody.stmts.concat(deleteTmpl) return ioBodyParser(rest, parentObj, bindBody, mapBody) } @@ -887,14 +988,20 @@ const maybeDeleteStmt = (input, parentObj, bindBody, mapBody) => /* make final statement */ const getCbBody = (bindBody, parentObj) => { if (bindBody.length === 0) { - if (parentObj.type === 'Identifier') { return estemplate.fnCall(parentObj, []) } + if (parentObj.type === 'Identifier') { + return estemplate.fnCall(parentObj, []) + } return parentObj } const callBack = estemplate.lambda( parentObj.nextParams, estemplate.array([estemplate.fnCall(bindBody[0], [])]) ) - const val = estemplate.ioBind(parentObj, callBack, parentObj.nextParams) + const val = estemplate.ioBind( + parentObj, + callBack, + parentObj.nextParams + ) val.nextParams = parentObj.nextParams return getCbBody(bindBody.slice(1), val) } @@ -944,7 +1051,13 @@ const makeFinalStmt = (input, rest, parentObj, bindBody, mapBody) => { } /* final statement ends here */ -const maybeFinalStmt = (finalStmt, input, parentObj, bindBody, mapBody) => { +const maybeFinalStmt = ( + finalStmt, + input, + parentObj, + bindBody, + mapBody +) => { let [, rest] = finalStmt const result = spaceParser(rest) if (isNull(result)) { @@ -974,7 +1087,11 @@ const maybeIOCallStmt = (maybeIOCall, parentObj, bindBody, mapBody) => { const val = ioID return isEmptyArr(mapBody.stmts) ? ioBodyParser(rest, parentObj, bindBody.concat(val), mapBody) - : ioBodyParser(rest, makeMap(parentObj, mapBody), bindBody.concat(val)) + : ioBodyParser( + rest, + makeMap(parentObj, mapBody), + bindBody.concat(val) + ) } /* @@ -1049,11 +1166,17 @@ const ioParser = (input) => parser.all( nonReservedIdParser, equalSignParser, - parser.any(doParser, noArgsCallParser, ioStmtParser, nonReservedIdParser) + parser.any( + doParser, + noArgsCallParser, + ioStmtParser, + nonReservedIdParser + ) )(input), (initIO, rest) => { const [doID, , maybeDo] = initIO - const isId = notUndefined(maybeDo.type) && maybeDo.type === 'Identifier' + const isId = + notUndefined(maybeDo.type) && maybeDo.type === 'Identifier' const idNotMain = isId && doID.name !== 'main' if (idNotMain) return null const isFunc = @@ -1115,11 +1238,7 @@ const whereClauseParser = (input) => { do { const declParsed = maybe( parser.all( - parser.any( - declParser, - fnDeclParser, - fnWithGuardsParser - ), + parser.any(declParser, fnDeclParser, fnWithGuardsParser), maybeNewLineAndIndent )(input), (val, rest) => { @@ -1149,7 +1268,9 @@ const valueAndWhereClauseParser = (input) => const nonReturnStatements = ['IfStatement'] block.body = [ ...whereDecls, - nonReturnStatements.includes(v.type) ? v : estemplate.returnStmt(v) + nonReturnStatements.includes(v.type) + ? v + : estemplate.returnStmt(v) ] return [block, rest] } @@ -1173,9 +1294,8 @@ const declParser = (input) => )(input), (val, rest) => { const [id, , value] = val - const nonReturnStatements = ['BlockStatement'] return [ - nonReturnStatements.includes(value.type) + nonExprStatements.includes(value.type) ? estemplate.declaration( id, estemplate.lambdaCall([], [], value) @@ -1248,13 +1368,18 @@ const guardsParser = (input) => { )(input), (val, rest) => { const [, , , , defaultCase] = val - return [estemplate.ifStmt(estemplate.boolLiteral('true'), defaultCase, true), rest] + return [ + estemplate.ifStmt( + estemplate.boolLiteral('true'), + defaultCase, + true + ), + rest + ] } ) if (defaultCase) { - guards.push( - defaultCase[0] - ) + guards.push(defaultCase[0]) const rest = defaultCase[1] return [combineGuards(guards), rest] } else if (guards.length) { @@ -1273,7 +1398,10 @@ const fnDeclParser = (input) => )(input), (val, rest) => { const [funcID, paramsArr, , body] = val - return [estemplate.funcDeclaration(funcID, paramsArr, body), rest] + return [ + estemplate.funcDeclaration(funcID, paramsArr, body), + rest + ] } ) @@ -1287,7 +1415,16 @@ const fnWithGuardsParser = (input) => { )(input), (val, rest) => { const [funcID, paramsArr, , body] = val - return [estemplate.funcDeclaration(funcID, paramsArr, estemplate.blockStmt([body])), rest] + return [ + estemplate.funcDeclaration( + funcID, + paramsArr, + nonExprStatements.includes(body.type) + ? body + : estemplate.blockStmt([body]) + ), + rest + ] } ) } diff --git a/test/assert/whereClause.json b/test/assert/whereClause.json new file mode 100644 index 0000000..a4f9118 --- /dev/null +++ b/test/assert/whereClause.json @@ -0,0 +1,336 @@ +{ + "type": "Program", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "add5" + }, + "init": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [ + { + "type": "Identifier", + "name": "x" + } + ], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "n" + }, + "init": { + "type": "Literal", + "value": 5, + "raw": "5", + "sType": "number" + } + } + ], + "kind": "const" + }, + { + "type": "IfStatement", + "test": { + "type": "Literal", + "value": true, + "raw": "true", + "sType": "bool" + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "BinaryExpression", + "operator": "+", + "sType": "number", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Identifier", + "name": "n" + } + } + } + ] + }, + "alternate": null + } + ] + }, + "generator": false, + "expression": false + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "mul2" + }, + "init": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [ + { + "type": "Identifier", + "name": "x" + } + ], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "n" + }, + "init": { + "type": "Literal", + "value": 2, + "raw": "2", + "sType": "number" + } + } + ], + "kind": "const" + }, + { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": "===", + "sType": "bool", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Literal", + "value": 0, + "raw": "0", + "sType": "number" + } + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "Literal", + "value": "multiplicant can not be 0", + "raw": "multiplicant can not be 0", + "sType": "string" + } + } + ] + }, + "alternate": { + "type": "IfStatement", + "test": { + "type": "Literal", + "value": true, + "raw": "true", + "sType": "bool" + }, + "consequent": { + "type": "BlockStatement", + "body": [ + { + "type": "ReturnStatement", + "argument": { + "type": "BinaryExpression", + "operator": "*", + "sType": "number", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Identifier", + "name": "n" + } + } + } + ] + }, + "alternate": null + } + } + ] + }, + "generator": false, + "expression": false + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "mul2and4" + }, + "init": { + "type": "CallExpression", + "callee": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "x" + }, + "init": { + "type": "Literal", + "value": 2, + "raw": "2", + "sType": "number" + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "y" + }, + "init": { + "type": "Literal", + "value": 4, + "raw": "4", + "sType": "number" + } + } + ], + "kind": "const" + }, + { + "type": "ReturnStatement", + "argument": { + "type": "BinaryExpression", + "operator": "*", + "sType": "number", + "left": { + "type": "Identifier", + "name": "x" + }, + "right": { + "type": "Identifier", + "name": "y" + } + } + } + ] + }, + "generator": false, + "expression": false + }, + "arguments": [] + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "val10" + }, + "init": { + "type": "CallExpression", + "callee": { + "type": "ArrowFunctionExpression", + "id": null, + "params": [], + "body": { + "type": "BlockStatement", + "body": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "v" + }, + "init": { + "type": "Literal", + "value": 10, + "raw": "10", + "sType": "number" + } + } + ], + "kind": "const" + }, + { + "type": "ReturnStatement", + "argument": { + "type": "Identifier", + "name": "v" + } + } + ] + }, + "generator": false, + "expression": false + }, + "arguments": [] + } + } + ], + "kind": "const" + } + ], + "sourceType": "script" +} diff --git a/test/src/whereClause.cl b/test/src/whereClause.cl new file mode 100644 index 0000000..a17760b --- /dev/null +++ b/test/src/whereClause.cl @@ -0,0 +1,15 @@ +add5 x + | otherwise = x + n + where n = 5 + +mul2 x + | x == 0 = 'multiplicant can not be 0' + | otherwise = x * n + where n = 2 + +mul2and4 = x * y + where x = 2 + y = 4 + +val10 = v + where v = 10 From 9acd1ce51b2f9d7a2ce9f23168b7f87309f82c2e Mon Sep 17 00:00:00 2001 From: Sumanth Yedoti Date: Fri, 29 Apr 2022 11:38:13 +0530 Subject: [PATCH 12/12] Fix guardsParser by replacing binaryparser with valueParser and add languageContraints --- lib/languageConstructs.js | 3 ++- lib/parser.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/languageConstructs.js b/lib/languageConstructs.js index 6ae72aa..10fa912 100644 --- a/lib/languageConstructs.js +++ b/lib/languageConstructs.js @@ -20,7 +20,8 @@ const languageConstructs = { do: true, return: true, delete: true, - defineProp: true + defineProp: true, + otherwise: true } const reservedFnCalls = { diff --git a/lib/parser.js b/lib/parser.js index 12361c2..711be1a 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1344,7 +1344,7 @@ const guardsParser = (input) => { parser.all( maybeNewLineAndIndent, vBarParser, - binaryExprParser, + valueParser, equalSignParser, valueParser )(input),