ASPLang.jl 10.9 KB
module ASPLang

export parse_asp, debug_asp, pstring,
    literal_grammar, term_grammar, aggregate_grammar, asp_grammar,
    Node, Terminal, NonTerminal, Variable, StringConstant, SymbolicConstant, AnonymousVariable, NumberConstant, Variable,
    Functor, FunctionTerm,
    Negated, Inverted, Product, Sum,
    LT, LE, EQ, NE, GE, GT,
    LTAtom, LEAtom, EQAtom, NEAtom, GEAtom, GTAtom,
    Atom, ClassicalLiteral, NafLiteral,
    Level, Contribution, WeightAtLevel,
    CountAggregate, SumAggregate, MaxAggregate, MinAggregate,
    Aggregate, Elements, InclusionCondition,
    Constraint, LeftBound, RightBound,
    Choice, RestrictedChoice, Choices,
    Disjunction, Head, Body, Rule, Restriction, WeakRestriction, Annotation,
    Query, Program



abstract type Node end
Base.:(==)(n1::Node, n2::Node) = (typeof(n1) == typeof(n2)) && (n1.args == n2.args)

pstring(x::Any, level=0) = repr(x)
abstract type Terminal <: Node end
macro terminal(lang)
    return quote
        struct $lang <: Terminal end
    end
end
pstring(x::Terminal, level=0) = "$(" "^level)$(typeof(x))"

abstract type NonTerminal <: Node end
macro variable(lang)
    return quote
        struct $lang <: NonTerminal
            args
        end
    end
end

function pstring(x::NonTerminal, level=0)
    if typeof(x.args) <: AbstractVector
        pargs = x.args .|> (z -> pstring(z, level + 2))
        "\n$(" "^level)$(typeof(x))($(join(pargs, ","))\n$(" "^level))"
    else
        pargs = join(pstring.(x.args), ",")
        "\n$(" "^level)$(typeof(x))($pargs) - $(typeof(x.args))"
    end
end

using ParserCombinator
#=
    DATA STRUCTURES
    =#

@variable Variable
@variable StringConstant
@variable SymbolicConstant
@variable AnonymousVariable
@variable NumberConstant

@variable Functor
@variable FunctionTerm
@variable Negated
@variable Inverted
@variable Product
@variable Sum

@terminal LT
@terminal LE
@terminal EQ
@terminal NE
@terminal GE
@terminal GT
@variable LTAtom
@variable LEAtom
@variable EQAtom
@variable NEAtom
@variable GEAtom
@variable GTAtom

@variable Atom
@variable ClassicalLiteral

@variable NafLiteral

@variable Level
@variable Weight
@variable Contribution
@variable WeightAtLevel

@variable CountAggregate
@variable SumAggregate
@variable MaxAggregate
@variable MinAggregate

@variable Aggregate
@variable Elements
@variable InclusionCondition
@variable Constraint
@variable LeftBound
@variable RightBound

@variable RestrictedChoice
@variable Choice
@variable Choices
@variable Disjunction
@variable Annotation
@variable Body
@variable Head

@variable Rule
@variable Restriction
@variable WeakRestriction
@variable Query
@variable Program

#=
------------------------------------------------------
GRAMMARS
------------------------------------------------------
=#
@with_names begin
spc = Drop(Star(Space()))
@with_pre spc begin
@with_post spc begin
#
#
#   TERMS
#
#
#---------------------------#
#           variable        #
#---------------------------#
variable = p"[A-Z]([a-zA-Z0-9_]*)" > Variable
#---------------------------#
#           string constant #
#---------------------------#
string_constant = E"\"" + p"[^\"]*" + E"\"" > StringConstant
#---------------------------#
#       symbolic constant   #
#---------------------------#
symbolic_constant = p"[a-z]([a-zA-Z0-9_]*)" > SymbolicConstant
#---------------------------#
#   anonymous variable      #
#---------------------------#
anonymous_variable = e"_" > AnonymousVariable
#---------------------------#
#           number constant #
#---------------------------#
number = (e"0" | p"[1-9]([0-9]*)") > NumberConstant
neg_number = E"-" + number > Negated
#---------------------------#
#       variable term       #
#---------------------------#
variable_term = variable | anonymous_variable
#---------------------------#
#       ground term         #
#---------------------------#
ground_term = symbolic_constant | string_constant | number # | neg_number
#---------------------------#
#           basic term      #
#---------------------------#
basic_term = ground_term | variable_term
basic_terms = StarList(basic_term, E",")
#---------------------------#
#               arith op    #
#---------------------------#
# arith_op = e"+" | e"-" | e"*" | e"/"
#---------------------------#
#               functor     #
#---------------------------#
functor = p"[a-z]([a-zA-Z0-9_]*)" > Functor
#---------------------------#
#                   term    #
#---------------------------#
term = Delayed()
#---------------------------#
#                   terms   #
#---------------------------#
terms = StarList(term, E",")
#---------------------------#
#           function term   #
#---------------------------#
function_term = functor + E"(" + terms + E")" |> FunctionTerm
#---------------------------#
#                   value   #
#---------------------------#
value = ground_term | variable_term | (E"(" + term + E")")
#---------------------------#
#               negated     #
#---------------------------#
negated = Delayed()
negated.matcher = function_term | value | (spc + E"-" + negated > Negated)
#---------------------------#
# multiplicative factor     #
#---------------------------#
mul_factor = E"*" + negated
#---------------------------#
#       inverted factor     #
#---------------------------#
inv_factor = E"/" + negated > Inverted
#---------------------------#
#               product     #
#---------------------------#
product = negated | (negated + Plus(mul_factor | inv_factor) |> Product)
#---------------------------#
#       positive parcel     #
#---------------------------#
pos_parcel = E"+" + product
#---------------------------#
#        negated parcel     #
#---------------------------#
neg_parcel = E"-" + product > Negated
#---------------------------#
#                sum        #
#---------------------------#
sum = product | (product + Plus(pos_parcel | neg_parcel) |> Sum)
#---------------------------#
#                   term    #
#---------------------------#
term.matcher = value | function_term | negated | product | sum
term_grammar = term + Eos()
#
#
#   LITERALS
#
#
#---------------------------#
#               bin op      #
#---------------------------#
bin_lt = (E"<" > LT)
bin_le = (E"<=" > LE)
bin_eq = (E"=" > EQ)
bin_ne = (E"!=" > NE)
bin_gt = (E">" > GT)
bin_ge = (E">=" > GE)
bin_op = bin_lt | bin_le | bin_eq | bin_ne | bin_gt | bin_ge
#---------------------------#
#           builtin atom    #
#---------------------------#
builtin_atom =
    (term + bin_lt + term |> LTAtom) |   # less than
    (term + bin_le + term |> LEAtom) |   # less equal
    (term + bin_eq + term |> EQAtom) |   # equal
    (term + bin_ne + term |> NEAtom) |   # not equal
    (term + bin_gt + term |> GTAtom) |   # greater than
    (term + bin_ge + term |> GEAtom)     # greater equal
#---------------------------#
#       classical literal   #
#---------------------------#
atom = symbolic_constant + Opt(E"(" + terms + E")") # |> Atom

negated_atom = Delayed()
negated_atom.matcher = atom | (spc + E"-" + negated_atom > Negated)

classical_literal = atom | negated_atom
#---------------------------#
#             naf literal   #
#---------------------------#
naf_literal = builtin_atom | classical_literal | (E"not" + classical_literal > NafLiteral)
naf_literals = StarList(naf_literal, E",")
#
literal_grammar = naf_literal + Eos()
#
#
#   AGGREGATES
#
#
#---------------------------#
#       weight at level     #
#---------------------------#
weight_at_level = (term > Weight) + Opt(E"@" + term > Level) + Opt(E"," + terms |> Contribution) |> WeightAtLevel
#---------------------------#
#           aggregates      #
#---------------------------#
aggregate_element = (basic_terms |> Elements) + Opt(E":" + naf_literals |> InclusionCondition)
aggregate_elements = StarList(aggregate_element, E";")
#
count_aggregate = Opt(term + bin_op |> LeftBound) +
                    E"#count" + spc + E"{" + (aggregate_elements |> Constraint) + E"}" +
                    Opt(bin_op + term |> RightBound) |> CountAggregate
#
sum_aggregate = Opt(term + bin_op |> LeftBound) +
                E"#sum" + spc + E"{" + (aggregate_elements |> Constraint) + E"}" +
                Opt(bin_op + term |> RightBound) |> SumAggregate
#
min_aggregate = Opt(term + bin_op |> LeftBound) +
                E"#min" + spc + E"{" + (aggregate_elements |> Constraint) + E"}" +
                Opt(bin_op + term |> RightBound) |> MinAggregate
#
max_aggregate = Opt(term + bin_op |> LeftBound) +
                E"#max" + spc + E"{" + (aggregate_elements |> Constraint) + E"}" +
                Opt(bin_op + term |> RightBound) |> MaxAggregate
#
aggregate = count_aggregate | sum_aggregate | min_aggregate | max_aggregate
aggregate_grammar = aggregate + Eos()
#
#
#   PROGRAMS
#
#
#---------------------------#
#               choice      #
#---------------------------#
choice_element = classical_literal | (classical_literal + E":" + naf_literals |> RestrictedChoice)
choice_elements = StarList(choice_element, E";")
choice = Opt(term + bin_op |> LeftBound) +
            E"{" + (choice_elements |> Choices) + E"}" +
            Opt(bin_op + term |> RightBound) |> Choice
#---------------------------#
#          disjunction      #
#---------------------------#
disjunction = PlusList(classical_literal, E"|") |> Disjunction
#---------------------------#
#                 body      #
#---------------------------#
body_element = naf_literal | aggregate | (E"not" + aggregate > Negated)
body = PlusList(body_element, E",") |> Body
#---------------------------#
#                 head      #
#---------------------------#
head = disjunction | choice
#---------------------------#
#           annotation      #
#---------------------------#
annotation = E"::" + term > Annotation
#---------------------------#
#            statement      #
#---------------------------#
statement =
    # strong restriction
    (E":-" + body + E"." > Restriction) |
    # rule
    ((head + Opt(annotation) |> Head) + Opt(E":-" + body) + E"." |> Rule) |
    # weak restriction
    (E":~" + Opt(body) + E"." + spc + E"[" + weight_at_level + E"]" |> WeakRestriction)
statements = StarList(statement, E"")
#---------------------------#
#                query      #
#---------------------------#
query = classical_literal + E"?" > Query
#---------------------------#
#              program      #
#---------------------------#
program = statements + Opt(query) |> Program
#---------------------------#
#           asp             #
#---------------------------#
asp_grammar = program + Eos()
end
end
end

parse_asp(source::String, grammar=asp_grammar) = parse_one(source, grammar)[1] # |> simplify

function debug_asp(source::String, grammar=asp_grammar, show_trace=false)
    debug, task = make(Debug, source, grammar; delegate=NoCache)
    try
        once(task)
        expr = parse_asp(source, grammar)
        println("---\n\"$source\"\n\tparses to\n$(expr)\n---")
    catch #_::ParserException
        println("Error parsing after \"$(source[1:debug.max_iter - 1])\" and before \"$(source[debug.max_iter:end])\".\n")

        if show_trace
            parse_dbg(source, Trace(grammar=grammar))
        end
    end
end

end