arithfunclang.jl 2.77 KB
using ParserCombinator

# the AST nodes we will construct, with evaluation via calc()

abstract type Node end
Base.:(==)(n1::Node, n2::Node) = n1.val == n2.val
calc(n::Float64) = n
# repr(n::Float64) = string(n)

struct Const <: Node val end
calc(c::Const) = c.val
# repr(c::Const) = repr(c.val)

struct Inv <: Node val end
calc(i::Inv) = 1.0/calc(i.val)
# repr(i::Inv) = "1.0/$(repr(i.val))"

struct Prd <: Node val end
calc(p::Prd) = Base.prod(map(calc, p.val))
# repr(p::Prd) = join(map(repr, p.val), "×")

struct Neg <: Node val end
calc(n::Neg) = -calc(n.val)
# repr(n::Neg) = "-$(repr(n.val))"

struct Sum <: Node val end
calc(s::Sum) = Base.sum(map(calc, s.val))
# repr(s::Sum) = join(map(repr, s.val), "+")

struct TermSequence <: Node val end
calc(ts::TermSequence) = map(calc, ts.val)
# repr(ts::TermSequence) = join(map(repr, ts.val), ",")

struct Functor <: Node val end
calc(f::Functor) = nothing
# repr(f::Functor) = "$(ts.val)"

struct Function <: Node val end
calc(f::Function) = Const(0.0)
# repr(f::Function) = "$(repr(f.val[1]))($(repr(f.val[2])))"

# the grammar (the combinators!)

#=
    sum     ::= prd (add | sub)*
    add     ::= "+" prd
    sub     ::= "-" prd

    prd     ::= neg (mul | div)*
=#
@with_names begin    
    spc = Drop(Star(Space()))
    @with_pre spc begin
        sum = Delayed()

        term_sequence = StarList(sum, E",") |> TermSequence
        functor = p"[a-z][a-zA-Z0-9_]*" > Functor 
        func = functor + E"(" + term_sequence + E")" |> Function
        
        val = E"(" + sum + E")" | (PFloat64() > Const)


        neg = Delayed()       # allow multiple (or no) negations (eg ---3)
        neg.matcher = func | val | (E"-" + neg > Neg)

        mul = E"*" + neg
        div = E"/" + neg > Inv
        prd = neg + Star(mul | div) |> Prd

        add = E"+" + prd
        sub = E"-" + prd > Neg
        sum.matcher = prd + Star(add | sub) |> Sum

        arith_lang = (func | val | neg | prd | sum) + Eos()
        func_lang = func + Eos()
        tl_lang = term_sequence + Eos()
    end
end


# and test

source = "f(1,2,3+4)*1+g(5,10,h(100,3))" 
grammar = arith_lang

debug, task = make(Debug, source, grammar; delegate=NoCache)
try
    expr = once(task)
    println("---\n\"$source\"\tparses to\t\"$(expr)\"\n---")
catch ParserException
    println("Error parsing after \"$(source[1:debug.max_iter - 1])\" and before \"$(source[debug.max_iter:end])\".\n")

    #println(grammar)
    parse_dbg(source, Trace(grammar))
end

x = Any[
        Sum(
            Any[    
                Prd(
                    Any[
                        Const(2.0)
                    ]
                ), 
                Prd(
                    Any[
                        Const(3.0), 
                        Const(4.0)
                    ]
                )
            ]
        )
    ]