第016回_構文の整理3_PITarget_Contentspec
»
簡単な検討を要するもの1
今回は基底クラスで実装できない構文やLL (1)でない構文について検討します。■PITarget
PITargetは、EbnfLimitedWordNodeを使って正しく解釈できないので検討が必要になります。
Nameの構文は
[5] Name ::= NameStartChar (NameChar)*
ですから、
条件1: Name = "^[" + NameStartChar + "][" + NameChar + "]*$"
条件2: CASE_INSENSITIVE(^xml$)
として、
PITarget = WORD (条件1 && !条件2)
となります。
# CASE_INSENSITIVE(xml)は、「大文字と小文字を区別しないxmlの文字列に
# マッチする場合」を意味します。よって !CASE_INSENSITIVE(^xml$) は
# 「大文字と小文字を区別せずにxmlの文字列にマッチする場合」を意味します。
■contentspec以下の構文
ポイント1
まず呼び出し元となる、PIの構文が
PI::=SYMBOL ('<?') PITarget (S PI_WORD)? SYMBOL ('?>')
となっています。ここで、SYMBOL ('<?') PITargetの部分に注目します。
SYMBOLトークンは、
SYMBOL ::= '#FIXED' | '#IMPLIED'| '#PCDATA' | '#REQUIRED'
# もし、<?xmlの並びならSYMBOL ('<?xml')として取得し、
# そうでない場合はSYMBOL ('<?')として取得するので
# SYMBOL ('<?')とWORD ('xml')に分解して取得することはありません。
ポイント2
次にPITargetの構文は
[17] PITarget ::= Name − (('X' | 'x') ('M' | 'm') ('L' | 'l'))
ですので、
PITargetはNameかつ、
もし3文字の場合はxml,Xml,xMl,xmL,XMl,XmL,xML,XMLではいけない
ということを示しています。
総合すると、字句解析器によってPITargetに到達したWORDトークンはxmlを除外できますがXml,xMl,xmL,XMl,XmL,xML,XMLは除外できないため、これを構文NGにする機能がEbnfPITargetNodeに必要です。まず呼び出し元となる、PIの構文が
PI::=SYMBOL ('<?') PITarget (S PI_WORD)? SYMBOL ('?>')
となっています。ここで、SYMBOL ('<?') PITargetの部分に注目します。
SYMBOLトークンは、
SYMBOL ::= '#FIXED' | '#IMPLIED'| '#PCDATA' | '#REQUIRED'
| '%' | '&#' | '&#x' | '&' | '(' | ')' | ')*'
| '*' | '+' | ',' | '/>' | ';' | '='
| '<' | '</' | '<?' | '<?xml' | '<!['
| '<![CDATA[' | '<!ATTLIST' | '<!DOCTYPE' | '<!ENTITY'
| '<!ELEMENT' | '<!NOTATION'
| '>' | '?' | '?>' | '[' | ']' | ']]>' | '|'
となっており、太字部分の<?xmlと<?は異なるSYMBOLとして抽出する必要があります。方法は字句解析側で考えるとして、そのようなことが可能とすると<?に続くPITargetは xmlでない保証を持ちます。 | '*' | '+' | ',' | '/>' | ';' | '='
| '<' | '</' | '<?' | '<?xml' | '<!['
| '<![CDATA[' | '<!ATTLIST' | '<!DOCTYPE' | '<!ENTITY'
| '<!ELEMENT' | '<!NOTATION'
| '>' | '?' | '?>' | '[' | ']' | ']]>' | '|'
# もし、<?xmlの並びならSYMBOL ('<?xml')として取得し、
# そうでない場合はSYMBOL ('<?')として取得するので
# SYMBOL ('<?')とWORD ('xml')に分解して取得することはありません。
ポイント2
次にPITargetの構文は
[17] PITarget ::= Name − (('X' | 'x') ('M' | 'm') ('L' | 'l'))
ですので、
PITargetはNameかつ、
もし3文字の場合はxml,Xml,xMl,xmL,XMl,XmL,xML,XMLではいけない
ということを示しています。
Nameの構文は
[5] Name ::= NameStartChar (NameChar)*
ですから、
条件1: Name = "^[" + NameStartChar + "][" + NameChar + "]*$"
条件2: CASE_INSENSITIVE(^xml$)
として、
PITarget = WORD (条件1 && !条件2)
となります。
# CASE_INSENSITIVE(xml)は、「大文字と小文字を区別しないxmlの文字列に
# マッチする場合」を意味します。よって !CASE_INSENSITIVE(^xml$) は
# 「大文字と小文字を区別せずにxmlの文字列にマッチする場合」を意味します。
LL (1)で解析できないので、変形が必要です。
まずは自動的な変換を行うと次のようになります。
contentspec ::= WORD ('EMPTY') | WORD ('ANY') | Mixed | children
children ::= (choice | seq) ( SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+') )?
cp ::= (Name | choice | seq) ( SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
choice ::= SYMBOL ('(') S? cp ( S? SYMBOL ('|') S? cp )+ S? SYMBOL (')')
seq ::= SYMBOL ('(') S? cp ( S? SYMBOL (',') S? cp )* S? SYMBOL (')')
Mixed ::= ( SYMBOL ('(') S? SYMBOL ('#PCDATA')
(S? SYMBOL ('|') S? Name)* S? SYMBOL (')*')
)
| ( SYMBOL ('(') S? SYMBOL ('#PCDATA') S? SYMBOL (')') )
contentspecのLL (1)でない部分の確認
SYMBOL ::= '#FIXED'
まずは自動的な変換を行うと次のようになります。
contentspec ::= WORD ('EMPTY') | WORD ('ANY') | Mixed | children
children ::= (choice | seq) ( SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+') )?
cp ::= (Name | choice | seq) ( SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
choice ::= SYMBOL ('(') S? cp ( S? SYMBOL ('|') S? cp )+ S? SYMBOL (')')
seq ::= SYMBOL ('(') S? cp ( S? SYMBOL (',') S? cp )* S? SYMBOL (')')
Mixed ::= ( SYMBOL ('(') S? SYMBOL ('#PCDATA')
(S? SYMBOL ('|') S? Name)* S? SYMBOL (')*')
)
| ( SYMBOL ('(') S? SYMBOL ('#PCDATA') S? SYMBOL (')') )
contentspecのLL (1)でない部分の確認
contentspecのLL (1)でない部分について確認します。
例えば、
SYMBOL ('<!ELEMENT') S WORD (name) S contentspec SYMBOL ('>')
の具体値
<!ELEMENT data EMPTY >
<!ELEMENT data ANY >
<!ELEMENT data (#PCDATA) >
<!ELEMENT data (data0 ) >
のとき、
SYMBOL ('<!ELEMENT') S WORD ('data') S WORD ('EMPTY')
S SYMBOL ('>')
→contentspec EMPTYを適用
SYMBOL ('<!ELEMENT') S WORD ('data') S WORD ('ANY')
S SYMBOL ('>')
→contentspec ANYを適用
SYMBOL ('<!ELEMENT') S WORD ('data') S SYMBOL ('(')
SYMBOL ('#PCDATA') SYMBOL (')') S SYMBOL ('>')
→contentspecからMixedに至り、後半の構文を適用
SYMBOL ('<!ELEMENT') S WORD ('data') S SYMBOL ('(')
WORD (data0) SYMBOL (')') S SYMBOL ('>')
→contentspecからchildrenを通じてseqを適用
と決定する必要があります。
しかし、contentspecの最初のトークンで考えると、
となり、SYMBOL ('(')を取得した場合に構文が確定しないのでLL (1)ではありません。
contentspecのLL (1)化例えば、
SYMBOL ('<!ELEMENT') S WORD (name) S contentspec SYMBOL ('>')
の具体値
<!ELEMENT data EMPTY >
<!ELEMENT data ANY >
<!ELEMENT data (#PCDATA) >
<!ELEMENT data (data0 ) >
のとき、
SYMBOL ('<!ELEMENT') S WORD ('data') S WORD ('EMPTY')
S SYMBOL ('>')
→contentspec EMPTYを適用
SYMBOL ('<!ELEMENT') S WORD ('data') S WORD ('ANY')
S SYMBOL ('>')
→contentspec ANYを適用
SYMBOL ('<!ELEMENT') S WORD ('data') S SYMBOL ('(')
SYMBOL ('#PCDATA') SYMBOL (')') S SYMBOL ('>')
→contentspecからMixedに至り、後半の構文を適用
SYMBOL ('<!ELEMENT') S WORD ('data') S SYMBOL ('(')
WORD (data0) SYMBOL (')') S SYMBOL ('>')
→contentspecからchildrenを通じてseqを適用
と決定する必要があります。
しかし、contentspecの最初のトークンで考えると、
EMPTY | : 構文決定 |
ANY | : 構文決定 |
SYMBOL ('(') | : Mixedかchildrenのどちらかの'('であって、どちらかは未決定 |
その他 | : 構文エラー決定 |
Mixedとchildrenの共通部分を括りだすと
contentspec ::= EMPTY | ANY | ( SYMBOL ('(') S? (Mixed | children) )
となります。
また、この修正により、
Mixed ::= ( SYMBOL ('#PCDATA')
(S? SYMBOL ('|') S? Name)* S?
SYMBOL (')*')
)
| ( SYMBOL ('#PCDATA') S? SYMBOL (')') )
と修正します。childrenは先頭の非終端記号のchioceとseqを修整する必要があり、
choice ::= cp ( S? SYMBOL ('|') S? cp )+ S? SYMBOL (')')
seq ::= cp ( S? SYMBOL (',') S? cp )* S? SYMBOL (')')
となります。さらに、choiceとseqはcpにもあるのでcpも影響を受けて、
cp ::= (Name | (SYMBOL ('(') S? choice) | (SYMBOL ('(') S? seq))
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
となります。
これでcontentspecはLL (1)の構文になりました。
MixedのLL (1)化contentspec ::= EMPTY | ANY | ( SYMBOL ('(') S? (Mixed | children) )
となります。
また、この修正により、
Mixed ::= ( SYMBOL ('#PCDATA')
(S? SYMBOL ('|') S? Name)* S?
SYMBOL (')*')
)
| ( SYMBOL ('#PCDATA') S? SYMBOL (')') )
と修正します。childrenは先頭の非終端記号のchioceとseqを修整する必要があり、
choice ::= cp ( S? SYMBOL ('|') S? cp )+ S? SYMBOL (')')
seq ::= cp ( S? SYMBOL (',') S? cp )* S? SYMBOL (')')
となります。さらに、choiceとseqはcpにもあるのでcpも影響を受けて、
cp ::= (Name | (SYMBOL ('(') S? choice) | (SYMBOL ('(') S? seq))
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
となります。
これでcontentspecはLL (1)の構文になりました。
Mixedの新しい構文を見ると、同じようにLL (1)でないことが判ります。
そこで、共通項を括りだすと、
Mixed ::= SYMBOL ('#PCDATA')
(
( (S? SYMBOL ('|') S? Name)* S? SYMBOL (')*') )
| ( S? SYMBOL (')') )
)
となります。さらに後の説明のために非終端記号で構文を分離して。
Mixed ::= SYMBOL ('#PCDATA') ( MixedA | MixedB )
MixedA::= (S? SYMBOL ('|') S? Name)* S? SYMBOL (')*')
MixedB::= S? SYMBOL (')')
とします。
このMixedのLL (1)化は不十分です。なぜならSYMBOL ('#PCDATA')の次に来るトークンは、
となり、S を取得した場合に構文が確定せずLL (1)でないからです。
そこで、MixedAの
(S? SYMBOL ('|') S? Name)*
の繰り返し部分を0回から3回まで展開してみると、
※具体的にするために3回分展開してみます
・・・
となりますので、 X ::= SYMBOL ('|') S? Name S? とすると、
・・・
になります。
すると最終的に、
MixedA ::= S? SYMBOL (')*')
MixedA' ::= S? (SYMBOL ('|') S? Name S?)+ SYMBOL (')*')
に変形できます。
# 後の変形を考えて、MixedAとMixedA'は分けておきます
MixedBと並べると、
MixedA ::= S? SYMBOL (')*')
MixedA'::= S? (SYMBOL ('|') S? Name S?)+ SYMBOL (')*')
MixedB ::= S? SYMBOL (')')
ですから、S?が共通項となり、
Mixed ::= SYMBOL ('#PCDATA') S? (MixedA | MixedA' | MixedB)
MixedA ::= SYMBOL (')*')
MixedA'::= (SYMBOL ('|') S? Name S?)+ SYMBOL (')*')
MixedB ::= SYMBOL (')')
となります。
単独のトークンになった非終端記号はさらに整理して、
Mixed ::= SYMBOL ('#PCDATA') S?
(SYMBOL (')') | SYMBOL (')*') | MixedElemContents)
MixedElemContents ::= ( SYMBOL ('|') S? Name S? )+ SYMBOL (')*')
とします。
childrenのLL (1)化そこで、共通項を括りだすと、
Mixed ::= SYMBOL ('#PCDATA')
(
( (S? SYMBOL ('|') S? Name)* S? SYMBOL (')*') )
| ( S? SYMBOL (')') )
)
となります。さらに後の説明のために非終端記号で構文を分離して。
Mixed ::= SYMBOL ('#PCDATA') ( MixedA | MixedB )
MixedA::= (S? SYMBOL ('|') S? Name)* S? SYMBOL (')*')
MixedB::= S? SYMBOL (')')
とします。
このMixedのLL (1)化は不十分です。なぜならSYMBOL ('#PCDATA')の次に来るトークンは、
S | : MixedAまたはMixedBで決定できない |
SYMBOL ('|') | : MixedA |
SYMBOL (')*') | : MixedA |
SYMBOL (')') | : MixedB |
そこで、MixedAの
(S? SYMBOL ('|') S? Name)*
の繰り返し部分を0回から3回まで展開してみると、
※具体的にするために3回分展開してみます
0回 | S? SYMBOL (')*') |
1回 | S? SYMBOL ('|') S? Name S? SYMBOL (')*') |
2回 | S? SYMBOL ('|') S? Name S? SYMBOL ('|') S? Name S? SYMBOL (')*') |
3回 | S? SYMBOL ('|') S? Name S? SYMBOL ('|') S? Name S? SYMBOL ('|') S? Name S? SYMBOL (')*') |
となりますので、 X ::= SYMBOL ('|') S? Name S? とすると、
0回 | S? SYMBOL (')*') |
1回 | S? X SYMBOL (')*') |
2回 | S? X X SYMBOL (')*') |
3回 | S? X X X SYMBOL (')*') |
になります。
すると最終的に、
MixedA ::= S? SYMBOL (')*')
MixedA' ::= S? (SYMBOL ('|') S? Name S?)+ SYMBOL (')*')
に変形できます。
# 後の変形を考えて、MixedAとMixedA'は分けておきます
MixedBと並べると、
MixedA ::= S? SYMBOL (')*')
MixedA'::= S? (SYMBOL ('|') S? Name S?)+ SYMBOL (')*')
MixedB ::= S? SYMBOL (')')
ですから、S?が共通項となり、
Mixed ::= SYMBOL ('#PCDATA') S? (MixedA | MixedA' | MixedB)
MixedA ::= SYMBOL (')*')
MixedA'::= (SYMBOL ('|') S? Name S?)+ SYMBOL (')*')
MixedB ::= SYMBOL (')')
となります。
単独のトークンになった非終端記号はさらに整理して、
Mixed ::= SYMBOL ('#PCDATA') S?
(SYMBOL (')') | SYMBOL (')*') | MixedElemContents)
MixedElemContents ::= ( SYMBOL ('|') S? Name S? )+ SYMBOL (')*')
とします。
children ::= (choice | seq) (SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
choice ::= cp ( S? SYMBOL ('|') S? cp )+ S? SYMBOL (')')
seq ::= cp ( S? SYMBOL (',') S? cp )* S? SYMBOL (')')
cp ::= (Name | (SYMBOL ('(') S? choice) | (SYMBOL ('(') S? seq))
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
において、childrenの先頭はchoiceかseqのAlternative句をとり、
かつ、choiceとseqの最初のトークンはどちらもcpであり、続くトークンがRepetition句からなります。
つまり、childrenはLL (1)ではありませんので、LL (1)に変形してみます。
まず、choiceとseqのRepetition句を展開してcp周りを整理します。
choice ::=
・・・
seq ::=
・・・
となります。先頭のcp S? を除き繰り返し部分を抽出して
choiceTerm ::= SYMBOL ('|') S? cp S?
seqTerm ::= SYMBOL (',') S? cp S?
を非終端記号とすると、
choice ::=
・・・
seq ::=
・・・
となりますので、
choice ::= cp S? choiceTerm+ SYMBOL (')')
seq ::= cp S? seqTerm* SYMBOL (')')
に整理できます。よってchildrenの先頭のAlternative句は
(choice | seq) ::= cp S? (choiceTerm+ | seqTerm*) SYMBOL (')')
となります。
さてchoiceTermの繰り返しが+でseqTermの繰り返しが*なのは、childrenで単項(cpだけからなる)の場合に、seqTerm側の構文を通るようにするためです。上記の(choice | seq)句では、単項のcpを括りだしているのでその必要はありません。よって
(choice | seq) ::= cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')')
のように変形して繰り返し記号をあわせます。
最終的に、
となります。
cpの整理choice ::= cp ( S? SYMBOL ('|') S? cp )+ S? SYMBOL (')')
seq ::= cp ( S? SYMBOL (',') S? cp )* S? SYMBOL (')')
cp ::= (Name | (SYMBOL ('(') S? choice) | (SYMBOL ('(') S? seq))
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
において、childrenの先頭はchoiceかseqのAlternative句をとり、
かつ、choiceとseqの最初のトークンはどちらもcpであり、続くトークンがRepetition句からなります。
つまり、childrenはLL (1)ではありませんので、LL (1)に変形してみます。
まず、choiceとseqのRepetition句を展開してcp周りを整理します。
choice ::=
0回 | cp S? SYMBOL ('|') S? cp S? SYMBOL (')') |
1回 | cp S? SYMBOL ('|') S? cp S? SYMBOL ('|') S? cp S? SYMBOL (')') |
2回 | cp S? SYMBOL ('|') S? cp S? SYMBOL ('|') S? cp S? SYMBOL ('|') S? cp S? SYMBOL (')') |
seq ::=
0回 | cp S? SYMBOL (')') |
1回 | cp S? SYMBOL (',') S? cp S? SYMBOL (')') |
2回 | cp S? SYMBOL (',') S? cp S? SYMBOL (',') S? cp S? SYMBOL (')') |
となります。先頭のcp S? を除き繰り返し部分を抽出して
choiceTerm ::= SYMBOL ('|') S? cp S?
seqTerm ::= SYMBOL (',') S? cp S?
を非終端記号とすると、
choice ::=
0回 | cp S? choiceTerm SYMBOL (')') |
1回 | cp S? choiceTerm choiceTerm SYMBOL (')') |
2回 | cp S? choiceTerm choiceTerm choiceTerm SYMBOL (')') |
seq ::=
0回 | cp S? SYMBOL (')') |
1回 | cp S? seqTerm SYMBOL (')') |
2回 | cp S? seqTerm seqTerm SYMBOL (')') |
となりますので、
choice ::= cp S? choiceTerm+ SYMBOL (')')
seq ::= cp S? seqTerm* SYMBOL (')')
に整理できます。よってchildrenの先頭のAlternative句は
(choice | seq) ::= cp S? (choiceTerm+ | seqTerm*) SYMBOL (')')
となります。
さてchoiceTermの繰り返しが+でseqTermの繰り返しが*なのは、childrenで単項(cpだけからなる)の場合に、seqTerm側の構文を通るようにするためです。上記の(choice | seq)句では、単項のcpを括りだしているのでその必要はありません。よって
(choice | seq) ::= cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')')
のように変形して繰り返し記号をあわせます。
最終的に、
children | ::= | cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')') (SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))? |
choiceTerm | ::= | SYMBOL ('|') S? cp S? |
seqTerm | ::= | SYMBOL (',') S? cp S? |
cpを整理します。
cp ::= (Name | (SYMBOL ('(') S? choice) | (SYMBOL ('(') S? seq))
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
から、
cp ::= (Name | ( SYMBOL ('(') S? (choice | seq) ) )
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
に変形して、さらにchildrenのLL (1)化で
(choice | seq) ::= cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')')
としたので、
cp ::= (Name
| ( SYMBOL ('(') S? cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')') )
)
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
となります。
cpにchildrenを導入するcp ::= (Name | (SYMBOL ('(') S? choice) | (SYMBOL ('(') S? seq))
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
から、
cp ::= (Name | ( SYMBOL ('(') S? (choice | seq) ) )
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
に変形して、さらにchildrenのLL (1)化で
(choice | seq) ::= cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')')
としたので、
cp ::= (Name
| ( SYMBOL ('(') S? cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')') )
)
(SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
となります。
cpは SYMBOL ('(')とSYMBOL (')')で囲んだ部分にchildrenを書くことができます。
そこで、共通項を
modifier ::= (SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
とすると
になります。cpは次のように形を変えると、
cp ::= (Name modifier)
| (SYMBOL ('(') S? cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')')
modifier )
とでき、childrenを含みますから
cp ::= (Name modifier) | ( SYMBOL ('(') S? children )
となります。非終端記号cpName,multiTermを導入すると、
cp ::= (cpName | multiTerm)
cpName ::= Name modifier
multiTerm ::= SYMBOL ('(') S? children
になります。
multiTermの開き括弧と閉じ括弧を同じ構文に入れるそこで、共通項を
modifier ::= (SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+'))?
とすると
children | ::= | cp S? (choiceTerm+ | seqTerm*)? SYMBOL (')') modifier |
cp | ::= | (Name | ( SYMBOL ('(') S? cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')') ) ) modifier |
cp ::= (Name modifier)
| (SYMBOL ('(') S? cp S? (choiceTerm+ | seqTerm+)? SYMBOL (')')
modifier )
とでき、childrenを含みますから
cp ::= (Name modifier) | ( SYMBOL ('(') S? children )
となります。非終端記号cpName,multiTermを導入すると、
cp ::= (cpName | multiTerm)
cpName ::= Name modifier
multiTerm ::= SYMBOL ('(') S? children
になります。
multiTermは、開き括弧はあるのに、同じ構文に閉じ括弧がありません。
そこでchildrenとmultiTermを修正して括弧を同じ構文内に収めます。
term ::= cp S? (choiceTerm+ | seqTerm+)?
とすると、
multiTerm ::= SYMBOL ('(') S? term S? SYMBOL (')') modifier
children ::= term S? SYMBOL (')') modifier
に整理できます。
modifierとトークン粒度の整理およびここまでの構文のまとめそこでchildrenとmultiTermを修正して括弧を同じ構文内に収めます。
term ::= cp S? (choiceTerm+ | seqTerm+)?
とすると、
multiTerm ::= SYMBOL ('(') S? term S? SYMBOL (')') modifier
children ::= term S? SYMBOL (')') modifier
に整理できます。
modifierは説明するために必要だったので導入しましたが
実際は SYMBOL (')') modifierが連続する場合は、1つのトークンになります。※cpName用のmodifierは残ります。
よって、ここまでの検討を経て
となります。実際は SYMBOL (')') modifierが連続する場合は、1つのトークンになります。※cpName用のmodifierは残ります。
よって、ここまでの検討を経て
クラス名 | 整理した構文 |
EbnfMixedElemContentsNode | MixedElemContents ::= ( SYMBOL ('|') S? Name S? )+ SYMBOL (')*') |
EbnfMixedNode | Mixed ::= SYMBOL ('#PCDATA') S? ( SYMBOL (')') | SYMBOL (')*') | MixedElemContents ) |
EbnfModifierNode | modifier ::= ( SYMBOL ('?') | SYMBOL ('*') | SYMBOL ('+') )? |
EbnfCpNode | cp ::= (cpName | multiTerm) |
EbnfMultiTermNode | multiTerm ::= SYMBOL ('(') S? term S? ( SYMBOL (')') | SYMBOL (')?') | SYMBOL (')*') | SYMBOL (')+') ) |
EbnfTermNode | term ::= cp S? (choiceTerm+ | seqTerm+)? |
EbnfCpNameNode | cpName ::= Name modifier |
EbnfChildrenNode | children ::= term S? ( SYMBOL (')') | SYMBOL (')?') | SYMBOL (')*') | SYMBOL (')+') ) |
EbnfChoiceTermNode | choiceTerm ::= SYMBOL ('|') S? cp S? |
EbnfSeqTermNode | seqTerm ::= SYMBOL (',') S? cp S? |
EbnfContentspecNode | contentspec ::= WORD ('EMPTY')| WORD ('ANY') | ( SYMBOL ('(') S? ( Mixed | children ) ) |
Symbolの追加
')'とmodifierが一つのトークンになったので、')?'と')+'を追加します。SYMBOL ::= '#FIXED'
| '#IMPLIED'
| '#PCDATA'
| '#REQUIRED'
| '%'
| '&#'
| '&#x'
| '&'
| '('
| ')'
| ')?' //追加
| ')*'
| ')+' //追加
| '*'
| '+'
| ','
| '/>'
| ';'
| '='
| '<'
| '</'
| '<?'
| '<?xml'
| '<!['
| '<![CDATA['
| '<!ATTLIST'
| '<!DOCTYPE'
| '<!ENTITY'
| '<!ELEMENT'
| '<!NOTATION'
| '>'
| '?'
| '?>'
| '['
| ']'
| ']]>'
| '|'
| '#PCDATA'
| '#REQUIRED'
| '%'
| '&#'
| '&#x'
| '&'
| '('
| ')'
| ')?' //追加
| ')*'
| ')+' //追加
| '*'
| '+'
| ','
| '/>'
| ';'
| '='
| '<'
| '</'
| '<?'
| '<?xml'
| '<!['
| '<![CDATA['
| '<!ATTLIST'
| '<!DOCTYPE'
| '<!ENTITY'
| '<!ELEMENT'
| '<!NOTATION'
| '>'
| '?'
| '?>'
| '['
| ']'
| ']]>'
| '|'
コメント
コメントを投稿する
SpecialPR