[Btw, no need to Cc: me in replies, as I'm subscribed to bison-patches]
On Fri, Mar 01, 2019 at 06:46:43AM +0100, Akim Demaille wrote:
> Hi!
>
> > Le 1 mars 2019 à 03:10, H. S. Teoh <[email protected]> a écrit :
[...]
> > In the ideal case, the lexer would simply be a struct that wraps
> > around an arbitrary input range of characters. In order to make
> > things work with the current code, though, I conceded to make
> > CalcLexer a class that implements Lexer. We can change that when we
> > get around to moving away from lexer classes.
[...]
> The version you sent works, thanks! Is it ok if I install it under
> your name? The work is yours, not mine: (add there's a question
> for you afterwards).
That's fine.
> (For the records, the GNU Coding Standards require a space before
> the parens in function calls. But the GCS were written because there
> are too many styles in C/C++, and you have to pick one. If the same
> dissonance hits D, then let's stick to the GCS. However, if there
> is one "true" style in D, tell me and let's stick to it.)
There isn't really "one true style" in D, though there is the Phobos
(standard library) style guide described here:
https://dlang.org/dstyle.html
This style is really only adhered to in Phobos, and not many D
programmers follow it. I follow it myself (though I diverge on some
minor points), mainly because I occasionally contribute to Phobos and
it's much easier to just use the style for my own code than to have to
switch between styles depending on which project I'm working on.
So it's really a judgment call whether we should stick to GCS or to
Phobos style. I have no strong opinions either way. (Though I have to
admit, 2-space indentation is a bit extreme... I can work with it,
though. I used to write Perl with 3-space indentation, and I *have*
seen code with 1-space indentation before. So it's not that big of a
deal.)
[...]
> I think we should also expose a simpler API to build the scanner
> from a File, *in addition* to the range based one? Just as a
> convenience wrapper. WDYT?
Certainly! It would make the example look a lot less scary. :-D See
updated calc.y (attached).
T
--
There are two ways to write error-free programs; only the third one works.
%language "D"
%define api.parser.class {Calc}
%define parse.error verbose
%code imports {
import std.ascii;
import std.stdio;
}
%union {
int ival;
}
/* Bison Declarations */
%token EQ "="
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
LPAR "("
RPAR ")"
EOL "end of line"
%token <ival> NUM "number"
%type <ival> exp
%left "-" "+"
%left "*" "/"
%precedence UNARY /* unary operators */
/* Grammar follows */
%%
input:
line
| input line
;
line:
EOL
| exp EOL { writeln ($exp); }
| error EOL
;
exp:
NUM { $$ = $1; }
| exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp { $$ = $1 / $3; }
| "+" exp %prec UNARY { $$ = -$2; }
| "-" exp %prec UNARY { $$ = -$2; }
| "(" exp ")" { $$ = $2; }
;
%%
import std.range.primitives;
import std.stdio;
auto calcLexer(R)(R range)
if (isInputRange!R && is(ElementType!R : dchar))
{
return new CalcLexer!R(range);
}
auto calcLexer(File f)
{
import std.algorithm : map, joiner;
import std.utf : byDchar;
return f.byChunk(1024) // avoid making a syscall roundtrip per char
.map!(chunk => cast(char[]) chunk) // because byChunk returns ubyte[]
.joiner // combine chunks into a single virtual range
of char
.calcLexer; // forward to other overload
}
class CalcLexer(R) : Lexer
if (isInputRange!R && is(ElementType!R : dchar))
{
R input;
this(R r) { input = r; }
// Should be a local in main, shared with %parse-param.
int exit_status = 0;
public void yyerror (string s)
{
exit_status = 1;
stderr.writeln (s);
}
YYSemanticType semanticVal_;
public final @property YYSemanticType semanticVal()
{
return semanticVal_;
}
YYTokenType yylex()
{
import std.uni : isWhite, isNumber;
// Skip initial spaces
while (!input.empty && input.front != '\n' && isWhite (input.front))
{
input.popFront;
}
// Handle EOF.
if (input.empty)
return YYTokenType.EOF;
// Numbers.
if (input.front == '.' || input.front.isNumber)
{
import std.conv : parse;
semanticVal_.ival = input.parse!int;
return YYTokenType.NUM;
}
// Individual characters
auto ch = input.front;
input.popFront;
switch (ch)
{
case EOF: return YYTokenType.EOF;
case '=': return YYTokenType.EQ;
case '+': return YYTokenType.PLUS;
case '-': return YYTokenType.MINUS;
case '*': return YYTokenType.STAR;
case '/': return YYTokenType.SLASH;
case '(': return YYTokenType.LPAR;
case ')': return YYTokenType.RPAR;
case '\n': return YYTokenType.EOL;
default: assert(0);
}
}
}
int main()
{
auto l = calcLexer(stdin);
Calc p = new Calc (l);
p.parse ();
return l.exit_status;
}