viktorstrate / algebra-latex

Parse and calculate latex formatted math
https://www.npmjs.com/package/algebra-latex
MIT License
28 stars 9 forks source link

Parsing of function expressions #8

Open EricRabil opened 5 years ago

EricRabil commented 5 years ago

Hello, I am attempting to parse f\left(x\right)=x^2 into a function definition, f(x)=x^2, which is an acceptable format in Algebrite. Instead, f*x=x^2 is produced.

If this is my mistake, how can I declare an algebraic function and have it translate to the desired output above?

If this is not my mistake, and algebra-latex does not support this, are there plans to implement this into the library?

viktorstrate commented 5 years ago

Hi, there is currently no way to declare a function like you are trying.

There is no way to know the difference between a function declaration f(x), and a variable f followed by another variable ´x´ inside parenthesis, which is also a totally valid math string.

I don't see an obvious way to tell algebra-latex to parse it as a function rather than the product of two variables. Maybe you have an idea?

EricRabil commented 5 years ago

Perhaps it could be implemented as an option or a custom method?

Some ideas that come to mind:

Sent with GitHawk

viktorstrate commented 5 years ago
  1. Reserving a set of characters. This is already done, for the builtin math functions like sin, cos, sqrt etc. see /src/models/functions.js

  2. Looking at the syntax eg. f(x) immediatley followed by =. This is a great idea, but this doesn't really solve the problem completely, because often you want to use the function after it has been declared, and this would not be possible.

  3. Custom options for external libraries. This might work, but other libraries would have to implement it.

A solution might be to add a configuration object where custom function names could be set. And the parser would know to parse it as a function whenever it sees these names.

Like so:

new AlgebraLatex({
  functions: ['f', 'g', 'func']
}).parseLatex(latexInput)
EricRabil commented 5 years ago

I think that your solution is ideal, especially because you can customize the reservations on a per-parse basis. I can try to implement this, although my experience with this library is minuscule.

viktorstrate commented 5 years ago

Great 🎉 I will encourage you to try to implement it.

The important files are:

The /src/index.js file, where the object is created

The keyword method in Parser.js line 144, where it is decided if it should be interpreted as a function.

EricRabil commented 5 years ago

So at Parser.js:144 I would just need to mixin the added keywords?

viktorstrate commented 5 years ago

Exactly, the variable functions is an array of all predefined functions. See /src/models/functions.js

EricRabil commented 5 years ago

The deprecated latex parameter in the AlgebraLatex constructor is preventing me from adding an options object. Shall I add a typeof check and preserve original functionality if the first parameter is a string?

constructor(latex) {
  if (typeof latex === 'undefined') {
    return
  } else if (typeof latex === 'object') {
    this.parseOptions(latex)
    return
  }

  this.parseLatex(latex)
}
viktorstrate commented 5 years ago

Okay. Just remove the old parameter. Then we just have to push a major version (v2.0) for next update

EricRabil commented 5 years ago

My fork is a working concept of this, however it does not allow me to define a function with multiple arguments.

For example: \\f\\left(x,y\\right) yields

\f\left(x.y\right)
       ^
Error at line: 1 col: 8
Unknown symbol: .

This seems to be a result of a regex that accommodates the European decimal. Perhaps I can replace this with another option?

viktorstrate commented 5 years ago

Your implementation is great, and I will happily merge it, if you send a pull request.

The problem with multi-variable functions is a new issue of its own. You are welcome to fix this issue too, but it is a little more complicated, as both the lexer, the parser and the formatter would have to be modified.

If you want to try to implement it, here are what needs to be done. If not, I can do it later when I have the time.

Lexer

The lexer should first determine if the comma is a part of a european number, otherwise it should return a new separator token object maybe { type: 'separator' }

Eg. 2,3 is a number. 2, 3 is a separator. a,b is a separator

Note that multi-variable functions also can be written as f(x; y) or f(2,5; 3,0). So semicolons should also be interpreted as a { type: 'separator' }

Parser

Parser.function() should be rewritten to understand the comma. The output for a multi-variable function should be somthing like the following. The change is that content is an array instead of a single object.

// f(x, y)
{
  type: 'function',
  value: 'f',
  content: [{
    type: 'variable', value: 'x',
  }, {
    type: 'variable', value: 'y',
  }
}

Formatters

The formatters should be updated to understand the new parsed formats

EricRabil commented 3 years ago

Sorry to bump a dead thread. Working on multivariate arguments, and not sure how to handle the separators in Latex. Popular editors like MathQuill don't support spaces between the arguments, at least not by default. Even the demo page of MathQuill doesn't let you put spaces in the arguments :(

Perhaps an option is in order that can disable European decimal detection?

viktorstrate commented 3 years ago

No issue, i've reopened it.

Perhaps an option is in order that can disable European decimal detection?

I agree that it would probably be the best solution to add an option to chose comma separator format. This would also make the output formatting more precise as the user can decide.

Also I don't see any general solution that would cover all edge-cases.