Skip to content

Commit

Permalink
Test: pass some additional tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MuhammadSawalhy committed Jan 1, 2021
1 parent 408d2bc commit ba8fde8
Show file tree
Hide file tree
Showing 15 changed files with 265 additions and 70 deletions.
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
# Unreleased, v3.0

## Breaking

- Node of type "function", when its args is `[{ type: "block", name: "()", ... }]`, function's args is assigned to parenthesis args. In other words, in this case it will be `Array` not `{ type: "block", ... }`.
- Removing node type `delimeter`:
- `f(1,3,4)` when parsed as function, it will have args with length 3.
- `(1,3,4)` will be parsed with `type = "tuple"`.

## Added

- Built-in function `sqrt`

- Node of type "tuple": `(1, 2, x, ...)`
- `options.spreadOperatorAllowed`


# 22 Oct 2020, v2.3.0

## Add
- check for validity of block (including brackets) syntax, e.g., make sure that they are put in the right order and nested correctly, the block has opening and closing charachters.
- check for validity of block (including brackets) syntax, e.g., make sure that they are put in the right order and nested correctly, the block has opening and closing characters.

# 31 Aug 2020, v2.2.0
Summary of changes:
Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Default: `true`
Maths conventionally works with single char named variables and constants, but in programming languages you have freedom. Moreover, the convention is to use multi-char named identifier.
For example, if you want to use "pi" or "phi", etc, you have to set this to `false`.

When a member expression is found, properties and methods are allowed to be multichar, despite of `options.singleCharName`

> TODO: make new options `variables`, with default values "pi" and "phi", ..., use this option to deal with some multi-char variable (or constants, or you can say identifiers) in singleCharName mode
## strict: boolean
Expand All @@ -77,8 +79,23 @@ For example:

## functions: [string]

When autoMult is `true`, some expression like `f(x)` will be considered as multiplication, in order to parse it as a function with name = "f", you can pass `options.functions = ['f']`. Notice that, strict = flase, some expression such as `f()`, an id follwed by empty parentheses, will be parsed with type = "function" whether or not `options.function` includes "f".
When autoMult is `true`, some expression like `f(x)` will be considered
as multiplication `f*(x)`, in order to parse it as a function with name = "f",
you can pass `options.functions = ['f']`.
Notice that when `strict = false`, some expression such as `f()`,
an id follwed by empty parentheses, will be parsed with type = "function"
whether or not `options.function` includes "f".

When parsing `obj.method(...)`, regradless of options, it will be always:
```
member expression
/\_
/ \_
__/ \________
id function
| name | name = "method"
| = "obj" | arg = [ ... ]
```

# Unsure about
In these confusing cases, you can handle the parsed expression to transform to what you want.
Expand Down
2 changes: 1 addition & 1 deletion src/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Node.prototype.types = {
BLOCK: 'block',
AUTO_MULT: "automult",
OPERATOR: 'operator',
DELIMITER: 'delimiter',
MEMBER_EXPRESSION: 'member expression',
TUPLE: 'tuple',
};

Node.prototype.types.values = Object.values(Node.prototype.types);
Expand Down
81 changes: 53 additions & 28 deletions src/math.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
functions: [],
singleCharName: true,
memberExpressionAllowed: true,
keepParentheses: false,
strict: false,
builtInFunctions: [
"sinh", "cosh", "tanh", "sech", "csch", "coth",
"arsinh", "arcosh", "artanh", "arsech", "arcsch", "arcoth",
"sin", "cos", "tan", "sec", "csc", "cot",
"asin", "acos", "atan", "asec", "acsc", "acot",
"arcsin", "arccos", "arctan", "arcsec", "arccsc", "arccot",
"ln", "log", "exp", "floor", "ceil", "round", "random"
"ln", "log", "exp", "floor", "ceil", "round", "random", "sqrt"
]
}, options); /// override the default options

Expand Down Expand Up @@ -127,20 +128,22 @@ Factor
}

factorWithoutNumber =
base:(MemberExpression / Functions / BlockParentheses / BlockVBars / NameNME) _ fac:factorial? {
base:(MemberExpression / Functions / TupleOrExprOrBlock / BlockVBars / NameNME) _ fac:factorial? {
if (fac) base = createNode('operator', [base], {name: '!', operatorType: 'postfix'});
return base;
}

Delimiter
// there is spaces around expressions already no need for _ rule
= head:Expression tail:("," Expression)* {
// there is spaces around expressions already no need for _ rule
delimiterExpression
= head:Expression tail:(delimiters Expression)* {
if (tail.length){
return createNode('delimiter', [head].concat(tail.map(a => a[1])), {name: ','});
return [head].concat(tail.map(a => a[1]));
}
return head;
}

delimiters = ","

Functions "functions" =
BuiltInFunctions / Function

Expand All @@ -161,7 +164,7 @@ builtInFuncsTitles =
"sin"/ "cos"/ "tan"/ "sec"/ "csc"/ "cot"/
"asin"/ "acos"/ "atan"/ "asec"/ "acsc"/ "acot"/
"arcsin"/ "arccos"/ "arctan"/ "arcsec"/ "arccsc"/ "arccot"/
"ln"/ "log"/ "exp"/ "floor"/ "ceil"/ "round"/ "random" / "sum"
"ln"/ "log"/ "exp"/ "floor"/ "ceil"/ "round"/ "random" / "sum" / "sqrt"
) { return n; } /
n:$multiCharName &{ return options.builtInFunctions.indexOf(n) > -1 } { return n; }

Expand All @@ -178,39 +181,61 @@ builtInFuncArgs = a:(
error('invalid syntax, hint: missing * sign');
}
) /
BlockParentheses /
functionParentheses /
BlockVBars /
Functions
) {
if (a.type === "block" && a.name === "()") return a.args ? a.args : [];
return [a]; // a is not parenthese
return Array.isArray(a) ? a : [a]; // array when it is functionParentheses
}

// TODO: 2axsin3y --- singleCharName = true
Function =
// no need for FnNameNME
name:$NameNME _ parentheses:(BlockParentheses / VoidBlockParentheses) &{
if(!parentheses.args /*: VoidBlockParentheses */ && !options.strict) {
// it has to be a function, it may or may not be provided in `options.functions`
return true;
}
let functionExists = options.functions.indexOf(name)>-1;
if (!functionExists && !parentheses.args) error("unexpected empty parenthese after a non-function");
return functionExists;
} { return createNode('function', parentheses.args || [], { name }); }

name:$NameNME _ args:(
a:(functionParenthesesNotVoid &{
let exists = options.functions.indexOf(name)>-1;
if (!exists && !options.autoMult)
error("even autoMult is not activated, hint: add \"*\" sign");
return exists;
}) { return a[0] } /
voidParentheses &{
let exists = options.functions.indexOf(name)>-1;
if (!exists && options.strict)
error("unexpected empty a after a non-function");
return true; // in case not strict mode, it is a valid function regardless of `exists`
} { return [] }
) {
// `a` is eiher array or expr
return createNode('function', args, { name });
}

// for member expressions
MultiCharFunction =
// for member expressions
name:$MultiCharNameNME _ parentheses:(BlockParentheses / VoidBlockParentheses) {
return createNode('function', [parentheses], { name });
name:$MultiCharNameNME _ a:functionParentheses {
// `a` is eiher array or expr
return createNode('function', a, { name });
}

BlockParentheses =
"(" args:Delimiter /* returns Expression id no delimiter found */ ")" { return createNode('block', [args], {name: '()'}); }
TupleOrExprOrBlock =
"(" delmOrExpr:delimiterExpression ")" {
if(Array.isArray(delmOrExpr))
return createNode("tuple", delmOrExpr);
return options.keepParentheses
? createNode('block', [delmOrExpr], { name: '()' })
: delmOrExpr;
}

functionParentheses =
a:("(" b:delimiterExpression ")" { return b } / voidParentheses { return [] }) {
return Array.isArray(a) ? a : [a];
}

functionParenthesesNotVoid =
"(" a:delimiterExpression ")" {
return Array.isArray(a) ? a : [a];
}

VoidBlockParentheses =
"(" _ ")" { return createNode('block', null, {name: '()'}); }
// related to functions
voidParentheses = "(" _ ")" { return [] };

BlockVBars =
"|" expr:Expression "|" { return createNode('block', [expr], {name: '||'}) }
Expand Down
5 changes: 5 additions & 0 deletions tests/maps/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ module.exports = [
]),
},

{
math: "(1, 3, \n4)",
struct: node.tuple([1,3,4]),
},

]
16 changes: 10 additions & 6 deletions tests/maps/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
const basic = require("./basic");
// const singleChar__memExpr = require("./singleChar__memExpr");
// const singleChar__functions = require("./singleChar__functions");
const singleChar__memExpr = require("./singleChar__memExpr");
const singleChar__functions = require("./singleChar__functions");
const singleChar__autoMult = require("./singleChar__autoMult");
// const noSingleChar__memExpr = require("./noSingleChar__memExpr");
const noSingleChar__memExpr = require("./noSingleChar__memExpr");
const noSingleChar__functions = require("./noSingleChar__functions");
const noSingleChar__autoMult = require("./noSingleChar__autoMult");
const strict = require("./strict");
const keepParentheses = require("./keepParentheses");

module.exports = {
basic,
options: {
strict,
keepParentheses,
"singleCharName=true": {
// "member expression": singleChar__memExpr,
// "function": singleChar__functions,
"member expression": singleChar__memExpr,
"function": singleChar__functions,
"auto multiplication": singleChar__autoMult,
},

"singleCharName=false": {
// "member expression": noSingleChar__memExpr,
"member expression": noSingleChar__memExpr,
"functions": noSingleChar__functions,
"auto multiplication": noSingleChar__autoMult,
}
Expand Down
6 changes: 6 additions & 0 deletions tests/maps/keepParentheses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// testing options.keepParentheses

module.exports = [

]

30 changes: 28 additions & 2 deletions tests/maps/noSingleChar__functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,36 @@ module.exports = [
struct: node.am([2, node.pOP("!", [node.F("longFuncName", [])])]),
},

{
title: "function with multiple arguments",
math: "-.2longFuncName(1,2, sqrt(1)^(x))!",
parserOptions: { singleCharName: false, functions: ['longFuncName'] },
struct: node.am([
-0.2,
node.pOP("!", [
node.F("longFuncName", [1,2,
node.op("^", [
node.F('sqrt', [1]),
"x"
])
])
])]
),
},

{
math: "ax sin 3y",
parserOptions: { singleCharName: false },
struct: node.am([
"ax",
node.BIF("sin", [node.am([3, "y"])])
])
},

{
title: "should use function id as reference (or variable) when strict=false",
math: "(2longFuncName! + x)",
parserOptions: { singleCharName: false, functions: ['longFuncName'] },
parserOptions: { singleCharName: false, keepParentheses: true, functions: ['longFuncName'] },
struct: node.block("()", [node.op("+", [
node.am([2, node.pOP("!", ["longFuncName"])]),
"x"
Expand All @@ -34,7 +60,7 @@ module.exports = [
node.F("fn", [
node.op("&&", ["variable_name", 2])
]),
node.block("()", [node.op("/", [3,2])])
node.op("/", [3,2])
]),
},

Expand Down
52 changes: 46 additions & 6 deletions tests/maps/noSingleChar__memExpr.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,72 @@ module.exports = [
{
math: "point.x",
parserOptions: { singleCharName: false, },
struct: node.mem(["point", "x"]),
},

{
math: "should throw: when options.memExpressionAllowed=false, (point.x)",
parserOptions: { singleCharName: false, memExpressionAllowed: false },
error: true, errorType: "syntax"
},

{
math: "1+ point.component_1^2!",
parserOptions: { singleCharName: false, },
struct: node.op("+", [
1,
node.op("^", [
node.mem(["point", "component_1"]),
node.pOP("!", [2])
])
])
},

{
math: "1 + point1. func()",
parserOptions: { singleCharName: false, },
struct: node.op("+", [
1,
node.mem(["point1", node.F("func", [])])
])
},

{
math: "1 + point1 .\\n func(1.2+x)",
math: "1 + point1 .\n func(1.2+x, s)",
parserOptions: { singleCharName: false, },
struct: node.op("+", [
1,
node.mem(["point1", node.F("func", [
node.op("+", [1.2, "x"]),
"s"
])])
])
},

{
math: "1 + p_1.func(1.2+x)!^2",
parserOptions: { singleCharName: false, },
math: "1 + p_1().func(1.2+x)!^2",
parserOptions: { singleCharName: false, strict: true },
error: true, // the next one won't throw
errorType: "syntax"
},

{
math: "1 + p.func(1.2+x)^2!",
parserOptions: { singleCharName: false },
struct: ,
math: "1 + p_1().func(1.2+x)!^2",
parserOptions: { singleCharName: false, functions: ["p_1"] },
struct: node.op("+", [
1,
node.op("^",[
node.pOP("!",[
node.mem([
node.F("p_1", []),
node.F("func", [
node.op("+", [1.2, "x"])
])
])
]),
2
])
])
},

];
Loading

0 comments on commit ba8fde8

Please sign in to comment.