SerafimArts / Calculator

Calculator Example (AST Edition). Why not?
15 stars 1 forks source link

Build error #4

Open HMRDevil opened 6 months ago

HMRDevil commented 6 months ago

Hello, Kirill!

I noticed a bug (not a bug, but a feature) that negatively affects calculations.

Description

When 2+ operators with the same priority sequentially follow each other in the original expression, the nodes are arranged like this. From the grammar side, this is true, because it obeys the rules:

#Expression
  : Addition()
  | Subtraction()
  | Term()
  ;

Term
  : Multiplication()
  | Division()
  | Factor()
  ;
...
Subtraction -> {
...
}
  : Term() ::T_MINUS:: Expression()
  ;

Example with subtraction is more illustrative: 1-2-3 Expected: -4 Result: 2

Subtraction (
        [offset] => 0
        [a] => IntValue (
                [offset] => 0
                [value] => 1
        )
        [b] => Subtraction (
                [offset] => 2
                [a] => IntValue (
                        [offset] => 2
                        [value] => 2
                )
                [b] => IntValue (
                        [offset] => 4
                        [value] => 3
                )
        )
)

1-2-3-4 Expected: -8 Result: -2

Subtraction (
        [offset] => 0
        [a] => IntValue (
                [offset] => 0
                [value] => 1
        )
        [b] => Subtraction (
                [offset] => 2
                [a] => IntValue (
                        [offset] => 2
                        [value] => 2
                )
                [b] => Subtraction (
                        [offset] => 4
                        [a] => IntValue (
                                [offset] => 4
                                [value] => 3
                        )
                        [b] => IntValue (
                                [offset] => 6
                                [value] => 4
                        )
                )
        )
)

I tried to change the grammatical rules, changing them, swapping them, but in vain. Either it doesn't work, or it goes into recursion.

Possible Solutions

  1. Reassemble the nodes in the "Builder" before returning them or in the calculator itself before sending them for calculation?
  2. Enter priorities in the "Expression" and rebuild nodes based on these priorities?
  3. Combine these variants?

I am ready to do PR, but I doubt the correctness of my solutions

I need help)))

@SerafimArts

SerafimArts commented 6 months ago

There the associativity is incorrect. In the first example this is clearly visible:

We get: 1) 2 - 3 = -1 2) 1 - -1 = 2

It should be inverted it and make it left-associative instead of right) Just a bug, lol)

here are examples of correct grammar with left-associativity https://phplrt.org/docs/examples/advanced-math

HMRDevil commented 6 months ago

@SerafimArts Hello again. I looked at the example from the documentation on the link, and I can say with confidence that it does not work. For clarity, I deleted the extra tokens:

%token  T_INT           \d+

%token  T_PLUS          \+
%token  T_MUL           \*

%token  T_BRACE_OPEN    \(
%token  T_BRACE_CLOSE   \)

%skip   T_WHITESPACE    \s+

%pragma root Expression

Expression
  : BinaryExpression()
  ;

BinaryExpression
  : AdditiveExpression()
  ;

AdditiveExpression
  : (MultiplicativeExpression() (<T_PLUS>))* MultiplicativeExpression()
  ;

MultiplicativeExpression
  : (UnaryExpression() (<T_MUL>))* UnaryExpression()
  ;

UnaryExpression
  : ::T_BRACE_OPEN:: Expression() ::T_BRACE_CLOSE::
  | Literal()
  ;

Literal
  : <T_INT>
  ;

By entering 1, we completely switch to the literal. BinaryExpression -> no match -> AdditiveExpression -> no match -> MultiplicativeExpression -> no match -> UnaryExpression -> match. We get a Literal [1]

By entering 1+2, we get an AdditiveExpression, because 1) MultiplicativeExpression => UnaryExpression => Literal [1] and 2) MultiplicativeExpression => UnaryExpression => Literal [2]

By entering 1+2+3, we get an AdditiveExpression ( a .Literal [1] + b. AdditiveExpression ( a. Literal [2] + b. Literal [3])). But if we add new rules:

AdditiveExpression
: AdditiveExpression <T_PLUS> MultiplicativeExpression
| (MultiplicativeExpression() (<T_PLUS>))* MultiplicativeExpression()
;

, we get recursion, because tokens don't change (T_INT T_PLUS T_INT) and AdditiveExpression is BinaryExpression.

I have already tried, it seems to me, all the ways with changes/permutations, etc. of the rules in the current implementation. And today I had the idea to "pack" a Binary Expression into a UnaryExpression.