[m-rev.] for review: direct parsing of goals

Julien Fischer jfischer at opturion.com
Mon Nov 23 16:47:46 AEDT 2015


Hi Zoltan,

On Mon, 23 Nov 2015, Zoltan Somogyi wrote:

> Make the parsing of goals more direct.
> 
> compiler/prog_io_dcg.m:
> compiler/prog_io_goal.m:
>     We used to parse goals using a fallback strategy: first we tried to parse
>     each term as a language construct, and if that failed, as a call.
>     The code that tested whether a term was a language construct looked at
>     not just the top function symbol of the term, but also its arity,
>     and sometimes at some of the function symbols nested inside. If the
>     arity or the nested function symbols weren't what was expected, the term
>     was parsed as a call. This meant that if the term was a malformed
>     language construct (e.g. because it used the right keyword/functor with
>     the wrong arity), it could be eventually parsed as a call
>
>     This diff changes over to a direct parsing strategy: deciding what kind
>     of goal a term SHOULD BE given its top level functor, and committing
>     to that choice.
> 
> doc/reference_manual.texi:
>     Fix a typo in the example of try expressions, and make that example
>     more readable. (The code for parsing try expressions was complex enough
>     that I had to look up the manual.)

The test suite should also be updated to cover the error messages added or
modified below.

...

> diff --git a/compiler/prog_io_dcg.m b/compiler/prog_io_dcg.m
> index cbcb2e4..c020273 100644
> --- a/compiler/prog_io_dcg.m
> +++ b/compiler/prog_io_dcg.m

...

> @@ -204,203 +226,316 @@ parse_non_call_dcg_goal(Functor, Args, Context, ContextPieces, MaybeGoal,
>              Functor = "promise_impure",
>              PromisedPurity = purity_impure

...

>          Functor = "[|]",
> -        Args = [X, Xs],
> -        % Non-empty list of terminals. Append the DCG output arg as the
> -        % new tail of the list, and unify the result with the DCG input arg.
> -        InVar = !.DCGVar,
> -        new_dcg_var(!VarSet, !Counter, OutVar),
> -        !:DCGVar = OutVar,
> -        ConsTerm0 = term.functor(term.atom("[|]"), [X, Xs], Context),
> -        term.coerce(ConsTerm0, ConsTerm),
> -        term_list_append_term(ConsTerm, term.variable(OutVar, Context), Term),
> -        Goal = unify_expr(Context, variable(InVar, Context), Term,
> -            purity_pure),
> -        MaybeGoal = ok1(Goal)
> +        ( if Args = [X, Xs] then
> +            % Translate
> +            %   [A, B, C]
> +            % as
> +            %   DCGIn = [A, B, C | DCGOut].
> +            InVar = !.DCGVar,
> +            new_dcg_var(!VarSet, !Counter, OutVar),
> +            !:DCGVar = OutVar,
> +            ConsTerm0 = term.functor(term.atom("[|]"), [X, Xs], Context),
> +            term.coerce(ConsTerm0, ConsTerm),
> +            OutVarTerm = term.variable(OutVar, Context),
> +            ( if term_list_append_term(ConsTerm, OutVarTerm, Term) then
> +                Goal = unify_expr(Context, variable(InVar, Context), Term,
> +                    purity_pure),
> +                MaybeGoal = ok1(Goal)
> +            else
> +                Pieces = [words("Error: there no"),

s/there no/there is no/

> +                    quote("[]"), words("at the end of the list."), nl],
> +                Spec = error_spec(severity_error, phase_term_to_parse_tree,
> +                    [simple_msg(Context, [always(Pieces)])]),
> +                MaybeGoal = error1([Spec])
> +            )
> +        else
> +            Pieces = [words("Error: in DCG clauses,"),
> +                words("the"), quote("[|]"), words("operator"),
> +                words("may only be used to match the input"),
> +                words("against a list of one or more items,"),
> +                words("and must therefore be used with arity 2."), nl],
> +            Spec = error_spec(severity_error, phase_term_to_parse_tree,
> +                [simple_msg(Context, [always(Pieces)])]),
> +            MaybeGoal = error1([Spec])
> +        )

...

> diff --git a/compiler/prog_io_goal.m b/compiler/prog_io_goal.m
> index 3749522..2fac107 100644
> --- a/compiler/prog_io_goal.m
> +++ b/compiler/prog_io_goal.m
> @@ -104,6 +104,19 @@
>  :- pred apply_purity_marker_to_maybe_goal(term::in, purity::in,
>      maybe1(goal)::in, maybe1(goal)::out) is det.
> 
> +    % Functions to construct error messages. Exported to prog_io_cdg.m,

s/prog_io_cdg/prog_io_dcg/


> +    % to allow DCG and non-DCG clauses to generate identical error messages
> +    % in analogous situations.
> +    %
> +:- func should_have_one_goal_prefix(list(format_component),
> +    term.context, string) = error_spec.
> +:- func should_have_two_terms_infix(list(format_component),
> +    term.context, string) = error_spec.
> +:- func should_have_two_goals_infix(list(format_component),
> +    term.context, string) = error_spec.
> +:- func should_have_one_x_one_goal_prefix(list(format_component),
> +    term.context, string, string) = error_spec.
> +
>  %-----------------------------------------------------------------------------%
>  %-----------------------------------------------------------------------------%
>

...


> @@ -169,159 +182,255 @@ parse_goal(Term, ContextPieces, MaybeGoal, !VarSet) :-

...


>      ;
>          Functor = "then",
> -        Args = [TryTerm, ThenTerm],
> -        parse_then_try_term(
> -            term.functor(atom("then"), [TryTerm, ThenTerm], Context),
> -            no, [], no, Context, ContextPieces, MaybeGoal, !VarSet)
> +        ( if Args = [TryTerm, ThenTerm] then
> +            parse_then_try_term(
> +                term.functor(atom("then"), [TryTerm, ThenTerm], Context),
> +                no, [], no, Context, ContextPieces, MaybeGoal, !VarSet)
> +        else
> +            % Since there was no "else" wrapped around this use of "then",
> +            % it is quite likely that this may have been intended to be
> +            % a try goal.
> +            % XXX Should we list all the things that may follow
> +            % the initial part of a try goal?
> +            Pieces = [words("Error: the "), quote("then"), words("operator,"),
> +                words("should be used either in an expression of the form"),
> +                quote("( if goal then goal else goal )"), suffix(","),
> +                words("or in an expression of the form"),
> +                quote("try [try_params] main_goal then success_goal"),
> +                suffix(","), words("optionally followed by"),
> +                quote("else failue_goal"), suffix(","),

s/failue_goal/failure_goal/

> +                words("which in turn may be followed by zero or more"),
> +                quote("catch"), words("clauses, and optionally by a single"),
> +                quote("catch_any"), words("clause."), nl],
> +            Spec = error_spec(severity_error, phase_term_to_parse_tree,
> +                [simple_msg(Context, [always(Pieces)])]),
> +            MaybeGoal = error1([Spec])
> +        )
>      ;

...

>          Functor = "catch_any",
> -        Args = [TermA, ArrowTerm],
> -        parse_catch_any_term(ArrowTerm, Context, ContextPieces,
> -            MaybeCatchAnyExpr, !VarSet),
> -        (
> -            MaybeCatchAnyExpr = ok1(CatchAnyExpr),
> -            ( if TermA = term.functor(atom("catch"), _, _) then
> -                parse_catch_then_try_term(TermA, yes(CatchAnyExpr),
> -                    Context, ContextPieces, MaybeGoal, !VarSet)
> -            else
> -                parse_else_then_try_term(TermA, [], yes(CatchAnyExpr),
> -                    Context, ContextPieces, MaybeGoal, !VarSet)
> +        ( if Args = [TermA, ArrowTerm] then
> +            parse_catch_any_term(ArrowTerm, Context, ContextPieces,
> +                MaybeCatchAnyExpr, !VarSet),
> +            (
> +                MaybeCatchAnyExpr = ok1(CatchAnyExpr),
> +                % YYY

What's YYY in aid of?

The diff looks fine otherwise.

Julien.



More information about the reviews mailing list