本章节对应库房

3.表达式解析 Github

巴科斯范式(BNF)

在本章之前,需求先科普一个常识点:BNF

假如现已有此类常识的读者能够越过该段内容。

BNF

什么是BNF

BNF是一种形式化的语法表明方法,其有几个特点:

  • 所描绘的语法是上下文无关的
  • 在双引号中的字("word")代表着这些字符自身。而double_quote用来代表双引号。
  • 在双引号外的字(有可能有下划线)代表着语法部分。
  • 尖括号( < > )内包括的为必选项。
  • 方括号( [ ] )内包括的为可选项。
  • 大括号( { } )内包括的为可重复0至无数次的项。
  • 竖线( | )表明在其左右两头任选一项,相当于”OR”的意思。
  • ::= 是“被界说为”的意思
  • 终结符:语言中的根本元素,无法再分解
  • 非终结符:能够再分解的元素

语法界说

描绘简略的数字相加时,语法规则为:

add ::= <number> <"+"> <number>

这种表达式能够解析 1 + 2,解析后的结果为1 "+" 2

但这显然是不够用的,假如需求连续的数字相加时,如 1 + 2 + 3 时,上面的表达式便有了局限性。

因而需求对其进行改造,不过这并不难,BNF支撑递归语法。

改造后的加法:

add ::= <add> <"+"> <number> | <number>

在解析 1 + 2 + 3 时,会解析为:(1 "+" 2) "+" 3

此时,简略的加法现已没有了难度。所以能够对其进行进一步改造,使其支撑减法与括号

  • 减法支撑
expr0 ::= <expr0> <"+"|"-"> <number> | <number>

因为减法与加法的优先级相同,能够把加减法合并到一个表达式当中。

  • 括号支撑
    相较减法,支撑括号显然在难度上更大一些。因为括号会改动运算优先级,因而咱们把带括号的表达式单独界说为一个非终结符。
primary ::= <"("> <expr0> <")"> | <number>
expr0   ::= <expr0> <"+"|"-"> <primary> | <primary>

如解析1 + (3 – 2 + 5)时,会依照如下方式进行解析:

expr0--primary--1
        |
       "+"
        |
       primary--"("
                 |
                expr0--primary--3
                 |        |
                ")"      "-"
                          |
                         expr0--primary--2
                                   |
                                  "+"
                                   |
                                primary--5
  • 右结合的操作符
    上面罗列的加法是左结合的运算符,所谓左结合是指解析时依照从左往右进行结合,如1 + 2 + 3,假如+号是左结合,那么最终得到的表达式是(1 + 2) + 3

而假如是右结合运算符,例如使用符号表明乘方时,2 ^ 3 为2的3次方,2 ^ 3 ^ 2 为 2的(3的2次方)次方(即2的9次方),解分出来的表达式则是2 ^ (3 ^ 2)

要表达右结合运算符时,便要对语法界说进行一定的变换。上述例子中,左结合的加法界说为:

primary ::= <"("> <expr0> <")"> | <number>
expr0   ::= <expr0> <"+"> <primary> | <primary>

则右结合的乘方界说为

primary ::= <"("> <expr0> <")"> | <number>
expr0   ::= <primary> <"^"> <expr0> | <primary>

SwordScript的算术表达式界说

在开端写语法解析代码前,咱们需求先写出算术表达式的界说。

依照优先级,能够将操作符的优先级表格列出:

操作符 称号 优先级 类型 结合性
. 成员获取 1 成员获取 左结合
[ ] 数组取值 1 访问器调用 左结合
( ) 函数调用 1 函数调用 左结合
not 取否 2 单目运算符 右结合
取负 2 单目运算符 右结合
乘方 3 双目运算符 右结合
* 4 双目运算符 左结合
/ 4 双目运算符 左结合
% 取余 4 双目运算符 左结合
+ 5 双目运算符 左结合
5 双目运算符 左结合
大于 6 双目运算符 左结合
>= 大于等于 6 双目运算符 左结合
< 小于 6 双目运算符 左结合
<= 小于等于 6 双目运算符 左结合
== 等于 7 双目运算符 左结合
!= 不等于 7 双目运算符 左结合
and 逻辑与 8 双目运算符 左结合
or 逻辑或 9 双目运算符 左结合

在本章中,暂不考虑成员、数组与函数调用

将以上操作符化作BNF如下:

primary ::= <"("> <expr> <")"> | <literal|identifier>
expr2 ::= <"not"|"-"> <primary> | <primary>
expr3 ::= <expr2> <"^"> <expr3> | <expr2>
expr4 ::= <expr4> <"*"|"/"|"%"> <expr3> | <expr3>
expr5 ::= <expr5> <"+"|"-"> <expr4> | <expr4>
expr6 ::= <expr6> <">"|">="|"<"|"<="> <expr5> | <expr5>
expr7 ::= <expr7> <"=="|"!="> <expr6> | <expr6>
expr8 ::= <expr8> <"and"> <expr7> | <expr7>
expr9 ::= <expr9> <"or"> <expr8> | <expr8>
expr  ::= <expr9>

将BNF转换为解析器

有了以上界说后,便能够开端写解析器了。

新建一个类,命名为ScriptParser

ScriptParser.cs

using Sprache;
namespace SwordScript;
public static class ScriptParser
{
}

在界说表达式之前,咱们需求界说终结符。

在上面的BNF中,终结符有各种符号以及literalidentifier

literal是字面量,指写在代码里的数值。因而能够得到界说:

public static readonly Parser<ASTLiteral> Literal =
    (from nullValue in Lexer.Null select new ASTNullLiteral())
    .Or<ASTLiteral>(from booleanValue in Lexer.Boolean select new ASTBooleanLiteral(booleanValue))
    .Or(from doubleValue in Lexer.DoubleFloat select new ASTDoubleFloatLiteral(doubleValue))
    .Or(from longValue in Lexer.LongInteger select new ASTLongIntegerLiteral(longValue))
    .Or(from stringValue in Lexer.String select new ASTStringLiteral(stringValue));

同理可得identifier

public static readonly Parser<ASTIdentifier> Identifier =
    from id in Lexer.Identifier select new ASTIdentifier(id);

primary

而在界说primary前,咱们需求先界说一个指代表达式的非终结符expr

public static Parser<ASTNode> Expr;

expr的界说会在悉数表达式界说结束后界说。

primary的界说:

public static readonly Parser<ASTNode> Primary = (
        from left in Parse.Char('(').SuperToken()
        from expr in Parse.Ref(() => Expr)
        from right in Parse.Char(')').SuperToken()
        select expr)
    .Or(Literal)
    .Or(Identifier)
    .SuperToken();

单元测验

Tests工程新建类ExprTest

namespace Tests;
public class ExprTest
{
}

增加如下内容:

[Test]
public void PrimaryTest()
{
    Assert.AreEqual("123", ScriptParser.Primary.Parse(" 123 ").ToString());
    Assert.AreEqual("0.5", ScriptParser.Primary.Parse(" 0.5 ").ToString());
    Assert.AreEqual("abc", ScriptParser.Primary.Parse(" abc ").ToString());
    Assert.AreEqual("true", ScriptParser.Primary.Parse(" true ").ToString());
    Assert.AreEqual("null", ScriptParser.Primary.Parse(" null ").ToString());
}

运转单元测验,悉数经过

expr2 – 取非、取反

在界说expr2之前,首先需求界说对应的语法树节点。

新建类ASTUnaryExpr

namespace SwordScript;
public abstract class ASTUnaryExpr : ASTList
{
    public ASTUnaryExpr(ASTNode expr) : base(new ASTNode[] { expr })
    {
    }
    public ASTNode Expr => this[0];
}

该类将作为一切一元表达式的基类

新建类ASTUnaryExprNegative表明取负

public class ASTUnaryExprNegative : ASTUnaryExpr
{
    public ASTUnaryExprNegative(ASTNode expr) : base(expr)
    {
    }
    public override string ToString()
    {
        return $"-{Expr}";
    }
}

新建类ASTUnaryExprNot表明取反

public class ASTUnaryExprNot : ASTUnaryExpr
{
    public ASTUnaryExprNot(ASTNode expr) : base(expr)
    {
    }
    public override string ToString()
    {
        return $"not {Expr}";
    }
}

随后在Lexer类中界说如下静态方法

/// <summary>
/// 回来下个字符不为Unicode字符的字符符号解析器
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
public static Parser<string> LetterSymbol(string symbol)
{
    return (from symbolStr in Parse.String(symbol).Text()
        from nextChar in Parse.Regex(@"[0-9_\p{L}]").Preview()
        where nextChar.IsEmpty
        select symbolStr).SuperToken();
}
/// <summary>
/// 回来普通的标点符号解析器
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
public static Parser<string> PunctuationSymbol(string symbol)
{
    return (from symbolStr in Parse.String(symbol).Text() select symbolStr).SuperToken();
}

LetterSymbol用于解析如 and or not 这类由字母组成的符号,防止与标识符黏连在一起时也被识别

PunctuationSymbol用于解析普通的标点符号

增加负号与取非的词法解析:

public static readonly Parser<string> Negate = PunctuationSymbol("-");
public static readonly Parser<string> Not = LetterSymbol("not");

ScriptParser类中界说expr2

public static readonly Parser<ASTNode> Expr2 =(
        from symbol in Lexer.Negate.Or(Lexer.Not)
        from expr in Primary
        select (ASTNode)(symbol == "-" ? new ASTUnaryExprNegative(expr) : new ASTUnaryExprNot(expr)))
    .Or(Primary)
    .SuperToken();

单元测验

增加单元测验:

[Test]
public void Expr2Test()
{
    Assert.AreEqual("123", ScriptParser.Expr2.Parse(" 123 ").ToString());
    Assert.AreEqual("-123", ScriptParser.Expr2.Parse(" -123 ").ToString());
    Assert.AreEqual("not false", ScriptParser.Expr2.Parse(" not false ").ToString());
    Assert.AreEqual("nottrue", ScriptParser.Expr2.Parse(" nottrue ").ToString());
}

运转测验,悉数经过

expr3 – 乘方

先界说乘方对应类:

ASTBinaryExprPower

public class ASTBinaryExprPower : ASTBinaryExpr
{
    public ASTBinaryExprPower(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} ^ {Right})";
    }
}

界说Expr3

public static readonly Parser<ASTNode> Expr3 =(
        from left in Expr2
        from symbol in Lexer.Power
        from right in Expr3
        select new ASTBinaryExprPower(left, right))
    .Or(Expr2)
    .SuperToken();

单元测验

[Test]
public void Expr3Test()
{
    Assert.AreEqual("(1 ^ 4)", ScriptParser.Expr3.Parse(" 1 ^ 4 ").ToString());
    Assert.AreEqual("(5 ^ 0.7)", ScriptParser.Expr3.Parse(" 5 ^ .7 ").ToString());
    Assert.AreEqual("(0.1 ^ 0.1)", ScriptParser.Expr3.Parse(" 0.1 ^ .1 ").ToString());
    Assert.AreEqual("(2 ^ (2 ^ 2))", ScriptParser.Expr3.Parse(" 2 ^ 2 ^ 2 ").ToString());
}

因为目前Expr还没有界说,所以暂时无法测验嵌套公式

运转测验,悉数经过

expr4 – 乘、除、取余

与前面的表达式不同,从expr4开端,在文法上是无法直接输入解析器的。

左递归

先来看看expr4的文法:

expr4 ::= <expr4> <"*"|"/"|"%"> <expr3> | <expr3>

能够看到,expr4构成了一个左递归,有左递归的文法,在解析时可能会陷入无限左打开,如:

A ::= Aa | a
A = Aa
A = Aaa
A = Aaaa
A = Aaaaa.......

因而,在将BNF转换为语法解析前,需求先进行左递归消除。

消除左递归

首先需求剖析,左递归的句子能够怎么拆分。
网上常用的拆分方法如下:

expr4 ::= <expr3> <expr4'>
expr4' ::= <"*"|"/"|"%"> <expr3> <expr4'> | 

注:代表空语法

而从方便语法解析的角度剖析,其实也能够将expr4'视为一个重复N次的语法项

所以有了以下拆解:

expr4 ::= <expr3> {<"*"|"/"|"%"> <expr3>}

因为咱们后边的表达式简直都是左递归的文法,因而能够将左递归的表达式进行封装,封装后如下:

public delegate ASTNode CreateNode(ASTNode left,string op,ASTNode right);
public static Parser<ASTNode> LeftOperator(Parser<ASTNode> leftExpr, Parser<string> symbol,
    CreateNode apply)
{
    ASTNode CreateNode(ASTNode left, IEnumerable<Tuple<string,ASTNode>> rights)
    {
        foreach(var right in rights)
        {
            left = apply(left, right.Item1, right.Item2);
        }
        return left;
    }
    Parser<Tuple<string,ASTNode>> innerOperatorExpr = (
        from getSymbol in symbol
        from right in leftExpr
        select new Tuple<string, ASTNode>(getSymbol, right)
    ).SuperToken();
    Parser<ASTNode> operatorExpr = (
            from left in leftExpr
            from rights in innerOperatorExpr.Many()
            select CreateNode(left, rights)
        ).SuperToken();
    return operatorExpr;
}

之后的表达式只需求输入子项、符号与结构语法树的委托,即可直接结构并拆分左递归表达式文法。

Expr4界说

界说对应类:

public class ASTBinaryExprMultiply : ASTBinaryExpr
{
    public ASTBinaryExprMultiply(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} * {Right})";
    }
}
public class ASTBinaryExprDivide : ASTBinaryExpr
{
    public ASTBinaryExprDivide(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} / {Right})";
    }
}
public class ASTBinaryExprModulo : ASTBinaryExpr
{
    public ASTBinaryExprModulo(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} % {Right})";
    }
}

Lexer类中界说操作符

public static readonly Parser<string> Multiply = PunctuationSymbol("*");
public static readonly Parser<string> Divide = PunctuationSymbol("/");
public static readonly Parser<string> Modulo = PunctuationSymbol("%");

界说expr4

public static readonly Parser<ASTNode> Expr4 = LeftOperator(Expr3, Lexer.Multiply.Or(Lexer.Divide).Or(Lexer.Modulo),
    (left, op, right) =>
    {
        switch (op)
        {
            case "*":
                return new ASTBinaryExprMultiply(left, right);
            case "/":
                return new ASTBinaryExprDivide(left, right);
            case "%":
                return new ASTBinaryExprModulo(left, right);
            default:
                throw new ArgumentException($"Unknown operator '{op}'");
        }
    });

单元测验

[Test]
public void Expr4Test()
{
    Assert.AreEqual("(1 * 2)", ScriptParser.Expr4.Parse(" 1 * 2 ").ToString());
    Assert.AreEqual("(1 / 2)", ScriptParser.Expr4.Parse(" 1 / 2 ").ToString());
    Assert.AreEqual("(1 % 2)", ScriptParser.Expr4.Parse(" 1 % 2 ").ToString());
    Assert.AreEqual("((((1 * 2) / 3) % 4) * 5)", ScriptParser.Expr4.Parse(" 1 * 2 / 3 % 4 * 5 ").ToString());
}

运转测验,悉数经过

Expr5 – 加、减

界说对应类:

public class ASTBinaryExprPlus : ASTBinaryExpr
{
    public ASTBinaryExprPlus(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} + {Right})";
    }
}
public class ASTBinaryExprMinus : ASTBinaryExpr
{
    public ASTBinaryExprMinus(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} - {Right})";
    }
}

界说操作符:

public static readonly Parser<string> Plus = PunctuationSymbol("+");
public static readonly Parser<string> Minus = PunctuationSymbol("-");

界说expr5

public static readonly Parser<ASTNode> Expr5 = LeftOperator(Expr4, Lexer.Plus.Or(Lexer.Minus),
    (left, op, right) =>
    {
        switch (op)
        {
            case "+":
                return new ASTBinaryExprPlus(left, right);
            case "-":
                return new ASTBinaryExprMinus(left, right);
            default:
                throw new ArgumentException($"Unknown operator '{op}'");
        }
    });

单元测验

[Test]
public void Expr5Test()
{
    Assert.AreEqual("(1 + 2)", ScriptParser.Expr5.Parse(" 1 + 2 ").ToString());
    Assert.AreEqual("(1 - 2)", ScriptParser.Expr5.Parse(" 1 - 2 ").ToString());
    Assert.AreEqual("(((1 + 2) - 3) + 4)", ScriptParser.Expr5.Parse(" 1 + 2 - 3 + 4 ").ToString());
}

运转单元测验,悉数经过

Expr6 大于、大于等于、小于、小于等于

界说对应类:

public class ASTBinaryExprGreaterThan : ASTBinaryExpr
{
    public ASTBinaryExprGreaterThan(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} > {Right})";
    }
}
public class ASTBinaryExprGreaterThanOrEqual : ASTBinaryExpr
{
    public ASTBinaryExprGreaterThanOrEqual(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} >= {Right})";
    }
}
public class ASTBinaryExprLessThan : ASTBinaryExpr
{
    public ASTBinaryExprLessThan(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} < {Right})";
    }
}
public class ASTBinaryExprLessThanOrEqual : ASTBinaryExpr
{
    public ASTBinaryExprLessThanOrEqual(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} <= {Right})";
    }
}

界说操作符

public static readonly Parser<string> GreaterThan = PunctuationSymbol(">");
public static readonly Parser<string> GreaterThanOrEqual = PunctuationSymbol(">=");
public static readonly Parser<string> LessThan = PunctuationSymbol("<");
public static readonly Parser<string> LessThanOrEqual = PunctuationSymbol("<=");

界说Expr6

public static readonly Parser<ASTNode> Expr6 = LeftOperator(Expr5,
    Lexer.GreaterThanOrEqual.Or(Lexer.GreaterThan)
    .Or(Lexer.LessThanOrEqual).Or(Lexer.LessThan),
    (left, op, right) =>
    {
        switch (op)
        {
            case ">":
                return new ASTBinaryExprGreaterThan(left, right);
            case ">=":
                return new ASTBinaryExprGreaterThanOrEqual(left, right);
            case "<":
                return new ASTBinaryExprLessThan(left, right);
            case "<=":
                return new ASTBinaryExprLessThanOrEqual(left, right);
            default:
                throw new ArgumentException($"Unknown operator '{op}'");
        }
    });

需求注意的是,大于等于需求放在大于前,小于等于需求放在小于前,否则会因为先匹配了大于或小于符号,导致大于等于和小于等于无法匹配。

单元测验

[Test]
public void Expr6Test()
{
    Assert.AreEqual("(1 < 2)", ScriptParser.Expr6.Parse(" 1 < 2 ").ToString());
    Assert.AreEqual("(1 > 2)", ScriptParser.Expr6.Parse(" 1 > 2 ").ToString());
    Assert.AreEqual("(1 <= 2)", ScriptParser.Expr6.Parse(" 1 <= 2 ").ToString());
    Assert.AreEqual("(1 >= 2)", ScriptParser.Expr6.Parse(" 1 >= 2 ").ToString());
    Assert.AreEqual("(((1 < 2) < 3) < 4)", ScriptParser.Expr6.Parse(" 1 < 2 < 3 < 4 ").ToString());
}

运转测验,悉数经过

Expr7 等于、不等于

界说对应类:

public class ASTBinaryExprEqual : ASTBinaryExpr
{
    public ASTBinaryExprEqual(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} == {Right})";
    }
}
public class ASTBinaryExprNotEqual : ASTBinaryExpr
{
    public ASTBinaryExprNotEqual(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} != {Right})";
    }
}

界说操作符:

public static readonly Parser<string> Equal = PunctuationSymbol("==");
public static readonly Parser<string> NotEqual = PunctuationSymbol("!=");

界说expr7

public static readonly Parser<ASTNode> Expr7 = LeftOperator(Expr6, Lexer.Equal.Or(Lexer.NotEqual),
    (left, op, right) =>
    {
        switch (op)
        {
            case "==":
                return new ASTBinaryExprEqual(left, right);
            case "!=":
                return new ASTBinaryExprNotEqual(left, right);
            default:
                throw new ArgumentException($"Unknown operator '{op}'");
        }
    });

单元测验

[Test]
public void Expr7Test()
{
    Assert.AreEqual("(1 == 2)", ScriptParser.Expr7.Parse(" 1 == 2 ").ToString());
    Assert.AreEqual("(1 != 2)", ScriptParser.Expr7.Parse(" 1 != 2 ").ToString());
    Assert.AreEqual("(((1 == 2) == 3) == 4)", ScriptParser.Expr7.Parse(" 1 == 2 == 3 == 4 ").ToString());
}

运转单元测验,悉数经过

Expr8 逻辑与

界说对应类:

public class ASTBinaryExprAnd : ASTBinaryExpr
{
    public ASTBinaryExprAnd(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} and {Right})";
    }
}

界说操作符:

public static readonly Parser<string> And = LetterSymbol("and");

界说expr8

public static readonly Parser<ASTNode> Expr8 = LeftOperator(Expr7, Lexer.And,
    (left, op, right) => new ASTBinaryExprAnd(left, right));

单元测验

[Test]
public void Expr8Test()
{
    Assert.AreEqual("(a and b)", ScriptParser.Expr8.Parse(" a and b ").ToString());
    Assert.AreEqual("((a and b) and c)", ScriptParser.Expr8.Parse(" a and b and c ").ToString());
}

运转单元测验,悉数经过

Expr9 逻辑或

界说对应类:

public class ASTBinaryExprOr : ASTBinaryExpr
{
    public ASTBinaryExprOr(ASTNode left, ASTNode right) : base(left, right)
    {
    }
    public override string ToString()
    {
        return $"({Left} or {Right})";
    }
}

界说操作符:

public static readonly Parser<string> Or = LetterSymbol("or");

界说Expr9

public static readonly Parser<ASTNode> Expr9 = LeftOperator(Expr8, Lexer.Or,
    (left, op, right) => new ASTBinaryExprOr(left, right));

单元测验

[Test]
public void Expr9Test()
{
    Assert.AreEqual("(a or b)", ScriptParser.Expr9.Parse(" a or b ").ToString());
    Assert.AreEqual("((a or b) or c)", ScriptParser.Expr9.Parse(" a or b or c ").ToString());
}

运转单元测验,悉数经过

表达式闭环

在界说完一切表达式后,回到Expr上,将其弥补为如下界说:

public static readonly Parser<ASTNode> Expr = Parse.Ref(() => Expr9);

此时,表达式解析现已完成了一个闭环,一切的表达式相互嵌套应当没有了问题。

单元测验

[Test]
public void Expression()
{
    Assert.AreEqual("((1 + 2) * (3 + 4))", ScriptParser.Expr.Parse(" (1 + 2) * (3 + 4)  ").ToString());
    Assert.AreEqual("(a >= ((3 * (b ^ 5)) - 8))", ScriptParser.Expr.Parse(" a >= 3 * b ^ 5 - 8 ").ToString());
    Assert.AreEqual("((a and (b1 == b2)) or (((9 * 9) == c) and d))", ScriptParser.Expr.Parse(" a and b1 == b2 or 9*9 == c and d ").ToString());
    Assert.AreEqual("((a and (b1 == (b2 or ((9 * 9) == c)))) and d)", ScriptParser.Expr.Parse(" a and b1 == (b2 or 9*9 == c) and d ").ToString());
}

在测验中,各级表达式嵌套及优先级匹配都运转经过

结语

本章内容较多,主要触及了完整的算术表达式解析。在下章中,咱们将以表达式解分出的抽象语法树为基础,进行表达式的计算。