aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyo Nihei <nihei.dev@gmail.com>2022-05-14 15:45:09 +0900
committerRyo Nihei <nihei.dev@gmail.com>2022-05-15 20:42:28 +0900
commit24fd80555cb35d9fc63cca9e8697bf156f41780b (patch)
tree0bb5402b8061f63ebef0ff779e59d0d50e20db9e
parentProhibit applying the expansion operator to anything other than identifiers (diff)
downloadcotia-24fd80555cb35d9fc63cca9e8697bf156f41780b.tar.gz
cotia-24fd80555cb35d9fc63cca9e8697bf156f41780b.tar.xz
Prohibit applying #left, #right, #assign, and #prec to an error symbol
The shift of the error symbol is an operation forced by the driver. Therefore it is impossible to change this behavior by giving precedence to the error symbol. If we desire to change the precedence of a production rule with the error symbol, we can use #prec directive.
-rw-r--r--grammar/grammar.go46
-rw-r--r--grammar/grammar_test.go71
2 files changed, 103 insertions, 14 deletions
diff --git a/grammar/grammar.go b/grammar/grammar.go
index 82eff5d..410236b 100644
--- a/grammar/grammar.go
+++ b/grammar/grammar.go
@@ -155,7 +155,7 @@ func (b *GrammarBuilder) Build() (*Grammar, error) {
return nil, b.errs
}
- pa, err := b.genPrecAndAssoc(symTabAndLexSpec.symTab, prodsAndActs)
+ pa, err := b.genPrecAndAssoc(symTabAndLexSpec.symTab, symTabAndLexSpec.errSym, prodsAndActs)
if err != nil {
return nil, err
}
@@ -999,32 +999,41 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
})
continue LOOP_RHS
}
+ param := dir.Parameters[0]
switch {
- case dir.Parameters[0].ID != "":
- sym, ok := symTab.toSymbol(dir.Parameters[0].ID)
+ case param.ID != "":
+ sym, ok := symTab.toSymbol(param.ID)
if !ok {
b.errs = append(b.errs, &verr.SpecError{
Cause: semErrDirInvalidParam,
- Detail: fmt.Sprintf("unknown terminal symbol: %v", dir.Parameters[0].ID),
- Row: dir.Pos.Row,
- Col: dir.Pos.Col,
+ Detail: fmt.Sprintf("unknown terminal symbol: %v", param.ID),
+ Row: param.Pos.Row,
+ Col: param.Pos.Col,
})
continue LOOP_RHS
}
+ if sym == errSym {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDirInvalidParam,
+ Detail: fmt.Sprintf("'%v' directive cannot be applied to an error symbol", dir.Name),
+ Row: param.Pos.Row,
+ Col: param.Pos.Col,
+ })
+ }
if !sym.isTerminal() {
b.errs = append(b.errs, &verr.SpecError{
Cause: semErrDirInvalidParam,
- Detail: fmt.Sprintf("the symbol must be a terminal: %v", dir.Parameters[0].ID),
- Row: dir.Pos.Row,
- Col: dir.Pos.Col,
+ Detail: fmt.Sprintf("the symbol must be a terminal: %v", param.ID),
+ Row: param.Pos.Row,
+ Col: param.Pos.Col,
})
continue LOOP_RHS
}
prodPrecsTerm[p.id] = sym
- prodPrecPoss[p.id] = &dir.Parameters[0].Pos
- case dir.Parameters[0].OrderedSymbol != "":
- prodPrecsOrdSym[p.id] = dir.Parameters[0].OrderedSymbol
- prodPrecPoss[p.id] = &dir.Parameters[0].Pos
+ prodPrecPoss[p.id] = &param.Pos
+ case param.OrderedSymbol != "":
+ prodPrecsOrdSym[p.id] = param.OrderedSymbol
+ prodPrecPoss[p.id] = &param.Pos
}
case "recover":
if len(dir.Parameters) > 0 {
@@ -1061,7 +1070,7 @@ func (b *GrammarBuilder) genProductionsAndActions(root *spec.RootNode, symTabAnd
}, nil
}
-func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prodsAndActs *productionsAndActions) (*precAndAssoc, error) {
+func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, errSym symbol, prodsAndActs *productionsAndActions) (*precAndAssoc, error) {
termPrec := map[symbolNum]int{}
termAssoc := map[symbolNum]assocType{}
ordSymPrec := map[string]int{}
@@ -1136,6 +1145,15 @@ func (b *GrammarBuilder) genPrecAndAssoc(symTab *symbolTable, prodsAndActs *prod
})
return nil, nil
}
+ if sym == errSym {
+ b.errs = append(b.errs, &verr.SpecError{
+ Cause: semErrDirInvalidParam,
+ Detail: fmt.Sprintf("'%v' directive cannot be applied to an error symbol", dir.Name),
+ Row: p.Pos.Row,
+ Col: p.Pos.Col,
+ })
+ return nil, nil
+ }
if !sym.isTerminal() {
b.errs = append(b.errs, &verr.SpecError{
Cause: semErrDirInvalidParam,
diff --git a/grammar/grammar_test.go b/grammar/grammar_test.go
index 5a8bb4a..aeeffac 100644
--- a/grammar/grammar_test.go
+++ b/grammar/grammar_test.go
@@ -1714,6 +1714,25 @@ foo
errs: []*SemanticError{semErrDirInvalidParam},
},
{
+ caption: "the `#left` directive cannot be applied to an error symbol",
+ specSrc: `
+#name test;
+
+#prec (
+ #left error
+);
+
+s
+ : foo ';'
+ | error ';'
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
caption: "the `#left` directive cannot take an undefined symbol",
specSrc: `
#name test;
@@ -1937,6 +1956,25 @@ foo
errs: []*SemanticError{semErrDirInvalidParam},
},
{
+ caption: "the `#right` directive cannot be applied to an error symbol",
+ specSrc: `
+#name test;
+
+#prec (
+ #right error
+);
+
+s
+ : foo ';'
+ | error ';'
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
caption: "the `#right` directive cannot take an undefined symbol",
specSrc: `
#name test;
@@ -2160,6 +2198,25 @@ foo
errs: []*SemanticError{semErrDirInvalidParam},
},
{
+ caption: "the `#assign` directive cannot be applied to an error symbol",
+ specSrc: `
+#name test;
+
+#prec (
+ #assign error
+);
+
+s
+ : foo ';'
+ | error ';'
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
caption: "the `#assign` directive cannot take an undefined symbol",
specSrc: `
#name test;
@@ -2669,6 +2726,20 @@ foo
errs: []*SemanticError{semErrDirInvalidParam},
},
{
+ caption: "the `#prec` directive cannot be applied to an error symbol",
+ specSrc: `
+#name test;
+
+s
+ : foo #prec error
+ ;
+
+foo
+ : 'foo';
+`,
+ errs: []*SemanticError{semErrDirInvalidParam},
+ },
+ {
caption: "the `#prec` directive cannot take an undefined symbol",
specSrc: `
#name test;