Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.java.programmer > #37777 > unrolled thread
| Started by | Victor Porton <porton@narod.ru> (Victor Porton) |
|---|---|
| First post | 2017-12-09 00:53 +1200 |
| Last post | 2017-12-12 11:14 +0200 |
| Articles | 15 — 9 participants |
Back to article view | Back to comp.lang.java.programmer
Two exceptions Victor Porton <porton@narod.ru> (Victor Porton) - 2017-12-09 00:53 +1200
Re: Two exceptions Eric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman) - 2017-12-08 18:21 +1200
Re: Two exceptions Victor Porton <porton@narod.ru> (Victor Porton) - 2017-12-09 01:55 +1200
Re: Two exceptions Eric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman) - 2017-12-09 07:39 +1200
Re: Two exceptions Victor Porton <porton@narod.ru> (Victor Porton) - 2017-12-10 02:39 +1200
Re: Two exceptions Victor Porton <porton@narod.ru> (Victor Porton) - 2017-12-10 02:46 +1200
Re: Two exceptions Kiwi User <martin@mydomain.invalid> (Kiwi User) - 2017-12-10 00:52 +1200
Re: Two exceptions Kiwi User <martin@mydomain.invalid> - 2017-12-10 21:50 +0000
Re: Two exceptions Patrick Roemer <sangamon@netcologne.de> (Patrick Roemer) - 2017-12-09 13:47 +1200
Re: Two exceptions Arne Vajhøj <arne@vajhoej.dk> (Arne Vajhøj) - 2017-12-08 18:49 +1200
Re: Two exceptions v_borchert@despammed.com (Volker Borchert) (Volker Borchert) - 2017-12-09 07:53 +1200
Re: Two exceptions ram@zedat.fu-berlin.de (Stefan Ram) (Stefan Ram) - 2017-12-09 16:15 +1200
Re: Two exceptions Eric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman) - 2017-12-09 14:55 +1200
Re: Two exceptions Patrick Roemer <sangamon@netcologne.de> (Patrick Roemer) - 2017-12-11 15:09 +1200
Re: Two exceptions Victor Porton <porton@narod.ru> - 2017-12-12 11:14 +0200
| From | Victor Porton <porton@narod.ru> (Victor Porton) |
|---|---|
| Date | 2017-12-09 00:53 +1200 |
| Subject | Two exceptions |
| Message-ID | <4199194256@f38.n261.z1.binkp.net> |
I am writing something like a recursive descent parser: That is, I inspect object and its parts. When a part of an object is wrong, my method parse() throws my exception ParseException. It is sometimes handled in parse() of a higher-level object. Example: An object X may be either X1 or X2 or X3 class. I try to call parse() for an object of X1 class. If it parses, the higher level parse() (for X) return X1 casted to X. If it fails with exception ParseException, X2 is tried, and if that fails too X3 is tried. Or for an object X I try calling parse() for subobjects Y and Z. If either throws ParseException, the parse exception is propagated for parse() of X. In this way, I consider an object and its possible parts. This is sometimes called recursive descent. But I want also FatalParseException meant to stop parsing of a file altogether if it happens in the object or its subobject. Human logic suggests that FatalParseException is a kind of ParseException and so should be its subclass. But this way handling ParseException we would also handle FatalParseException what is wrong, because FatalParseException is meant to be handled only in the outer method, outside of all parse() methods. So I have two alternatives: 1. Derive ParseException from FatalParseException what seems to do the right thing but seems illogical. 2. Not derive either of these two exceptions from each other. By the way, I am to rewrite an Ada project in Java (because the Ada compiler is too buggy and I can't work with it). In Ada all exceptions are independent (no exception is a subclass of another one). -- Victor Porton - http://portonvictor.org
[toc] | [next] | [standalone]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman) |
|---|---|
| Date | 2017-12-08 18:21 +1200 |
| Message-ID | <2386781254@f38.n261.z1.binkp.net> |
| In reply to | #37777 |
On 12/8/2017 5:53 PM, Victor Porton wrote:
> I am writing something like a recursive descent parser: That is, I inspect
> object and its parts. When a part of an object is wrong, my method parse()
> throws my exception ParseException. It is sometimes handled in parse() of a
> higher-level object.
>
> Example:
>
> An object X may be either X1 or X2 or X3 class.
>
> I try to call parse() for an object of X1 class. If it parses, the higher
> level parse() (for X) return X1 casted to X. If it fails with exception
> ParseException, X2 is tried, and if that fails too X3 is tried.
>
> Or for an object X I try calling parse() for subobjects Y and Z. If either
> throws ParseException, the parse exception is propagated for parse() of X.
>
> In this way, I consider an object and its possible parts. This is sometimes
> called recursive descent.
>
> But I want also FatalParseException meant to stop parsing of a file
> altogether if it happens in the object or its subobject.
>
> Human logic suggests that FatalParseException is a kind of ParseException
> and so should be its subclass. But this way handling ParseException we would
> also handle FatalParseException what is wrong, because FatalParseException
> is meant to be handled only in the outer method, outside of all parse()
> methods.
>
> So I have two alternatives:
>
> 1. Derive ParseException from FatalParseException what seems to do the right
> thing but seems illogical.
>
> 2. Not derive either of these two exceptions from each other.
>
> By the way, I am to rewrite an Ada project in Java (because the Ada compiler
> is too buggy and I can't work with it). In Ada all exceptions are
> independent (no exception is a subclass of another one).
Alternative #1 seems preferable (to me). Perhaps you would find it
more palatable if you renamed FatalParseException to ParseFailure and
ParseException to ParseMismatch.
However, I feel you may be misusing exceptions. They are supposed
to be, well, "exceptional" rather than "normal control flow." In a recursive
descent parser you *expect* many matching failures, just in the ordinary course
of events; a matching failure is not "exceptional." (Also, building a
Throwable can be rather expensive, especially if it is thrown from a deep point
in the call stack and must fill in a long stack trace. Java's optimizations
tend to focus on the "normal" cases rather than on the "exceptional" ones, so
throwing and catching, even though much improved since Java 1.0, still involves
a lot of work.)
Instead of announcing a "venial" mismatch by throwing an exception,
consider returning a status value from each parse method. This might be a
boolean ("It's an X1" / "It's not an X1") or a reference value ("It's an X1 and
here it is" / "It's not an X1 and here's a null"), or something more
structured ("Here's a status-and-result-and-info object; look at its fields to
find out what happened"). Not only is this likely to produce faster code, but
it may also simplify
X result;
try {
result = X1.parse();
} catch (ParseMismatch ex1) {
try {
result = X2.parse();
} catch (ParseMismatch ex2) {
try {
result = X3.parse();
} catch (ParseMismatch ex3) {
...
}
to something more like
X result = X1.parse();
if (result == null)
result = X2.parse();
if (result == null)
result = X3.parse();
...
(You could, of course, nest these differently.)
Perhaps the structure of the existing Ada code is not amenable
to this sort of transformation -- but if I were you I'd give it some
consideration.
--
esosman@comcast-dot-net.invalid
Eleven hundred thirty-nine days to go.
[toc] | [prev] | [next] | [standalone]
| From | Victor Porton <porton@narod.ru> (Victor Porton) |
|---|---|
| Date | 2017-12-09 01:55 +1200 |
| Message-ID | <1735587349@f38.n261.z1.binkp.net> |
| In reply to | #37778 |
Eric Sosman wrote:
> On 12/8/2017 5:53 PM, Victor Porton wrote:
>> I am writing something like a recursive descent parser: That is, I
>> inspect object and its parts. When a part of an object is wrong, my
>> method parse() throws my exception ParseException. It is sometimes
>> handled in parse() of a higher-level object.
>>
>> Example:
>>
>> An object X may be either X1 or X2 or X3 class.
>>
>> I try to call parse() for an object of X1 class. If it parses, the higher
>> level parse() (for X) return X1 casted to X. If it fails with exception
>> ParseException, X2 is tried, and if that fails too X3 is tried.
>>
>> Or for an object X I try calling parse() for subobjects Y and Z. If
>> either throws ParseException, the parse exception is propagated for
>> parse() of X.
>>
>> In this way, I consider an object and its possible parts. This is
>> sometimes called recursive descent.
>>
>> But I want also FatalParseException meant to stop parsing of a file
>> altogether if it happens in the object or its subobject.
>>
>> Human logic suggests that FatalParseException is a kind of ParseException
>> and so should be its subclass. But this way handling ParseException we
>> would also handle FatalParseException what is wrong, because
>> FatalParseException is meant to be handled only in the outer method,
>> outside of all parse() methods.
>>
>> So I have two alternatives:
>>
>> 1. Derive ParseException from FatalParseException what seems to do the
>> right thing but seems illogical.
>>
>> 2. Not derive either of these two exceptions from each other.
>>
>> By the way, I am to rewrite an Ada project in Java (because the Ada
>> compiler is too buggy and I can't work with it). In Ada all exceptions
>> are independent (no exception is a subclass of another one).
>
> Alternative #1 seems preferable (to me). Perhaps you would find it
> more palatable if you renamed FatalParseException to ParseFailure and
> ParseException to ParseMismatch.
>
> However, I feel you may be misusing exceptions. They are supposed
> to be, well, "exceptional" rather than "normal control flow." In a
> recursive descent parser you *expect* many matching failures, just in
> the ordinary course of events; a matching failure is not "exceptional."
> (Also, building a Throwable can be rather expensive, especially if it
> is thrown from a deep point in the call stack and must fill in a long
> stack trace. Java's optimizations tend to focus on the "normal" cases
> rather than on the "exceptional" ones, so throwing and catching, even
> though much improved since Java 1.0, still involves a lot of work.)
>
> Instead of announcing a "venial" mismatch by throwing an exception,
> consider returning a status value from each parse method. This might
> be a boolean ("It's an X1" / "It's not an X1") or a reference value
> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
> or something more structured ("Here's a status-and-result-and-info
> object; look at its fields to find out what happened"). Not only is
> this likely to produce faster code, but it may also simplify
>
> X result;
> try {
> result = X1.parse();
> } catch (ParseMismatch ex1) {
> try {
> result = X2.parse();
> } catch (ParseMismatch ex2) {
> try {
> result = X3.parse();
> } catch (ParseMismatch ex3) {
> ...
> }
>
> to something more like
>
> X result = X1.parse();
> if (result == null)
> result = X2.parse();
> if (result == null)
> result = X3.parse();
> ...
The main reason I chose to use exceptions for this, is to allow parse() method
to return the result of parsing (not a boolean value). For example result of
parsing an integer may return "long".
Can you propose another alternative? It is to be easily programmable (if I find
handling exceptions too slow, I always can rewrite my program).
Oh, I see, you proposed to return "null" instead of my ParseException. It
however also seems to me not a quite good solution, because returning null may
be a non-exceptionable situation (as for a missing optional object in the
parsed resource).
> (You could, of course, nest these differently.)
>
> Perhaps the structure of the existing Ada code is not amenable
> to this sort of transformation -- but if I were you I'd give it
> some consideration.
>
--
Victor Porton - http://portonvictor.org
[toc] | [prev] | [next] | [standalone]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman) |
|---|---|
| Date | 2017-12-09 07:39 +1200 |
| Message-ID | <1984930110@f38.n261.z1.binkp.net> |
| In reply to | #37780 |
On 12/8/2017 6:55 PM, Victor Porton wrote:
> Eric Sosman wrote:
>
>> On 12/8/2017 5:53 PM, Victor Porton wrote:
>>> [... using exceptions to control a recursive descent parser ...]
>> [... maybe exceptions aren't the best tool ...]
>> Instead of announcing a "venial" mismatch by throwing an exception,
>> consider returning a status value from each parse method. This might
>> be a boolean ("It's an X1" / "It's not an X1") or a reference value
>> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
>> or something more structured ("Here's a status-and-result-and-info
>> object; look at its fields to find out what happened"). [...]
>
> The main reason I chose to use exceptions for this, is to allow parse()
> method to return the result of parsing (not a boolean value). For example
> result of parsing an integer may return "long".
>
> Can you propose another alternative? It is to be easily programmable (if I
> find handling exceptions too slow, I always can rewrite my program).
>
> Oh, I see, you proposed to return "null" instead of my ParseException. It
> however also seems to me not a quite good solution, because returning null
> may be a non-exceptionable situation (as for a missing optional object in
> the parsed resource).
If `null' is a legitimate result (so you can't use a `null' return
to mean "Not matched"), use a status-and-result-and-info object like
class ParseResult {
final boolean matched; // true = success, false = failure
final Object result; // only relevant if matched == true
// ... other info as desired ...
}
... and return a ParseResult instance from each parse() method. The `result'
field might be `null', but if `matched' is true the `null' is a "real" `null',
not a failure indication.
--
esosman@comcast-dot-net.invalid
Eleven hundred thirty-eight days to go.
[toc] | [prev] | [next] | [standalone]
| From | Victor Porton <porton@narod.ru> (Victor Porton) |
|---|---|
| Date | 2017-12-10 02:39 +1200 |
| Message-ID | <3640044093@f38.n261.z1.binkp.net> |
| In reply to | #37782 |
Eric Sosman wrote:
> On 12/8/2017 6:55 PM, Victor Porton wrote:
>> Eric Sosman wrote:
>>
>>> On 12/8/2017 5:53 PM, Victor Porton wrote:
>>>> [... using exceptions to control a recursive descent parser ...]
>>> [... maybe exceptions aren't the best tool ...]
>>> Instead of announcing a "venial" mismatch by throwing an
>>> exception,
>>> consider returning a status value from each parse method. This might
>>> be a boolean ("It's an X1" / "It's not an X1") or a reference value
>>> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
>>> or something more structured ("Here's a status-and-result-and-info
>>> object; look at its fields to find out what happened"). [...]
>>
>> The main reason I chose to use exceptions for this, is to allow parse()
>> method to return the result of parsing (not a boolean value). For example
>> result of parsing an integer may return "long".
>>
>> Can you propose another alternative? It is to be easily programmable (if
>> I find handling exceptions too slow, I always can rewrite my program).
>>
>> Oh, I see, you proposed to return "null" instead of my ParseException. It
>> however also seems to me not a quite good solution, because returning
>> null may be a non-exceptionable situation (as for a missing optional
>> object in the parsed resource).
>
> If `null' is a legitimate result (so you can't use a `null' return
> to mean "Not matched"), use a status-and-result-and-info object like
>
> class ParseResult {
> final boolean matched; // true = success, false = failure
> final Object result; // only relevant if matched == true
> // ... other info as desired ...
> }
I haven't considered this perhaps because Ada does not allow a class to be a
member of object (without wrapping it into something called Indefinite_Wrapper
or something similar). So in Ada this would be inconvenient.
> ... and return a ParseResult instance from each parse() method. The
> `result' field might be `null', but if `matched' is true the `null' is
> a "real" `null', not a failure indication.
>
--
Victor Porton - http://portonvictor.org
[toc] | [prev] | [next] | [standalone]
| From | Victor Porton <porton@narod.ru> (Victor Porton) |
|---|---|
| Date | 2017-12-10 02:46 +1200 |
| Message-ID | <378316829@f38.n261.z1.binkp.net> |
| In reply to | #37796 |
Victor Porton wrote:
> Eric Sosman wrote:
>
>> On 12/8/2017 6:55 PM, Victor Porton wrote:
>>> Eric Sosman wrote:
>>>
>>>> On 12/8/2017 5:53 PM, Victor Porton wrote:
>>>>> [... using exceptions to control a recursive descent parser ...]
>>>> [... maybe exceptions aren't the best tool ...]
>>>> Instead of announcing a "venial" mismatch by throwing an
>>>> exception,
>>>> consider returning a status value from each parse method. This might
>>>> be a boolean ("It's an X1" / "It's not an X1") or a reference value
>>>> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
>>>> or something more structured ("Here's a status-and-result-and-info
>>>> object; look at its fields to find out what happened"). [...]
>>>
>>> The main reason I chose to use exceptions for this, is to allow parse()
>>> method to return the result of parsing (not a boolean value). For
>>> example result of parsing an integer may return "long".
>>>
>>> Can you propose another alternative? It is to be easily programmable (if
>>> I find handling exceptions too slow, I always can rewrite my program).
>>>
>>> Oh, I see, you proposed to return "null" instead of my ParseException.
>>> It however also seems to me not a quite good solution, because returning
>>> null may be a non-exceptionable situation (as for a missing optional
>>> object in the parsed resource).
>>
>> If `null' is a legitimate result (so you can't use a `null' return
>> to mean "Not matched"), use a status-and-result-and-info object like
>>
>> class ParseResult {
>> final boolean matched; // true = success, false = failure
>> final Object result; // only relevant if matched == true
>> // ... other info as desired ...
>> }
>
> I haven't considered this perhaps because Ada does not allow a class to be
> a member of object (without wrapping it into something called
> Indefinite_Wrapper or something similar). So in Ada this would be
> inconvenient.
Hm, well, I should have considered this. In Ada this can be made with a
constructor function (just like as in Java) and an accessor hiding
Indefinite_Wrapper as a details of implementation. Weird that I have not
considered this. Well, maybe this is because I didn't wonder about performance
very much and considered many exceptions OK.
But thanks for the idea. I think, I will follow your advice and remove
ParseException altogether leaving only FatalParseException.
>> ... and return a ParseResult instance from each parse() method. The
>> `result' field might be `null', but if `matched' is true the `null' is
>> a "real" `null', not a failure indication.
>>
--
Victor Porton - http://portonvictor.org
[toc] | [prev] | [next] | [standalone]
| From | Kiwi User <martin@mydomain.invalid> (Kiwi User) |
|---|---|
| Date | 2017-12-10 00:52 +1200 |
| Message-ID | <2064065438@f38.n261.z1.binkp.net> |
| In reply to | #37796 |
On Sun, 10 Dec 2017 02:39:33 +0200, Victor Porton wrote:
> I haven't considered this perhaps because Ada does not allow a class to
> be a member of object (without wrapping it into something called
> Indefinite_Wrapper or something similar). So in Ada this would be
> inconvenient.
>
This looks like the best solution suggested so far, particularly if it is
expanded to include an error message and provide a boolean isError() method.
--
Martin | martin at
Gregorie | gregorie
| dot org
[toc] | [prev] | [next] | [standalone]
| From | Kiwi User <martin@mydomain.invalid> |
|---|---|
| Date | 2017-12-10 21:50 +0000 |
| Message-ID | <p0ka7a$v6a$1@news.albasani.net> |
| In reply to | #37799 |
On Sun, 10 Dec 2017 00:52:14 +1200, Kiwi User wrote:
> On Sun, 10 Dec 2017 02:39:33 +0200, Victor Porton wrote:
>
>> I haven't considered this perhaps because Ada does not allow a class to
>> be a member of object (without wrapping it into something called
>> Indefinite_Wrapper or something similar). So in Ada this would be
>> inconvenient.
>>
> This looks like the best solution suggested so far, particularly if it
> is expanded to include an error message and provide a boolean isError()
> method.
Gaaaagh: Misplaced comment: I meant to say that I thought Eric's
ParseResult class looked, to me anyway, like the best solution I've seen
so far.
It deals with all possible results neatly and doesn't need to use
exception handling for unexceptional results (matched, didn't match,
unrecognised input) so it leaves Exceptions free to handle parser
internal errors AND, if extended to hold error descriptions, it provides
a neat way of returning error messages without having to pass an error
reporting channel reference to every operation on the recursive parse
stack.
--
Martin | martin at
Gregorie | gregorie
| dot org
[toc] | [prev] | [next] | [standalone]
| From | Patrick Roemer <sangamon@netcologne.de> (Patrick Roemer) |
|---|---|
| Date | 2017-12-09 13:47 +1200 |
| Message-ID | <2326571928@f38.n261.z1.binkp.net> |
| In reply to | #37780 |
Responding to Victor Porton: > The main reason I chose to use exceptions for this, is to allow parse() > method to return the result of parsing (not a boolean value). For example > result of parsing an integer may return "long". > > Can you propose another alternative? It is to be easily programmable (if I > find handling exceptions too slow, I always can rewrite my program). > > Oh, I see, you proposed to return "null" instead of my ParseException. It > however also seems to me not a quite good solution, because returning null > may be a non-exceptionable situation (as for a missing optional object in > the parsed resource). Instead of null, you could use (potentially nested) java.util.Optional for both, signaling failed parse attempts and optional productions. failure: empty success (plain): of(x) success (optional, non-match): of(empty) success (optional, match): of(of(x)) Preferably, you could introduce a specialized union type for the parse result, allowing you to propagate detail information in the failure case. See the Reply type here, for example: https://github.com/jon-hanson/parsecj#types Best regards, Patrick
[toc] | [prev] | [next] | [standalone]
| From | Arne Vajhøj <arne@vajhoej.dk> (Arne Vajhøj) |
|---|---|
| Date | 2017-12-08 18:49 +1200 |
| Message-ID | <3203151262@f38.n261.z1.binkp.net> |
| In reply to | #37777 |
On 12/8/2017 5:53 PM, Victor Porton wrote:
> I am writing something like a recursive descent parser: That is, I inspect
> object and its parts. When a part of an object is wrong, my method parse()
> throws my exception ParseException. It is sometimes handled in parse() of a
> higher-level object.
>
> Example:
>
> An object X may be either X1 or X2 or X3 class.
>
> I try to call parse() for an object of X1 class. If it parses, the higher
> level parse() (for X) return X1 casted to X. If it fails with exception
> ParseException, X2 is tried, and if that fails too X3 is tried.
>
> Or for an object X I try calling parse() for subobjects Y and Z. If either
> throws ParseException, the parse exception is propagated for parse() of X.
>
> In this way, I consider an object and its possible parts. This is sometimes
> called recursive descent.
>
> But I want also FatalParseException meant to stop parsing of a file
> altogether if it happens in the object or its subobject.
>
> Human logic suggests that FatalParseException is a kind of ParseException
> and so should be its subclass. But this way handling ParseException we would
> also handle FatalParseException what is wrong, because FatalParseException
> is meant to be handled only in the outer method, outside of all parse()
> methods.
>
> So I have two alternatives:
>
> 1. Derive ParseException from FatalParseException what seems to do the right
> thing but seems illogical.
>
> 2. Not derive either of these two exceptions from each other.
First you need to decide if you really want to use exceptions for this. It is
usually frowned on to use exceptions to control flow. But OK the Java API has
some parse methods throwing checked exceptions.
If you want to proceed this route then I would go for:
3) Base-class ParseException with two sub-classes FatalParseException
and NonFatalParseException.
I think that is the OO representation of your domain logic.
Arne
[toc] | [prev] | [next] | [standalone]
| From | v_borchert@despammed.com (Volker Borchert) (Volker Borchert) |
|---|---|
| Date | 2017-12-09 07:53 +1200 |
| Message-ID | <1606898186@f38.n261.z1.binkp.net> |
| In reply to | #37779 |
Arne Vajh??j wrote: > If you want to proceed this route then I would go for: > > 3) Base-class ParseException with two sub-classes FatalParseException > and NonFatalParseException. Second that. -- "I'm a doctor, not a mechanic." Dr Leonard McCoy <mccoy@ncc1701.starfleet.fed> "I'm a mechanic, not a doctor." Volker Borchert <v_borchert@despammed.com>
[toc] | [prev] | [next] | [standalone]
| From | ram@zedat.fu-berlin.de (Stefan Ram) (Stefan Ram) |
|---|---|
| Date | 2017-12-09 16:15 +1200 |
| Message-ID | <3577663232@f38.n261.z1.binkp.net> |
| In reply to | #37777 |
Victor Porton <porton@narod.ru> writes:
>So I have two alternatives:
>1. Derive ParseException from FatalParseException what seems to do the right
>thing but seems illogical.
The type of a result of a parse is
class ParseResult { ...
which can have subclasses
class ProperParseResult extends ParseResult { ... class ErrorParseResult
extends ParseResult { ...
which in turn can have several subclasses each of which
might implement several interfaces.
A parseResult might contain other parseResults (AST).
But without knowing more details of the problem, it is
difficult to suggest further details.
>An object X may be either X1 or X2 or X3 class.
When X is not X1, of course, this is neither an error
nor a reason to throw.
(Check whether you might use the anti-pattern "switch on
(sub)type [i.e., X1, X2, X3]" whose use should be replaced
by polymorphism.)
Traversing given nested objects is not called "parsing"
(which is working on a sequence of tokens), it is more like
a tree travesal, which might suggest to check out the
visitor pattern.
[toc] | [prev] | [next] | [standalone]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman) |
|---|---|
| Date | 2017-12-09 14:55 +1200 |
| Message-ID | <168218953@f38.n261.z1.binkp.net> |
| In reply to | #37784 |
On 12/9/2017 11:15 AM, Stefan Ram wrote:
> Victor Porton <porton@narod.ru> writes:
>> So I have two alternatives:
>> 1. Derive ParseException from FatalParseException what seems to do the right
>> thing but seems illogical.
>
> The type of a result of a parse is
>
> class ParseResult { ...
>
> which can have subclasses
>
> class ProperParseResult extends ParseResult { ...
> class ErrorParseResult extends ParseResult { ...
>
> which in turn can have several subclasses each of which
> might implement several interfaces.
>
> A parseResult might contain other parseResults (AST).
This would work, yes, but I think it would lead to clumsy
code at the call site, something like
ParseResult result = parse();
if (result instanceof ProperParseResult) {
ProperParseResult ppr = (ProperParseResult) result;
...
} else if (result instanceof ErrorParseResult) {
ErrorParseResult epr = (ErrorParseResult) result;
...
} else {
throw new IllegalStateException("bad result", result);
}
That is, you'd need to use `instanceof' (or equivalent) to learn what kind of a
result you obtained -- and if you find yourself using `instanceof' a lot,
that's usually a sign that the design could be improved.
(It can be argued that `try/catch' is a disguised `instanceof',
particularly if there are multiple `catch' clauses. This seems forgivable,
though, on the grounds that exceptions are "unusual.")
--
esosman@comcast-dot-net.invalid
Eleven hundred thirty-eight days to go.
[toc] | [prev] | [next] | [standalone]
| From | Patrick Roemer <sangamon@netcologne.de> (Patrick Roemer) |
|---|---|
| Date | 2017-12-11 15:09 +1200 |
| Message-ID | <1592952094@f38.n261.z1.binkp.net> |
| In reply to | #37793 |
Responding to Eric Sosman:
> On 12/9/2017 11:15 AM, Stefan Ram wrote:
>> The type of a result of a parse is
>>
>> class ParseResult { ...
>>
>> which can have subclasses
>>
>> class ProperParseResult extends ParseResult { ...
>> class ErrorParseResult extends ParseResult { ...
>>
>> which in turn can have several subclasses each of which
>> might implement several interfaces.
>>
>> A parseResult might contain other parseResults (AST).
>
> This would work, yes, but I think it would lead to clumsy
> code at the call site, something like
>
> ParseResult result = parse();
> if (result instanceof ProperParseResult) {
> ProperParseResult ppr = (ProperParseResult) result;
> ...
> } else if (result instanceof ErrorParseResult) {
> ErrorParseResult epr = (ErrorParseResult) result;
> ...
> } else {
> throw new IllegalStateException("bad result", result);
> }
Not necessarily, or rather, not at all. Ignoring the AST suggestion...
ParseResult could declare a #matched() method, equivalent to the
#matched field in your single-class example. Handling would be identical
to your suggestion, but the types would be clearer, IMHO. Members that are
"only relevant if" another member carries a certain value feels like a code
smell to me, too.
There could also be a ParseResult#resultValue() method that throws in the
ErrorParseResult case. This would give the top level caller the same experience
as in the exception-signaling approach, but based on a cleaner implementation.
Furthermore there could be a ParseResult#fold[T]() method that takes two
lambdas, Object -> T and Throwable -> T, and invokes the corresponding lambda
for the success resp. error case.
Finally, the instanceof cascade could be replaced with a proper Visitor
implementation over ParseResult. Stefan mentioned the Visitor pattern, but
constrained it to the tree traversal context. It is primarily about double
dispatch, though, and would apply nicely to the ParseResult hierarchy, where
there's no tree in sight.
Actually, all the approaches listed above could be supported in parallel,
leaving the result resolution style to the discretion of the caller - which is
what the author(s) of parsecj[1] opted for (excluding the Visitor option).
The next step from this approach might be the introduction of combinators that
merge multiple results into one.
Best regards,
Patrick
[1] https://github.com/jon-hanson/parsecj#types
[toc] | [prev] | [next] | [standalone]
| From | Victor Porton <porton@narod.ru> |
|---|---|
| Date | 2017-12-12 11:14 +0200 |
| Message-ID | <p0o6m8$d77$1@gioia.aioe.org> |
| In reply to | #37808 |
Patrick Roemer wrote:
> Responding to Eric Sosman:
>> On 12/9/2017 11:15 AM, Stefan Ram wrote:
>>> The type of a result of a parse is
>>>
>>> class ParseResult { ...
>>>
>>> which can have subclasses
>>>
>>> class ProperParseResult extends ParseResult { ...
>>> class ErrorParseResult extends ParseResult { ...
>>>
>>> which in turn can have several subclasses each of which
>>> might implement several interfaces.
>>>
>>> A parseResult might contain other parseResults (AST).
>>
>> This would work, yes, but I think it would lead to clumsy
>> code at the call site, something like
>>
>> ParseResult result = parse();
>> if (result instanceof ProperParseResult) {
>> ProperParseResult ppr = (ProperParseResult) result;
>> ...
>> } else if (result instanceof ErrorParseResult) {
>> ErrorParseResult epr = (ErrorParseResult) result;
>> ...
>> } else {
>> throw new IllegalStateException("bad result", result);
>> }
>
> Not necessarily, or rather, not at all. Ignoring the AST suggestion...
>
> ParseResult could declare a #matched() method, equivalent to the
> #matched field in your single-class example. Handling would be identical
> to your suggestion, but the types would be clearer, IMHO. Members that are
> "only relevant if" another member carries a certain value feels like a
> code smell to me, too.
>
> There could also be a ParseResult#resultValue() method that throws in the
> ErrorParseResult case. This would give the top level caller the same
> experience
> as in the exception-signaling approach, but based on a cleaner
> implementation.
>
> Furthermore there could be a ParseResult#fold[T]() method that takes two
> lambdas, Object -> T and Throwable -> T, and invokes the corresponding
> lambda for the success resp. error case.
>
> Finally, the instanceof cascade could be replaced with a proper Visitor
> implementation over ParseResult. Stefan mentioned the Visitor pattern, but
> constrained it to the tree traversal context. It is primarily about double
> dispatch, though, and would apply nicely to the ParseResult hierarchy,
> where there's no tree in sight.
>
> Actually, all the approaches listed above could be supported in parallel,
> leaving the result resolution style to the discretion of the caller -
> which is what the author(s) of parsecj[1] opted for (excluding the Visitor
> option).
>
> The next step from this approach might be the introduction of combinators
> that merge multiple results into one.
>
> Best regards,
> Patrick
>
> [1] https://github.com/jon-hanson/parsecj#types
You can see my actual code which I have written (without exceptions
except of FatalParseError) at
https://github.com/vporton/xml-boiler/tree/master/core/src/main/java/org/boiler/rdf_recursive_descent
--
Victor Porton - http://portonvictor.org
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.java.programmer
csiph-web