学习编程时一个很好的练习就是编写计算器。为此,我在 DSL /en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form" rel="nofollow">BNF 并希望寻求您的帮助来改进它。使用这种迷你语言,您应该能够为名称添加、乘法和分配值和表达式(也称为创建变量和函数)。
首先看一下 BNF:
<Program> ::= <Line>(<NewLine><Line>)*
<Line> ::= {"("}<Expression>{")"}|<Assignment>
<Assignment> ::= <Identifier>"="<Expression>
<Identifier> ::= <Name>{"("<Name>(","<Name>)*")"}
<Expression> ::= <Summand>(("+"|"-")<Summand>)*
<Summand> ::= <Factor>(("*"|"/")<Factor>)*
<Factor> ::= <Number>|<Call>
<Call> ::= <Name> {"("<Expression>(","<Expression>)*")"}
<Name> ::= <Letter>(<Letter>|<Digit>)*
<Number> ::= {"+"|"-"}(<Digit>|<DigitNoZero><Digit>+)
<Digit> ::= "0"|<DigitNoZero>
<DigitNoZero> ::= "1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
<Letter> ::= [a-zA-Z]
<NewLine> ::= "\n"|"\r"|"\r\n"
如您所见,该 BNF 不处理 NewLine
旁边的空白。在解析开始之前,我计划从要解析的字符串中删除所有空格(当然除了 NewLine
之外)。无论如何,这对于解析器来说并不是必需的。
使用现在定义的这种语言时,有 4 件事可能会导致问题,我希望您能帮助我找出适当的解决方案:
- 我尝试遵循自上而下的方法,同时生成此语法,但
、
、
和 之间有一个圆圈;
。
- 语法对待变量和函数的方式完全相同。大多数编程语言都会有所作为。 这里有必要区分吗?
- 也许有一些我不知道的关于编程、BNF 的事情,无论如何,这会在以后尝试实现 BNF 时杀死我。但在我开始之前你也许能发现这一点。
- 可能有些简单而愚蠢的错误是我自己找不到的。抱歉,在这种情况下。我希望不再有这些错误。
用手和大脑我可以成功解析以下测试用例:
"3"
"-3"
"3-3"
"a=3"
"a=3+b"
"a=3+b\nc=a+3"
"a(b,c)=b*c\ra(1+2,2*3)"
请帮助改进 BNF,它可以用来成功编写计算器。
编辑:
这个BNF还真没完。它没有正确处理“2+-3”(应该失败,但没有失败)和“2+(-3)”(不应该失败,但确实失败)的情况。
A good exercise while learning programming, is to write a calculator. To do this, I created some kind of DSL in BNF and want to ask for your help to improve it. With this minilanguage you should be able to add
, multiply
and assign
values and expressions to names (a.k.a. create variables and functions).
Hava a look at the BNF first:
<Program> ::= <Line>(<NewLine><Line>)*
<Line> ::= {"("}<Expression>{")"}|<Assignment>
<Assignment> ::= <Identifier>"="<Expression>
<Identifier> ::= <Name>{"("<Name>(","<Name>)*")"}
<Expression> ::= <Summand>(("+"|"-")<Summand>)*
<Summand> ::= <Factor>(("*"|"/")<Factor>)*
<Factor> ::= <Number>|<Call>
<Call> ::= <Name> {"("<Expression>(","<Expression>)*")"}
<Name> ::= <Letter>(<Letter>|<Digit>)*
<Number> ::= {"+"|"-"}(<Digit>|<DigitNoZero><Digit>+)
<Digit> ::= "0"|<DigitNoZero>
<DigitNoZero> ::= "1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
<Letter> ::= [a-zA-Z]
<NewLine> ::= "\n"|"\r"|"\r\n"
As you can see, this BNF treats no Whitespace beside NewLine
. Before the parsing begins I plan to remove all whitespace (beside NewLine
of course) from the string to parse. It is not nessesary for the parser anyway.
There are 4 things, that might lead to problems when using this language as defined right now and I hope you can help me figure out appropriate solutions:
- I tried to follow the top-down approach, while generating this gramar, but there is a circle between
<Expression>
, <Summand>
, <Factor>
and <Call>
.
- The gramar treats variables and functions exactly the same way. Most programming languages make a difference. Is it nessesary to differentiate here?
- There are maybe some things that I don't know about programming, BNF, whatever, that will kill me later, while trying to implement the BNF. But you might be able to spot that before I start.
- There might be simple and stupid mistakes that I could not find myself. Sorry in that case. I hope there are none of these mistakes anymore.
Using hand and brain I could successfully parse the following test cases:
"3"
"-3"
"3-3"
"a=3"
"a=3+b"
"a=3+b\nc=a+3"
"a(b,c)=b*c\ra(1+2,2*3)"
Please help to improve the BNF, that it can be used to successfully write a calculator.
edit:
This BNF is really not finished. It does not treat the cases "2+-3" (should fail, but doesn't) and "2+(-3)" (should not fail, but does) correctly.
发布评论
评论(1)
能够将函数调用的结果与局部变量或常量表达式完全相同地对待,这正是定义(数学)函数的首要目的。我无法想象使用允许函数但不
完全相同的语法
与or
Being able to treat the result of a function invocation exactly the same as a local variable or a constant expression is precisely the point of defining (mathematical) functions in the first place. I can't imagine the use of a grammar that allowed functions but didn't treat
exactly the same as
or