Skip to content

An unknown function name is silently read as a product, so log10(x) becomes log^10 * x #733

Description

@Rafael-SOWNet

Split out of #730, which was one instance of this. Filing the sweep rather than fixing it, because what to do about most of these is a naming call rather than a defect.

The mechanism

A name the grammar does not know, followed by (, falls through to implicit multiplication — the rule that lets a(b + c) mean a * (b + c). For a one-argument call this never errors; it silently produces a product with an undeclared variable. Measured on master (7f83a82):

floor(x)       =>  floor * x
ceil(x)        =>  ceil * x
round(x)       =>  round * x
erf(x)         =>  erf * x
conjugate(x)   =>  conjugate * x
re(x), im(x)   =>  re * x, im * x
factorial(x)   =>  factorial * x

A two-argument call fails loudly instead, because name * (x, y) is not a valid parse:

min(x, y), max(x, y), gcd(x, y), lcm(x, y)   =>  UnhandledParseException

That inconsistency is worth noting on its own: whether a missing name is reported depends on how many arguments the caller passed, not on anything about the name.

The part that is a defect

Most of the names above are functions AngouriMath simply does not have, and a silent product is the ordinary consequence of implicit multiplication being a feature. You cannot refuse every unknown name without breaking a(b + c).

But two of them name something the library does have, and those are the same shape as #730:

log10(100)   =>  log ^ 10 * 100      // should be 2
log2(8)      =>  log ^ 2 * 8         // should be 3

log(100) is already 2 and log(2, 8) is already 3, so nothing is missing but the spelling. These are worse than the others to read, too: log10 lexes as the variable log followed by 10, and x2 means x^2 by design, so the result is a power of an undeclared variable rather than a product with one.

log10 and log2 are standard in C, Python, numpy and MATLAB.

Suggested fix

Add log10( and log2( to AngouriMath.g as MathS.Log(10, arg) and MathS.Log(2, arg) — the same shape as the exp( rule added in #731. The rest of the names above are feature requests for functions that do not exist yet (floor, ceil, round, min, max, gcd, lcm, …) and should be judged separately.

Found by sweeping common CAS function names past the parser after #730, which was the same defect for exp.

Activity

  1. Rafael-SOWNet commented on Aug 5, 2026

    @Rafael-SOWNet
    MemberAuthor

    The defect half of this is fixed in #734, merged to master as 318ac9f.

    before after
    log10(100) log ^ 10 * 100 2
    log10(1000) log ^ 10 * 1000 3
    log2(8) log ^ 2 * 8 3
    log2(1024) log ^ 2 * 1024 10

    Two grammar rules mapping to MathS.Log(10, arg) and MathS.Log(2, arg), so they are the same function as the spelling that already worked rather than merely each being defined -- log10(x) and log(x) parse to one tree, and the test asserts that rather than asserting each value separately.

    Only the exact name followed by a bracket is the function, as for every other function in the grammar: log2x is still the implicit power log^2 * x, log10 still log^10, and logx(y) and log3(x) are still the implicit products they were. The one- and two-argument log are untouched.

    Leaving this open for the rest, which is the part that is not a defect: floor, ceil, round, erf, re, im, conjugate, min, max, gcd and lcm name functions AngouriMath does not have, so a silent product is the ordinary consequence of implicit multiplication being a feature -- refusing every unknown name would break a(b + c). Those are feature requests, and each wants its own judgement about whether the library should have the function at all.

    The argument-count inconsistency in the report is also still there and unaddressed: a one-argument call to a missing name is silent, a two-argument one raises. Worth knowing before treating a parse error as the way a missing function shows up.

    Tests in LogBaseNamesParsedTest.cs.

  2. Rafael-SOWNet commented on Aug 5, 2026

    @Rafael-SOWNet
    MemberAuthor

    Partly landed, and the example in the title is now fixed — leaving a note so the issue is not read as untouched.

    Done:

    was is
    log10(100) log ^ 10 * 100 2 (#734)
    log2(8) log ^ 2 * 8 3 (#734)
    factorial(5) factorial * 5 120 (#737)

    Still open, and not a grammar line. Measured on af766b22:

    parse(floor(x))    => floor * x        <- silent
    parse(ceil(x))     => ceil * x         <- silent
    parse(round(x))    => round * x        <- silent
    parse(min(3,5))    => UnhandledParseException
    parse(max(3,5))    => UnhandledParseException
    parse(gcd(12,18))  => UnhandledParseException
    

    The three landed names each had a node behind them already, so adding them was a grammar entry and a test. floor, ceil and round have no node in the library at all — only an internal EDecimal.Floor helper — so reading them means a node through parsing, evaluation, simplification, differentiation, compilation, LaTeX and the F# wrapper. That is the same plumbing #402 wants for modulus, and it should probably be costed with it rather than treated as more of this issue.

    The silent ones are worth separating from the loud ones: min(3,5) raising a parse error tells you something is wrong, whereas floor(x) quietly becoming floor * x gives an answer to a different question. Implicit multiplication is why it cannot simply be made an error — x(y+1) is a legitimate product — so the fix really is "add the node", not "reject unknown names".

  3. Rafael-SOWNet commented on Aug 6, 2026

    @Rafael-SOWNet
    MemberAuthor

    Correcting my comment above — it says floor, ceil and round are still read silently as products, and that is no longer true. The silent misreading this issue is titled for is gone.

    Measured on stock master (184dd5a), every name from that list is now refused by name, with a message saying what is missing:

    parse(floor(x))      => UnrecognizedFunctionParseException: there is no function floor:
                            AngouriMath has no rounding functions. It is refused by name rather
                            than read as the product of a variable named floor with its argument,
                            which is what an unknown name followed by a bracket would otherwise mean
    parse(ceil(x))       => ... no rounding functions
    parse(round(x))      => ... no rounding functions
    parse(erf(x))        => ... no error function
    parse(conjugate(x))  => ... no complex conjugate as a symbolic function
    parse(min(3, 5))     => ... no minimum or maximum function
    parse(gcd(12, 18))   => ... no greatest common divisor as a symbolic function
    

    And implicit multiplication, which is the reason unknown names could not simply be rejected, still parses:

    parse(x(y + 1))      => x * (y + 1)
    

    So the mechanism in the report — a one-argument call falling through to implicit multiplication and quietly answering a different question — no longer applies to any of these names, and the loud/silent distinction I drew above has been resolved in favour of loud.

    What is genuinely left is the scope question, which is what this issue said from the start it was really about: which of floor, ceil, round, erf, conjugate, re, im, min, max, gcd should exist as nodes. That is a naming and design call per name, not a grammar line, and each wants its own answer — floor and round carry the same plumbing cost as the modulus in #402 and are probably best costed with it.

    I would suggest closing this one as fixed and letting the individual functions be requested where they are wanted, but it is your call and I have not touched it.

  4. Happypig375 commented on Aug 7, 2026

    @Happypig375
    Member

    Yea the inclusion of those functions belong to their own issue

  5. Rafael-SOWNet commented on Aug 8, 2026

    @Rafael-SOWNet
    MemberAuthor

    Closing: the defect in the title is fixed. Re-measured on master (21f0d16):

    was now
    log10(x) log ^ 10 * x log(10, x) ✅
    exp(x) a product e ^ x ✅
    floor(x) floor * x, silently UnrecognizedFunctionParseException, by name ✅

    An unknown name followed by a bracket is no longer read as a product. Where the function does not exist, it is refused by name with a message saying what is missing, which is the thing this issue was opened about.

    Per @Happypig375 — "the inclusion of those functions belong to their own issue" — the remaining work is tracked in #809: floor, ceil, round, min, max and gcd are still absent, and each needs a design decision rather than a grammar line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions