diff --git a/.gitignore b/.gitignore index 4cf7346..229ee80 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules coverage/ .tern-port test/src/**/*.js +playground diff --git a/lib/astupdate.js b/lib/astupdate.js index c7fa4aa..58f9790 100644 --- a/lib/astupdate.js +++ b/lib/astupdate.js @@ -6,7 +6,7 @@ const updateComment = require('./updateComment.js') let patterns = [] -const buildExplicitCases = (funcdecl, patterndecls) => { +const buildExplicitCases = (patterndecls) => { const patterncases = [] let flag = false for (const pattern of patterndecls) { @@ -44,10 +44,10 @@ const buildDefaultCase = (funcdecl) => { return defaultcase } -const formMultiPatternAst = (funcdecl, patterndecls, ast) => { +const formMultiPatternAst = (funcdecl, patterndecls) => { const funcdeclbody = { type: 'BlockStatement', body: [] } const blockstmtbody = { type: 'SwitchStatement', cases: [] } - const [cases, flag] = buildExplicitCases(funcdecl, patterndecls) + const [cases, flag] = buildExplicitCases(patterndecls) const testTempl = funcdecl.declarations[0].init.params[0] blockstmtbody.discriminant = testTempl if (flag === true) { @@ -107,12 +107,12 @@ const formFunctionAst = (funcdecl, patterndecls) => { const processSubTree = (decl) => { if (isArrowFuncDecl(decl)) { if (isFunction(decl)) { - // funcdeclns + // function declarations const funcast = formFunctionAst(decl, patterns) patterns = [] return funcast } else { - // patternfuncdeclns + // pattern matched function declarations patterns.push(decl) return null } @@ -120,17 +120,15 @@ const processSubTree = (decl) => { return decl } -const updateAst = (mayBeAst) => { - const ast = mayBeAst - let newbody = [] +const updateAst = (ast) => { + let newBody = [] const declarations = ast.body - declarations.forEach((decl) => { const subtree = processSubTree(decl) - if (subtree !== null) newbody.push(subtree) + if (subtree !== null) newBody.push(subtree) }) - newbody = updateComment(newbody, ast) - ast.body = newbody + newBody = updateComment(newBody) + ast.body = newBody return ast } diff --git a/lib/basicParsers.js b/lib/basicParsers.js index ddee4aa..5489843 100644 --- a/lib/basicParsers.js +++ b/lib/basicParsers.js @@ -16,12 +16,14 @@ const { /* Predefined regexes */ const spaceRegex = () => /^([ \t]+)((.|\n)*)$/ -const returnRegex = () => /^((?:\/\/.*)?[ \t]*\n)((.|\n)*)$/ // consume inline comment if any +const returnRegex = () => /^(\n+)((.|\n)*)$/ const idRegex = () => /^([_a-zA-Z]+[a-zA-Z0-9_]*)((.|\n)*)$/ -const numRegex = () => /^((?:\d+(?:\.\d*)?|\.\d+)(?:[e][+-]?\d+)?)((.|\n)*)$/ +const numRegex = () => + /^((?:\d+(?:\.\d*)?|\.\d+)(?:[e][+-]?\d+)?)((.|\n)*)$/ const boolRegex = () => /^(true|false)((.|\n)*)$/ const stringRegex = () => /^('[^\\\n']*(?:\\.[^\\\n']*)*')((.|\n)*)$/ -const binaryOperatorRegex = () => /^(\+\+|\+|-|%|\/|\*|<<|<=|<|>>>|>>|>=|>|&&|&|\|\||\||\^|==|!=)((.|\n)*)$/ +const binaryOperatorRegex = () => + /^(\+\+|\+|-|%|\/|\*|<<|<=|<|>>>|>>|>=|>|&&|&|\|\||\||\^|==|!=)((.|\n)*)$/ const unaryOperatorRegex = () => /^(:type|-|!)((.|\n)*)$/ const openCurlyRegex = () => /^({)((.|\n)*)$/ const closeCurlyRegex = () => /^(})((.|\n)*)$/ @@ -31,11 +33,13 @@ const openParensRegex = () => /^(\()((.|\n)*)$/ const closeParensRegex = () => /^(\))((.|\n)*)$/ const commaRegex = () => /^(,)((.|\n)*)$/ const colonRegex = () => /^(:)((.|\n)*)$/ -const singleLineCommentRegex = () => /^((\/\/)(.*)(\n|))((.|\n)*)$/ -const multiLineCommentRegex = () => /^((\/\*)((.|\n)*?)(\*\/))((.|\n)*)$/ +const singleLineCommentRegex = () => /^((\/\/)(.*)(?=\n|))((.|\n)*)$/ +const multiLineCommentRegex = () => + /^((\/\*)((.|\n)*?)(\*\/))((.|\n)*)$/ const nullRegex = () => /^(null)((.|\n)*)$/ const deleteRegex = () => /^(delete)((.|\n)*)$/ -const regexRegex = () => /^(\/((.)+)\/((?!(?:.\B)*(.)(?:\B.)*\5)[gmuiy]+\b)*)((.|\n)*)$/ +const regexRegex = () => + /^(\/((.)+)\/((?!(?:.\B)*(.)(?:\B.)*\5)[gmuiy]+\b)*)((.|\n)*)$/ const equalSignRegex = () => /^(\s+=\s+)((.|\n)*)$/ const thinArrowRegex = () => /^(\s*->\s+)((.|\n)*)$/ const reverseBindRegex = () => /^(\s*<-\s+)((.|\n)*)$/ @@ -55,201 +59,254 @@ const libNameRegex = () => /^(node-core|browser-core)((.|\n)*)$/ /* All required parsers are created below */ -const idParser = input => parser.regex(idRegex)(input) +const idParser = (input) => parser.regex(idRegex)(input) -const numParser = input => parser.regex(numRegex)(input) +const numParser = (input) => parser.regex(numRegex)(input) + +const spaceParser = (input) => parser.regex(spaceRegex)(input) -const spaceParser = input => parser.regex(spaceRegex)(input) - -const equalSignParser = input => parser.regex(equalSignRegex)(input) - -const thinArrowParser = input => parser.regex(thinArrowRegex)(input) - -const reverseBindParser = input => parser.regex(reverseBindRegex)(input) - -const slashParser = input => parser.regex(slashRegex)(input) - -const letParser = input => parser.regex(letRegex)(input) - -const inParser = input => parser.regex(inRegex)(input) - -const dotParser = input => parser.regex(dotRegex)(input) - -const ifParser = input => parser.regex(ifRegex)(input) -const thenParser = input => parser.regex(thenRegex)(input) -const elseParser = input => parser.regex(elseRegex)(input) - -const doParser = input => parser.regex(doRegex)(input) - -const returnKeywordParser = input => parser.regex(returnKeywordRegex)(input) - -const includeKeywordParser = input => parser.regex(includeKeywordRegex)(input) - -const definePropParser = input => parser.regex(definePropRegex)(input) - -const returnParser = input => maybe( - returnRegex().exec(input.str), - (m, newLine, rest) => returnRest(newLine, input, rest, { name: 'return', value: 1 }) -) - -const numberParser = input => maybe( - numParser(input), - (num, rest) => [estemplate.literal(num), rest] -) - -const nonReservedIdParser = input => maybe( - idParser(input), - (name, rest) => (isLanguageConstruct(name) || isStaticIOMethod(name) || isIOMethod(name) || isDOMmethod(name)) - ? null - : [estemplate.identifier(name), rest] -) - -const identifierParser = input => maybe( - idParser(input), - (name, rest) => [estemplate.identifier(name), rest] -) - -const document_ = id => estemplate.memberExpression(estemplate.identifier('document'), estemplate.identifier(id)) - -const domMethodParser = input => maybe( - idParser(input), - (name, rest) => !(isDOMmethod(name)) ? null : [document_(isDOMmethod(name)), rest] -) - -const ioFuncNameParser = input => maybe( - idParser(input), - (name, rest) => isStaticIOMethod(name) ? [estemplate.identifier(name), rest] : null -) - -const ioMethodNameParser = input => maybe( - idParser(input), - (name, rest) => isIOMethod(name) ? [estemplate.identifier(name), rest] : null -) - -const nullParser = input => maybe( - parser.regex(nullRegex)(input), - (val, rest) => [estemplate.nullLiteral(val), rest] -) - -const stringParser = input => maybe( - parser.regex(stringRegex)(input), - (string, rest) => [estemplate.stringLiteral(unescape(string)), rest] -) - -const booleanParser = input => maybe( - parser.regex(boolRegex)(input), - (bool, rest) => [estemplate.boolLiteral(bool), rest] -) - -const regexParser = input => maybe( - regexRegex().exec(input.str), - (m, regex, pattern, b, flags, _, rest) => returnRest(estemplate.regex(regex, pattern, flags), input, rest, - { name: 'column', value: pattern.length }) -) - -const openParensParser = input => maybe( - parser.regex(openParensRegex)(input), - (openParens, rest) => [openParens, rest] -) - -const closeParensParser = input => maybe( - parser.regex(closeParensRegex)(input), - (closeParens, rest) => [closeParens, rest] -) +const equalSignParser = (input) => parser.regex(equalSignRegex)(input) + +const thinArrowParser = (input) => parser.regex(thinArrowRegex)(input) + +const reverseBindParser = (input) => + parser.regex(reverseBindRegex)(input) + +const slashParser = (input) => parser.regex(slashRegex)(input) + +const letParser = (input) => parser.regex(letRegex)(input) + +const inParser = (input) => parser.regex(inRegex)(input) + +const dotParser = (input) => parser.regex(dotRegex)(input) + +const ifParser = (input) => parser.regex(ifRegex)(input) +const thenParser = (input) => parser.regex(thenRegex)(input) +const elseParser = (input) => parser.regex(elseRegex)(input) + +const doParser = (input) => parser.regex(doRegex)(input) + +const returnKeywordParser = (input) => + parser.regex(returnKeywordRegex)(input) + +const includeKeywordParser = (input) => + parser.regex(includeKeywordRegex)(input) + +const definePropParser = (input) => + parser.regex(definePropRegex)(input) + +const returnParser = (input) => + maybe(returnRegex().exec(input.str), (m, newLine, rest) => + returnRest(newLine, input, rest, { + name: 'return', + value: newLine.length + }) + ) + +const numberParser = (input) => + maybe(numParser(input), (num, rest) => [ + estemplate.literal(num), + rest + ]) + +const nonReservedIdParser = (input) => + maybe(idParser(input), (name, rest) => + isLanguageConstruct(name) || + isStaticIOMethod(name) || + isIOMethod(name) || + isDOMmethod(name) + ? null + : [estemplate.identifier(name), rest] + ) + +const identifierParser = (input) => + maybe(idParser(input), (name, rest) => [ + estemplate.identifier(name), + rest + ]) + +const document_ = (id) => + estemplate.memberExpression( + estemplate.identifier('document'), + estemplate.identifier(id) + ) + +const domMethodParser = (input) => + maybe(idParser(input), (name, rest) => + !isDOMmethod(name) ? null : [document_(isDOMmethod(name)), rest] + ) + +const ioFuncNameParser = (input) => + maybe(idParser(input), (name, rest) => + isStaticIOMethod(name) ? [estemplate.identifier(name), rest] : null + ) + +const ioMethodNameParser = (input) => + maybe(idParser(input), (name, rest) => + isIOMethod(name) ? [estemplate.identifier(name), rest] : null + ) + +const nullParser = (input) => + maybe(parser.regex(nullRegex)(input), (val, rest) => [ + estemplate.nullLiteral(val), + rest + ]) + +const stringParser = (input) => + maybe(parser.regex(stringRegex)(input), (string, rest) => [ + estemplate.stringLiteral(unescape(string)), + rest + ]) + +const booleanParser = (input) => + maybe(parser.regex(boolRegex)(input), (bool, rest) => [ + estemplate.boolLiteral(bool), + rest + ]) + +const regexParser = (input) => + maybe( + regexRegex().exec(input.str), + (m, regex, pattern, b, flags, _, rest) => + returnRest(estemplate.regex(regex, pattern, flags), input, rest, { + name: 'column', + value: pattern.length + }) + ) + +const openParensParser = (input) => + maybe(parser.regex(openParensRegex)(input), (openParens, rest) => [ + openParens, + rest + ]) + +const closeParensParser = (input) => + maybe(parser.regex(closeParensRegex)(input), (closeParens, rest) => [ + closeParens, + rest + ]) + +const openCurlyBraceParser = (input) => + maybe(parser.regex(openCurlyRegex)(input), (openCurlyBrace, rest) => [ + openCurlyBrace, + rest + ]) + +const closeCurlyBraceParser = (input) => + maybe( + parser.regex(closeCurlyRegex)(input), + (closeCurlyBrace, rest) => [closeCurlyBrace, rest] + ) + +const openSquareBracketParser = (input) => + maybe( + parser.regex(openSquareBracketRegex)(input), + (openSquareBracket, rest) => [openSquareBracket, rest] + ) + +const closeSquareBracketParser = (input) => + maybe( + parser.regex(closeSquareBracketRegex)(input), + (closeSquareBracket, rest) => [closeSquareBracket, rest] + ) + +const commaParser = (input) => + maybe(parser.regex(commaRegex)(input), (comma, rest) => [ + comma, + rest + ]) + +const colonParser = (input) => + maybe(parser.regex(colonRegex)(input), (colon, rest) => [ + colon, + rest + ]) + +const singleLineCommentParser = (input) => + maybe(singleLineCommentRegex().exec(input.str), (...vals) => { + const [, , , val, rest] = vals + return returnRest(estemplate.comment('Line', val), input, rest, { + name: 'return', + value: 0 + }) + }) -const openCurlyBraceParser = input => maybe( - parser.regex(openCurlyRegex)(input), - (openCurlyBrace, rest) => [openCurlyBrace, rest] -) - -const closeCurlyBraceParser = input => maybe( - parser.regex(closeCurlyRegex)(input), - (closeCurlyBrace, rest) => [closeCurlyBrace, rest] -) - -const openSquareBracketParser = input => maybe( - parser.regex(openSquareBracketRegex)(input), - (openSquareBracket, rest) => [openSquareBracket, rest] -) - -const closeSquareBracketParser = input => maybe( - parser.regex(closeSquareBracketRegex)(input), - (closeSquareBracket, rest) => [closeSquareBracket, rest] -) - -const commaParser = input => maybe( - parser.regex(commaRegex)(input), - (comma, rest) => [comma, rest] -) - -const colonParser = input => maybe( - parser.regex(colonRegex)(input), - (colon, rest) => [colon, rest] -) - -const singleLineCommentParser = input => maybe( - singleLineCommentRegex().exec(input.str), - (...vals) => { - const [, comment, , , , rest] = vals - const val = comment.slice(2) - return returnRest(estemplate.comment('Line', val), input, rest, { name: 'return', value: 1 }) - } -) - -const multiLineCommentParser = input => maybe( - multiLineCommentRegex().exec(input.str), - (...vals) => { +const multiLineCommentParser = (input) => + maybe(multiLineCommentRegex().exec(input.str), (...vals) => { const [, comment, , , , , rest] = vals - const lineCount = notNull(comment.match(/\n/g)) ? comment.match(/\n/g).length : 0 + const lineCount = notNull(comment.match(/\n/g)) + ? comment.match(/\n/g).length + : 0 const val = comment.slice(2, comment.length - 2) - return returnRest(estemplate.comment('Block', val), input, rest, { name: 'return', value: lineCount }) - } -) - -const binaryOperatorParser = input => maybe( - parser.all(maybeSpace, parser.regex(binaryOperatorRegex), maybeSpace)(input), - (val, rest) => { - const [sp1, op, sp2] = val - return returnRest(op, input, rest.str, { name: 'column', value: (sp1 + op + sp2).length }) + return returnRest(estemplate.comment('Block', val), input, rest, { + name: 'return', + value: lineCount + }) }) -const unaryOperatorParser = input => maybe( - parser.regex(unaryOperatorRegex)(input), - (operator, rest) => [operator, rest] -) - -const maybeSpace = input => { +const binaryOperatorParser = (input) => + maybe( + parser.all( + maybeSpace, + parser.regex(binaryOperatorRegex), + maybeSpace + )(input), + (val, rest) => { + const [sp1, op, sp2] = val + return returnRest(op, input, rest.str, { + name: 'column', + value: (sp1 + op + sp2).length + }) + } + ) + +const unaryOperatorParser = (input) => + maybe(parser.regex(unaryOperatorRegex)(input), (operator, rest) => [ + operator, + rest + ]) + +const maybeSpace = (input) => { let val = '' const space = spaceParser(input) let rest = input if (notNull(space)) [val, rest] = space - return returnRest(val, input, rest.str, { name: 'column', value: val.length }) + return returnRest(val, input, rest.str, { + name: 'column', + value: val.length + }) } -const maybeNewLine = input => maybe( - parser.all(returnParser, spaceParser)(input), - (val, rest) => [val, rest] -) - -const maybeNewLineAndIndent = input => parser.any(maybeNewLine, maybeSpace)(input) - -const libNameParser = input => maybe( - parser.regex(libNameRegex)(input), - (val, rest) => [val, rest] -) - -const includeParser = input => maybe( - parser.all(includeKeywordParser, spaceParser, libNameParser)(input), - (val, rest) => [val[2], rest] -) - -const deleteKeywordParser = input => maybe( - parser.regex(deleteRegex)(input), - (operator, rest) => [operator, rest] -) - -const emptyArgsParser = input => { - const result = parser.all(openParensParser, maybeSpace, closeParensParser)(input) +const maybeNewLine = (input) => + maybe(parser.all(returnParser, spaceParser)(input), (val, rest) => [ + val, + rest + ]) + +const maybeNewLineAndIndent = (input) => + parser.any(maybeNewLine, maybeSpace)(input) + +const libNameParser = (input) => + maybe(parser.regex(libNameRegex)(input), (val, rest) => [val, rest]) + +const includeParser = (input) => + maybe( + parser.all(includeKeywordParser, spaceParser, libNameParser)(input), + (val, rest) => [val[2], rest] + ) + +const deleteKeywordParser = (input) => + maybe(parser.regex(deleteRegex)(input), (operator, rest) => [ + operator, + rest + ]) + +const emptyArgsParser = (input) => { + const result = parser.all( + openParensParser, + maybeSpace, + closeParensParser + )(input) if (isNull(result)) return null const [, rest] = result return [[], rest] diff --git a/lib/cli.js b/lib/cli.js index ea0b61a..7723738 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -7,10 +7,12 @@ const chokidar = require('chokidar') const includeParser = require('./basicParsers').includeParser const parser = require('./parser').programParser const inferTypes = require('./typeInference') + const version = require(path.join( __dirname, '../package.json' )).version + const help = ` Usage : clean Compile and run @@ -53,33 +55,33 @@ const makeErrStr = (obj, infile) => { return whatErr + whereErr + errMsg } +const importCore = (libName) => { + const importPath = path.join(__dirname, '/include/') + return ( + fs.readFileSync(importPath + 'core.js', 'utf8').toString() + + fs.readFileSync(importPath + libName + '.js', 'utf8').toString() + + '\n'.repeat(2) + + (libName === 'node-core' ? 'global.IO = IO' : 'window.IO = IO') + + '\n'.repeat(2) + ) +} + const evalFunction = (argsObj, infile, outfile) => { - let input = { + const input = { str: fs.readFileSync(infile, 'utf8').toString(), line: 1, column: 0 } - const parseResult = includeParser(input) - let importCore = '' - if (parseResult !== null) { - const [libName, newInput] = parseResult - const importPath = path.join(__dirname, '/include/') - importCore = - fs.readFileSync(importPath + 'core.js', 'utf8').toString() + - fs.readFileSync(importPath + libName + '.js', 'utf8').toString() + - '\n'.repeat(2) - importCore += - (libName === 'node-core' ? 'global.IO = IO' : 'window.IO = IO') + - '\n'.repeat(2) - input = newInput - } - const tree = parser(input) + const includeParsedResult = includeParser(input) + const [libName = null, program = input] = includeParsedResult || [] + const core = libName ? importCore(libName) : '' + const tree = parser(program) if (tree.error) showAndExit(makeErrStr(tree, infile)) if (argsObj.ast) showAndExit(JSON.stringify(tree, null, 2)) const maybeTypeErr = argsObj.t ? tree.body : inferTypes(tree.body) if (maybeTypeErr.error) showAndExit(JSON.stringify(maybeTypeErr)) const out = escodegen.generate(tree, { format, comment: true }) - fs.writeFileSync(outfile, importCore + out + '\n', 'utf8') + fs.writeFileSync(outfile, core + out + '\n', 'utf8') const commandLineArgs = argsObj._.slice(1) if (!argsObj.o) { const mayBeCliArgs = @@ -163,11 +165,12 @@ const removeFile = (outfile, changedInpfile) => { } module.exports = (argsObj) => { - if (argsObj.v) showAndExit(version) - if (argsObj.h || argsObj.help) showAndExit(help) const infile = argsObj._[0] const watchFolder = argsObj.w - if (!infile && !watchFolder) showAndExit(help) + if (!infile && !watchFolder) { + if (argsObj.v) showAndExit(version) + showAndExit(help) + } const outfile = argsObj.o || infile.replace(/\.cl$/, '.js') if (infile) evalFunction(argsObj, infile, outfile) diff --git a/lib/estemplate.js b/lib/estemplate.js index 43b5809..d6ec7ee 100644 --- a/lib/estemplate.js +++ b/lib/estemplate.js @@ -4,7 +4,9 @@ const notUndefined = require('./utilityFunctions').notUndefined const estemplate = {} const extractExpr = (token) => - notUndefined(token) && notNull(token) && token.type === 'ExpressionStatement' + notUndefined(token) && + notNull(token) && + token.type === 'ExpressionStatement' ? token.expression : token diff --git a/lib/jsFunctions.js b/lib/jsFunctions.js index 896402f..5dd6d27 100644 --- a/lib/jsFunctions.js +++ b/lib/jsFunctions.js @@ -1,4 +1,8 @@ -/* This file contains JavaScript inbuilt functions, their accepted types and return types */ +/** + * This file contains JavaScript inbuilt functions, their accepted types and + * return types + */ + const jsFunctions = { Number: { type: 'function', diff --git a/lib/languageConstructs.js b/lib/languageConstructs.js index c3cc705..59a07e1 100644 --- a/lib/languageConstructs.js +++ b/lib/languageConstructs.js @@ -1,4 +1,7 @@ -/* This file contains language keywords supported by clean */ +/** + * This file contains language keywords supported by clean + */ + const languageConstructs = { if: true, then: true, diff --git a/lib/parser.js b/lib/parser.js index af669c7..af76912 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,27 +1,28 @@ -/* - Parser - Parses clean source. Generates clean AST. -*/ - -/* - All parsers take an input object and return an array of two values: - val1 = An ECMAScript compliant syntax tree of the input that was parsed - val2 = An object with the following fields: - str: rest of the string - line: line number - column: column number -*/ +/** + * Parses clean source. Generates clean AST. + * + * + * All parsers take an input object and return an array of two values: + * - val1 = An ECMAScript compliant syntax tree of the input that was parsed + * - val2 = An object with the following fields: + * - str: rest of the string + * - line: line number + * - column: column number + * + * + * All statements with `.expression` are used to unwrap an expression from within + * an `ExpressionStatement` + * All IO is converted into a syntax tree that can be handled by + * `node-core`/`browser-core` + */ -/* - All statements with `.expression` are used to unwrap an expression from within an `ExpressionStatement` - All IO is converted into a syntax tree that can be handled by `node-core`/`browser-core` -*/ const estemplate = require('./estemplate') const updateAst = require('./astupdate') const parser = require('./parserObject') const base = require('./basicParsers') const utils = require('./utilityFunctions') const errorMsg = require('./errors') + /* Utility Functions */ const { maybe, @@ -34,49 +35,50 @@ const { precedence, associativity } = utils + /* Base Parsers */ const { - returnParser, - spaceParser, - maybeSpace, - maybeNewLineAndIndent, - numberParser, - nonReservedIdParser, - identifierParser, - domMethodParser, - nullParser, - stringParser, + binaryOperatorParser, booleanParser, - regexParser, - openParensParser, - closeParensParser, - openCurlyBraceParser, closeCurlyBraceParser, - openSquareBracketParser, + closeParensParser, closeSquareBracketParser, - commaParser, colonParser, - equalSignParser, - thinArrowParser, + commaParser, + definePropParser, + deleteKeywordParser, + doParser, + domMethodParser, dotParser, - singleLineCommentParser, - multiLineCommentParser, - binaryOperatorParser, - unaryOperatorParser, - letParser, - inParser, - ifParser, - thenParser, elseParser, - slashParser, - reverseBindParser, - doParser, + emptyArgsParser, + equalSignParser, + identifierParser, + ifParser, + inParser, ioFuncNameParser, ioMethodNameParser, + letParser, + maybeNewLineAndIndent, + maybeSpace, + multiLineCommentParser, + nonReservedIdParser, + nullParser, + numberParser, + openCurlyBraceParser, + openParensParser, + openSquareBracketParser, + regexParser, returnKeywordParser, - deleteKeywordParser, - emptyArgsParser, - definePropParser + returnParser, + reverseBindParser, + singleLineCommentParser, + slashParser, + spaceParser, + stringParser, + thenParser, + thinArrowParser, + unaryOperatorParser } = base const unaryExprParser = (input) => @@ -102,7 +104,9 @@ const formBinaryExpr = (opStack, operandStack, input, rest) => { const opStackTop = opStack[opStack.length - 1] if (opStackTop === '$') { const binExpr = operandStack.pop() - return binExpr.type === 'BinaryExpression' ? [binExpr, input] : null // return final expression + return binExpr.type === 'BinaryExpression' + ? [binExpr, input] + : null // return final expression } const [right, left, op] = [ operandStack.pop(), @@ -247,7 +251,9 @@ const lambdaParser = (input) => /* Helper functions for fnCallParser */ const argsParser = (input, argArray = []) => { const maybeArg = expressionParser(input) - if (isNull(maybeArg)) return isEmptyArr(argArray) ? null : [argArray, input] + if (isNull(maybeArg)) { + return isEmptyArr(argArray) ? null : [argArray, input] + } const [arg, rest] = maybeArg argArray = argArray.concat(arg) const result = spaceParser(rest) @@ -406,9 +412,19 @@ const formMemberExpression = (input, obj) => { const prop = parser.any(dotParser, subscriptParser, identifierParser)(input) if (isNull(prop)) return [obj, input] const [exp, rest] = prop - if (exp.isSubscript) { return formMemberExpression(rest, estemplate.subscriptExpression(obj, exp)) } + if (exp.isSubscript) { + return formMemberExpression( + rest, + estemplate.subscriptExpression(obj, exp) + ) + } exp.isSubscript = false - if (exp.type === 'Identifier') { return formMemberExpression(rest, estemplate.memberExpression(obj, exp)) } + if (exp.type === 'Identifier') { + return formMemberExpression( + rest, + estemplate.memberExpression(obj, exp) + ) + } return formMemberExpression(rest, obj) } @@ -439,7 +455,7 @@ const memberExprParser = (input) => const result = formMemberExpression(rest, obj) let [memExpr, _rest] = result memExpr = - memExpr.type === 'ExpressionStatement' ? memExpr.expression : memExpr + memExpr.type === 'ExpressionStatement' ? memExpr.expression : memExpr return memExpr.type === 'MemberExpression' ? [memExpr, _rest] : null } ) @@ -691,7 +707,9 @@ const bindStmt = (maybeBind, parentObj, bindBody, mapBody) => { val.nextParams = isIOorFunc ? nextParams : val.nextParams return ioBodyParser(rest, val, bindBody, mapBody) } - if (!isEmptyArr(mapBody.stmts)) { nextParams = parentObj.nextParams.concat(nextParams) } + if (!isEmptyArr(mapBody.stmts)) { + nextParams = parentObj.nextParams.concat(nextParams) + } const cbBody = makeBind( isIOorFunc, isIOCall, @@ -962,10 +980,20 @@ const ioBodyParser = ( mapBody = { stmts: [], propagate: [] } ) => { const finalStmt = returnParser(input) - if (notNull(finalStmt)) { return maybeFinalStmt(finalStmt, input, parentObj, bindBody, mapBody) } + if (notNull(finalStmt)) { + return maybeFinalStmt( + finalStmt, + input, + parentObj, + bindBody, + mapBody + ) + } const bind = maybeBindStmt(input) - if (notNull(bind)) return bindStmt(bind, parentObj, bindBody, mapBody) + if (notNull(bind)) { + return bindStmt(bind, parentObj, bindBody, mapBody) + } if (!isEmptyObj(parentObj)) { const val = parser.any( @@ -979,10 +1007,14 @@ const ioBodyParser = ( } const ioStmt = ioStmtParser(input, parentObj, mapBody) - if (notNull(ioStmt)) return ioBodyParser(ioStmt[1], ioStmt[0], bindBody) + if (notNull(ioStmt)) { + return ioBodyParser(ioStmt[1], ioStmt[0], bindBody) + } const ioCall = nonReservedIdParser(input) - if (notNull(ioCall)) { return maybeIOCallStmt(ioCall, parentObj, bindBody, mapBody) } + if (notNull(ioCall)) { + return maybeIOCallStmt(ioCall, parentObj, bindBody, mapBody) + } return null } @@ -1149,8 +1181,7 @@ const makeErrorObj = (errObj) => { } const programParser = (input, ast = estemplate.ast()) => { - const [, rest] = ['', input] - const result = statementParser(rest) + const result = statementParser(input) if (isNull(result)) { const errObj = JSON.parse(JSON.stringify(parser.unParsed)) parser.unParsed = { line: 1, column: 0, regex: [], error: false } @@ -1160,6 +1191,7 @@ const programParser = (input, ast = estemplate.ast()) => { if (typeof result[0] !== 'string') ast.body.push(result[0]) return programParser(result[1], ast) } + /* Module Exports programParser */ module.exports = { programParser, diff --git a/lib/updateComment.js b/lib/updateComment.js index 5ec5e1d..84c61d6 100644 --- a/lib/updateComment.js +++ b/lib/updateComment.js @@ -1,28 +1,30 @@ -const updateComment = (body, ast, commentArr = []) => { - body = body - .map((obj, index, array) => { - if (obj.type === 'Line' || obj.type === 'Block') { - commentArr.push(obj) - if (array[index + 1] === undefined) { - if (index - commentArr.length === -1) ast.leadingComments = commentArr - else array[index - commentArr.length].trailingComments = commentArr - return undefined - } - if ( - !( - array[index + 1].type === 'Line' || - array[index + 1].type === 'Block' - ) +const isCommentBlock = (block) => { + return block.type === 'Line' || block.type === 'Block' +} + +const updateComment = (body, comments = []) => { + return body + .map((obj, index) => { + if (isCommentBlock(obj)) { + comments.push(obj) + if (obj.isInLine && body[index - 1] !== undefined) { + body[index - 1].trailingComments = comments + comments = [] + } else if (body[index + 1] === undefined && index > 0) { + body[index - comments.length].trailingComments = comments + } else if ( + body[index + 1] !== undefined && + !isCommentBlock(body[index + 1]) ) { - array[index + 1].leadingComments = commentArr - commentArr = [] + body[index + 1].leadingComments = comments + comments = [] } + delete obj.isInLine return undefined } return obj }) .filter((stmt) => stmt !== undefined) - return body } /* Module Exports updateComment */ diff --git a/lib/utilityFunctions.js b/lib/utilityFunctions.js index 2caec4e..6b28c4b 100644 --- a/lib/utilityFunctions.js +++ b/lib/utilityFunctions.js @@ -20,16 +20,22 @@ const unescape = (str) => .replace(/\\r/g, '\r') .replace(/\\'/g, "'") +let isInLine = false const returnRest = (val, input, rest, field) => { const output = JSON.parse(JSON.stringify(input)) + if (val.type === 'Line') { + val.isInLine = isInLine + } if (field) { const value = field.value switch (field.name) { case 'return': + isInLine = false output.column = 0 output.line += value break case 'column': + isInLine = true output.column += value break } @@ -49,12 +55,12 @@ const isEmptyObj = (obj) => { const isEmptyArr = (arr) => arr.toString() === '' const isNull = (...vals) => - vals.reduce((acc, v1, v2) => v1 === null && acc, true) + vals.reduce((acc, v1) => v1 === null && acc, true) const isUndefined = (...vals) => !notUndefined(...vals) const notNull = (...vals) => !isNull(...vals) const notUndefined = (...vals) => - vals.reduce((acc, v1, v2) => v1 !== undefined && acc, true) + vals.reduce((acc, v1) => v1 !== undefined && acc, true) /* Functions for the binary expression parser */ const precedence = (operator) => opSpec[operator].prec const associativity = (operator) => opSpec[operator].assoc diff --git a/test/assert/basicExamples.json b/test/assert/basicExamples.json index 320a464..831e4f9 100644 --- a/test/assert/basicExamples.json +++ b/test/assert/basicExamples.json @@ -862,9 +862,9 @@ "leadingComments": [ { "type": "Line", - "value": " BOOLEAN DECLARATION\n", + "value": " BOOLEAN DECLARATION", "cursorLoc": { - "line": 34, + "line": 33, "column": 0 } } @@ -1854,9 +1854,9 @@ "leadingComments": [ { "type": "Line", - "value": " ARRAYS\n", + "value": " ARRAYS", "cursorLoc": { - "line": 68, + "line": 67, "column": 0 } } @@ -2245,9 +2245,9 @@ "leadingComments": [ { "type": "Line", - "value": "OBJECTS\n", + "value": "OBJECTS", "cursorLoc": { - "line": 76, + "line": 75, "column": 0 } } @@ -2629,9 +2629,9 @@ "leadingComments": [ { "type": "Line", - "value": " ARRAY-SUBSCRIPTING -- MEMBER-EXPRESSIONS\n", + "value": " ARRAY-SUBSCRIPTING -- MEMBER-EXPRESSIONS", "cursorLoc": { - "line": 104, + "line": 103, "column": 0 } } @@ -2892,9 +2892,9 @@ "leadingComments": [ { "type": "Line", - "value": " Member check\n", + "value": " Member check", "cursorLoc": { - "line": 111, + "line": 110, "column": 0 } } @@ -3470,7 +3470,15 @@ "trailingComments": [ { "type": "Line", - "value": " should fail\n", + "value": " should fail", + "cursorLoc": { + "line": 126, + "column": 0 + } + }, + { + "type": "Line", + "value": "boolCheck5 = 5 < 'string'", "cursorLoc": { "line": 127, "column": 0 @@ -3478,7 +3486,7 @@ }, { "type": "Line", - "value": "boolCheck5 = 5 < 'string'\n", + "value": "strNumAdd = 5 + 'string'", "cursorLoc": { "line": 128, "column": 0 @@ -3486,7 +3494,7 @@ }, { "type": "Line", - "value": "strNumAdd = 5 + 'string'\n", + "value": " boolCheck8 = 5 <= 'clean'", "cursorLoc": { "line": 129, "column": 0 @@ -3494,7 +3502,7 @@ }, { "type": "Line", - "value": " boolCheck8 = 5 <= 'clean'\n", + "value": "boolCheck10 = true > 1", "cursorLoc": { "line": 130, "column": 0 @@ -3502,7 +3510,7 @@ }, { "type": "Line", - "value": "boolCheck10 = true > 1\n", + "value": "concatCheck = 1 ++ 3", "cursorLoc": { "line": 131, "column": 0 @@ -3510,7 +3518,7 @@ }, { "type": "Line", - "value": "concatCheck = 1 ++ 3\n", + "value": "concatCheck3 = 'clean' ++ 01235", "cursorLoc": { "line": 132, "column": 0 @@ -3518,7 +3526,7 @@ }, { "type": "Line", - "value": "concatCheck3 = 'clean' ++ 01235\n", + "value": " concatCheck4 = 01234 ++ 'afnbf'", "cursorLoc": { "line": 133, "column": 0 @@ -3526,7 +3534,7 @@ }, { "type": "Line", - "value": " concatCheck4 = 01234 ++ 'afnbf'\n", + "value": " concatCheck5 = true ++ false", "cursorLoc": { "line": 134, "column": 0 @@ -3534,7 +3542,7 @@ }, { "type": "Line", - "value": " concatCheck5 = true ++ false\n", + "value": " concatCheck6 = true ++ 1", "cursorLoc": { "line": 135, "column": 0 @@ -3542,7 +3550,7 @@ }, { "type": "Line", - "value": " concatCheck6 = true ++ 1\n", + "value": "logicalCheck11 = 1 && 'dack'", "cursorLoc": { "line": 136, "column": 0 @@ -3550,7 +3558,7 @@ }, { "type": "Line", - "value": "logicalCheck11 = 1 && 'dack'\n", + "value": " logicalCheck10 = 1 && true", "cursorLoc": { "line": 137, "column": 0 @@ -3558,7 +3566,7 @@ }, { "type": "Line", - "value": " logicalCheck10 = 1 && true\n", + "value": "logicalCheck12 = 1 || true", "cursorLoc": { "line": 138, "column": 0 @@ -3566,7 +3574,7 @@ }, { "type": "Line", - "value": "logicalCheck12 = 1 || true\n", + "value": " logicalCheck15 = 7 != 7 && 'garima' ++ ' kamboj'", "cursorLoc": { "line": 139, "column": 0 @@ -3574,7 +3582,7 @@ }, { "type": "Line", - "value": " logicalCheck15 = 7 != 7 && 'garima' ++ ' kamboj'\n", + "value": " logicalCheck19 = -4 == 'clean'", "cursorLoc": { "line": 140, "column": 0 @@ -3582,7 +3590,7 @@ }, { "type": "Line", - "value": " logicalCheck19 = -4 == 'clean'\n", + "value": " logicalCheck22 = 10 & 10", "cursorLoc": { "line": 141, "column": 0 @@ -3590,7 +3598,7 @@ }, { "type": "Line", - "value": " logicalCheck22 = 10 & 10\n", + "value": " logicalCheck24 = 5 > 1 == 9 != 9", "cursorLoc": { "line": 142, "column": 0 @@ -3598,7 +3606,7 @@ }, { "type": "Line", - "value": " logicalCheck24 = 5 > 1 == 9 != 9\n", + "value": " arrayCheck7 = (['str'] ++ ['tgb'])", "cursorLoc": { "line": 143, "column": 0 @@ -3606,7 +3614,7 @@ }, { "type": "Line", - "value": " arrayCheck7 = (['str'] ++ ['tgb'])\n", + "value": " arrSubs3 = arrayCheck3[0] + arrayCheck3[1]", "cursorLoc": { "line": 144, "column": 0 @@ -3614,7 +3622,7 @@ }, { "type": "Line", - "value": " arrSubs3 = arrayCheck3[0] + arrayCheck3[1]\n", + "value": " arrSubs5 = arrayCheck3[1 + 3]", "cursorLoc": { "line": 145, "column": 0 @@ -3622,19 +3630,11 @@ }, { "type": "Line", - "value": " arrSubs5 = arrayCheck3[1 + 3]\n", + "value": "", "cursorLoc": { "line": 146, "column": 0 } - }, - { - "type": "Line", - "value": "\n", - "cursorLoc": { - "line": 147, - "column": 0 - } } ] } diff --git a/test/assert/hackerrank/staircase.json b/test/assert/hackerrank/staircase.json index 219763e..4dd9b51 100644 --- a/test/assert/hackerrank/staircase.json +++ b/test/assert/hackerrank/staircase.json @@ -99,33 +99,33 @@ "leadingComments": [ { "type": "Line", - "value": " link to hackerRank : https://www.hackerrank.com/challenges/staircase\n", + "value": " link to hackerRank : https://www.hackerrank.com/challenges/staircase", "cursorLoc": { - "line": 2, + "line": 1, "column": 0 } }, { "type": "Line", - "value": " Print the stairs using space and hash------------------------------------z'\n", + "value": " Print the stairs using space and hash------------------------------------z'", "cursorLoc": { - "line": 3, + "line": 2, "column": 0 } }, { "type": "Line", - "value": " Concat single character count times to itself\n", + "value": " Concat single character count times to itself", "cursorLoc": { - "line": 6, + "line": 5, "column": 0 } }, { "type": "Line", - "value": " string count -> string\n", + "value": " string count -> string", "cursorLoc": { - "line": 7, + "line": 6, "column": 0 } } @@ -221,17 +221,17 @@ "leadingComments": [ { "type": "Line", - "value": " concat two strings\n", + "value": " concat two strings", "cursorLoc": { - "line": 13, + "line": 12, "column": 0 } }, { "type": "Line", - "value": " string string number number -> string\n", + "value": " string string number number -> string", "cursorLoc": { - "line": 14, + "line": 13, "column": 0 } } @@ -421,17 +421,17 @@ "leadingComments": [ { "type": "Line", - "value": " prints stairs given height of stairs\n", + "value": " prints stairs given height of stairs", "cursorLoc": { - "line": 20, + "line": 19, "column": 0 } }, { "type": "Line", - "value": " height -> string\n", + "value": " height -> string", "cursorLoc": { - "line": 21, + "line": 20, "column": 0 } } diff --git a/test/assert/withComment.json b/test/assert/withComment.json index 01ede8c..ac4b2cf 100644 --- a/test/assert/withComment.json +++ b/test/assert/withComment.json @@ -60,9 +60,19 @@ "leadingComments": [ { "type": "Line", - "value": " This is a comment to test\n", + "value": " This is a comment to test", "cursorLoc": { - "line": 8, + "line": 7, + "column": 0 + } + } + ], + "trailingComments": [ + { + "type": "Line", + "value": " this is an inline comment", + "cursorLoc": { + "line": 9, "column": 0 } } @@ -96,17 +106,7 @@ } } ], - "kind": "const", - "leadingComments": [ - { - "type": "Line", - "value": " this is an inline comment\n", - "cursorLoc": { - "line": 10, - "column": 0 - } - } - ] + "kind": "const" }, { "type": "VariableDeclaration", @@ -215,17 +215,7 @@ ], "ioParams": [], "sType": "IO" - }, - "trailingComments": [ - { - "type": "Line", - "value": " This is the end\n", - "cursorLoc": { - "line": 30, - "column": 0 - } - } - ] + } } ], "sourceType": "script" diff --git a/test/basicAssertion.json b/test/basicAssertion.json index 1f8865c..59522fa 100644 --- a/test/basicAssertion.json +++ b/test/basicAssertion.json @@ -93,7 +93,11 @@ "return": { "str": "return", "line": 1, "column": 6 } }, "includeParser": { - "include node-core": { "str": "node-core", "line": 1, "column": 17 } + "include node-core": { + "str": "node-core", + "line": 1, + "column": 17 + } }, "emptyArgsParser": { "()": { "str": [], "line": 1, "column": 2 }, @@ -221,12 +225,12 @@ "singleLineCommentParser": [ "comment", { - "// This is a comment\n": ["Line", " This is a comment\n"], + "// This is a comment\n": ["Line", " This is a comment"], "// This is a no end new line comment": [ "Line", " This is a no end new line comment" ], - "// This is a comment\ntest": ["Line", " This is a comment\n"] + "// This is a comment\ntest": ["Line", " This is a comment"] } ], "multiLineCommentParser": [ diff --git a/test/basicParsers.test.js b/test/basicParsers.test.js index 65397ea..bfbc6f0 100644 --- a/test/basicParsers.test.js +++ b/test/basicParsers.test.js @@ -8,9 +8,16 @@ const utils = require('../lib/utilityFunctions') /* test file with inputs and expected outputs */ const assertion = require('./basicAssertion') -const initObj = (input, line = 1, column = 0) => ({ str: input, line, column }) +const initInputObj = (input, line = 1, column = 0) => ({ + str: input, + line, + column +}) -const output = (input, line, column) => [input, initObj('', line, column)] +const output = (input, line, column) => [ + input, + initInputObj('', line, column) +] const basicTest = () => { for (const parser in base) { @@ -18,7 +25,7 @@ const basicTest = () => { if (utils.notUndefined(valid)) { for (const input in valid) { const op = valid[input] - const value = base[parser](initObj(input)) + const value = base[parser](initInputObj(input)) const expected = output(op.str, op.line, op.column) test(parser, (t) => { t.deepEqual(value, expected) @@ -30,13 +37,16 @@ const basicTest = () => { const inpTempl = valid[0] const checks = valid[1] for (const input in checks) { - let value = base[parser](initObj(input)) + let value = base[parser](initInputObj(input)) if (utils.notNull(value)) { value = value[0] delete value.cursorLoc + delete value.isInLine } const output = checks[input] - let expected = utils.isNull(output) ? null : templ[inpTempl](output) + let expected = utils.isNull(output) + ? null + : templ[inpTempl](output) if (Array.isArray(output)) { expected = templ[inpTempl](...output) } diff --git a/test/src/withComment.cl b/test/src/withComment.cl index 6932451..49df1c1 100644 --- a/test/src/withComment.cl +++ b/test/src/withComment.cl @@ -24,6 +24,4 @@ prod a b = a * b variable = if 'hello' > 'world' then true else false do - putLine 'testing inline comment inside "do"' // inside do - -// This is the end + putLine 'testing inline comment inside "do"'