回 6-2 道 6 言語と処理系
構文解析は、
読み終えたら1 + 2 * 3 を
この頁の流れ
字を
1 + 2 * 3 が 7 に なると、 どうして 分かるのか
式を
コンピュータに1、空白、+ と
構文 (こうぶん
道 6 では、
字句解析と 構文解析は、 それぞれ何をするのか
字句解析は
| 段 | 受け取る物 | 渡す物 | ここで |
|---|---|---|---|
| 字句解析 | 字の列 | 字句 (トークン) の列 | 使えない字 |
| 構文解析 | 字句の列 | 構文木 | 閉じない |
| 意味解析 | 構文木 | 調べ終えた木 | 型の |
意味解析は、
字句解析は 字を 字句に切る
字句解析は、
ちび電卓では、
| 種類 | 値 | 例 |
|---|---|---|
| num | 整数 | ("num", 12) |
| name | 英小文字の並び | ("name", "x") |
| op | 1 字の記号 | ("op", "+") |
| end | 値なし | ("end", None) |
end は
字句解析では、12 を\d+ は
文法は BNF で書く
構文解析の
電卓の文法 (BNF を広げた書き方)
式 ::= 項 (("+"|"-") 項)*
項 ::= 因子 (("*"|"/") 因子)*
因子 ::= 数 | "(" 式 ")"| は( )* は「0 回以上くり返す」です。
式や+ のような
この
因子の
構文木と 抽象構文木
構文解析の
| 木 | 残す物 | (1 + 2) * 3 の括弧 |
|---|---|---|
| 具体構文木 | 規則の | 字句として残る |
| 抽象構文木 (AST) | 演算と、その左右 | 残らない。 |
ちび電卓の
ちび電卓の
作り方は トップダウンと ボトムアップ
構文解析器 (パーサー) の
| 作り方 | 進め方 | 代表 |
|---|---|---|
| トップダウン | 根 (式) から | 再帰下降、 |
| ボトムアップ | 字句を | LR 法、 |
この
本物の
動かして確かめる
1 本目は
動かす 字句解析 1 + 2 * 3 を字句に切る
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
('num', 1)
('op', '+')
('num', 2)
('op', '*')
('num', 3)
('end', None)
num op op name op num op end空白は12*(x-3) のように
2 本目は、calc は、//) です。
動かす 構文解析して木をたどる
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
('+', 1, ('*', 2, 3))
= 7
('*', ('+', 1, 2), 3)
= 9
('/', 7, 2)
= 31 + 2 * 3 は ("+", 1, ("*", 2, 3)) に
expr が term を、term が factor をfactor はname の
calc も、
どこで 崩れるのか
構文解析は、
結合の 向きを 取り違えると 5 が 9 になる
10 - 3 - 2 は、**) と
ところが文法を 式 ::= 数 ("-" 式)? のように、lex と
動かす 左から組むか、右から組むか
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
left 5
('-', ('-', 10, 3), 2)
right 9
('-', 10, ('-', 3, 2))
Python 5ループで左から組んだ left はright は
2 本目の expr が while の
文法に 合わない 並びは、 実行の 前に止める
構文解析器のもう 1 つの役目は、
動かす 構文エラーの出し分け
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
(1 + 2 | ) が無い
1 + | 式が途中で終わる
2 * * 3 | 数が要る所に *
1 2 | 余計な字句 2
1 + 2) | 余計な字句 )どの1 2 と 1 + 2) は、
Python も
文法 その ものが 曖昧な こともある
文法の
ちび電卓の
構文解析は 仕事で どう 確かめるのか
仕事では、ast モジュールが
動かす Python の構文解析器の木を見る
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
Expression(
body=BinOp(
left=Constant(value=1),
op=Add(),
right=BinOp(
left=Constant(value=2),
op=Mult(),
right=Constant(value=3))))
(1 + 2 | '(' was never closed
1 + | invalid syntaxPython の木でも、Add (足し算) のMult (掛け算) の
確かめ方は
- 優先順位 (1 + 2 * 3) と、
括弧 ((1 + 2) * 3) と、 結合の 向き (10 - 3 - 2) を、 答えの 分かっている式で試す - 閉じない
括弧、 途中で 終わる 式、 余る 字句を 渡し、 エラーに なる ことと、 その文を試す - 本物の
処理系 (Python の astやeval) と同じ式で 答えを照らす
試験の例 (pytest の形)
def test_order():
assert calc(parse(
"1 + 2 * 3")) == 7
assert calc(parse(
"10 - 3 - 2")) == 5
def test_unclosed():
with pytest.raises(
SyntaxError):
parse("(1 + 2")設定ファイル、ast の
この
関わる実例
同じ字の 並びが、 版に よって 別の 木に なった Python の print (PEP 3105)
Python 3.0 で、print が、ふつうの関数 print() に
PEP 3105 は、print ("Hello", "world") という('Hello', 'world') とHello world と
崩れた
出典は PEP 3105 (python.org) です。
改行で ; が 入り、 return の 値が 消える JavaScript (ECMAScript の仕様)
JavaScript の; が; を
仕様の例では、return のa + b を書くと、return; と a + b; の 2 つの文にa + b はreturn と
崩れた
出典は ECMAScript Language Specification の
よくある問い
正規表現だけで 構文解析は できますか
括弧の
パーサーは 手で 書くのと、 生成器で 作るのと、 どちらが 良いのですか
どちらも
日本語の 文の 構文解析も 同じしくみですか
木を
確認クイズ
答えだと
1 問目
ちび電卓の
2 問目
この
木をたどって計算する
def calc(t):
if isinstance(t, int):
return t
op, a, b = t
if op == "-":
return calc(a) - calc(b)
return calc(a) * calc(b)
t = ("-", 10, ("*", 2, 3))
print(calc(t))3 問目
10 - 3 - 2 を、
4 問目
閉じない括弧 (1 + 2 を
説明課題
1 + 2 * 3 が
自分の
この回の 先の一歩
設定ファイル、
一次資料
どれも 2026年10月27日に開いて、
- PEP 3105 Make print a function (python.org、英語)。作られた
日、 Final、 対象の版、 print ("Hello", "world")の2.x と 3.0 の 出力の違い - ECMAScript Language Specification, 12.10 Automatic Semicolon Insertion (tc39.es、英語)。; を補う 3 つの規則、
returnとa + bの例 - PEP 617 New PEG parser for CPython (python.org、英語)。3.9 で
LL(1) から PEG に 替えたこと - Expressions, Operator precedence (Python 公式文書、英語)。同じ段は
左から、 累乗と 条件の 式だけ右から まとめること - ast, Abstract syntax trees (Python 公式文書、英語)。抽象構文 (木の
形) が 版ごとに 変わり得ること
次へ
道 6 言語と処理系の回
- 6-1正規表現と
オートマトン後戻りが増える式頁で読む - 6-2字句解析と構文解析電卓の
式を木にするこの回 - 6-3小さな
インタプリタ変数と if を持つ言語頁で読む - 6-4型と型検査型が
間違いを 先に止める頁で読む
