-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement FIRST and FOLLOW functions for context-free grammars
- Loading branch information
Showing
13 changed files
with
1,309 additions
and
315 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package grammar | ||
|
||
import ( | ||
"github.com/moorara/algo/set" | ||
. "github.com/moorara/algo/symboltable" | ||
) | ||
|
||
var eqTerminalsAndEmpty = func(lhs, rhs *TerminalsAndEmpty) bool { | ||
return lhs.Terminals.Equals(rhs.Terminals) && lhs.IncludesEmpty == rhs.IncludesEmpty | ||
} | ||
|
||
// FIRST is the FIRST function associated with a context-free grammar. | ||
// | ||
// FIRST(α), where α is any string of grammar symbols (terminals and non-terminals), | ||
// is the set of terminals that begin strings derived from α. | ||
// If α ⇒* ε, then ε is also in FIRST(α). | ||
type FIRST func(String[Symbol]) TerminalsAndEmpty | ||
|
||
// TerminalsAndEmpty is the return type for the FIRST function. | ||
// | ||
// It contains: | ||
// | ||
// - A set of terminals that may appear at the beginning of strings derived from α. | ||
// - A flag indicating whether the empty string ε is included in the FIRST set.. | ||
type TerminalsAndEmpty struct { | ||
Terminals set.Set[Terminal] | ||
IncludesEmpty bool | ||
} | ||
|
||
func newTerminalsAndEmpty(terms ...Terminal) *TerminalsAndEmpty { | ||
return &TerminalsAndEmpty{ | ||
Terminals: set.New(eqTerminal, terms...), | ||
IncludesEmpty: false, | ||
} | ||
} | ||
|
||
// firstBySymbolTable is the type for a table that stores the FIRST set for each grammar symbol. | ||
type firstBySymbolTable SymbolTable[Symbol, *TerminalsAndEmpty] | ||
|
||
func newFirstBySymbolTable() firstBySymbolTable { | ||
return NewQuadraticHashTable(hashSymbol, eqSymbol, eqTerminalsAndEmpty, HashOpts{}) | ||
} | ||
|
||
// firstByStringTable is the type for a table that stores the FIRST set for strings of grammar symbols. | ||
type firstByStringTable SymbolTable[String[Symbol], *TerminalsAndEmpty] | ||
|
||
func newFirstByStringTable() firstByStringTable { | ||
return NewQuadraticHashTable(hashString, eqString, eqTerminalsAndEmpty, HashOpts{}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package grammar | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNewTerminalsAndEmpty(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
terms []Terminal | ||
}{ | ||
{ | ||
name: "OK", | ||
terms: []Terminal{"a", "b", "c", "d", "e", "f"}, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
f := newTerminalsAndEmpty(tc.terms...) | ||
assert.NotNil(t, f) | ||
assert.True(t, f.Terminals.Contains(tc.terms...)) | ||
assert.False(t, f.IncludesEmpty) | ||
}) | ||
} | ||
} | ||
|
||
func TestNewFirstBySymbolTable(t *testing.T) { | ||
t.Run("OK", func(t *testing.T) { | ||
firstBySymbol := newFirstBySymbolTable() | ||
assert.NotNil(t, firstBySymbol) | ||
}) | ||
} | ||
|
||
func TestNewFirstByStringTable(t *testing.T) { | ||
t.Run("OK", func(t *testing.T) { | ||
firstByString := newFirstByStringTable() | ||
assert.NotNil(t, firstByString) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package grammar | ||
|
||
import ( | ||
"github.com/moorara/algo/set" | ||
. "github.com/moorara/algo/symboltable" | ||
) | ||
|
||
var eqTerminalsAndEndmarker = func(lhs, rhs *TerminalsAndEndmarker) bool { | ||
return lhs.Terminals.Equals(rhs.Terminals) && lhs.IncludesEndmarker == rhs.IncludesEndmarker | ||
} | ||
|
||
// FOLLOW is the FIRST function associated with a context-free grammar. | ||
// | ||
// FOLLOW(A), for non-terminal A, is the set of terminals 𝑎 | ||
// that can appear immediately to the right of A in some sentential form; | ||
// that is; the set of terminals 𝑎 such that there exists a derivation of the form S ⇒* αAaβ | ||
// for some α and β strings of grammar symbols (terminals and non-terminals). | ||
type FOLLOW func(NonTerminal) TerminalsAndEndmarker | ||
|
||
// TerminalsAndEndmarker is the return type for the FOLLOW function. | ||
// | ||
// It contains: | ||
// | ||
// - A set of terminals that can appear immediately after the given non-terminal. | ||
// - A flag indicating whether the special endmarker symbol is included in the FOLLOW set. | ||
type TerminalsAndEndmarker struct { | ||
Terminals set.Set[Terminal] | ||
IncludesEndmarker bool | ||
} | ||
|
||
func newTerminalsAndEndmarker(terms ...Terminal) *TerminalsAndEndmarker { | ||
return &TerminalsAndEndmarker{ | ||
Terminals: set.New(eqTerminal, terms...), | ||
IncludesEndmarker: false, | ||
} | ||
} | ||
|
||
// followTable is the type for a table that stores the FOLLOW set for each non-terminal. | ||
type followTable SymbolTable[NonTerminal, *TerminalsAndEndmarker] | ||
|
||
func newFollowTable() followTable { | ||
return NewQuadraticHashTable(hashNonTerminal, eqNonTerminal, eqTerminalsAndEndmarker, HashOpts{}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package grammar | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNewTerminalsAndEndmarker(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
terms []Terminal | ||
}{ | ||
{ | ||
name: "OK", | ||
terms: []Terminal{"a", "b", "c", "d", "e", "f"}, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
f := newTerminalsAndEndmarker(tc.terms...) | ||
assert.NotNil(t, f) | ||
assert.True(t, f.Terminals.Contains(tc.terms...)) | ||
assert.False(t, f.IncludesEndmarker) | ||
}) | ||
} | ||
} | ||
|
||
func TestNewFollowTable(t *testing.T) { | ||
t.Run("OK", func(t *testing.T) { | ||
follow := newFollowTable() | ||
assert.NotNil(t, follow) | ||
}) | ||
} |
Oops, something went wrong.