refactor init phase - core

This commit is contained in:
Arpad Ryszka 2017-11-01 02:43:46 +01:00
parent c43dd97310
commit 52b2bcc751
7 changed files with 162 additions and 204 deletions

25
char.go
View File

@ -29,27 +29,24 @@ func (p *charParser) nodeID() int { return p.id }
func (p *charParser) setID(id int) { p.id = id } func (p *charParser) setID(id int) { p.id = id }
func (p *charParser) commitType() CommitType { return Alias } func (p *charParser) commitType() CommitType { return Alias }
func (p *charParser) setCommitType(ct CommitType) {} func (p *charParser) setCommitType(ct CommitType) {}
func (p *charParser) validate(*registry, *idSet) error { return nil } func (p *charParser) validate(*registry) error { return nil }
func (p *charParser) normalize(*registry, *idSet) error { return nil } func (p *charParser) init(*registry) {}
func (p *charParser) init(r *registry) error { return nil } func (p *charParser) setIncludedBy(r *registry, includedBy int) {
if intsContain(p.includedBy, includedBy) {
func (p *charParser) setIncludedBy(r *registry, includedBy int, parsers *idSet) error { return
p.includedBy = appendIfMissing(p.includedBy, includedBy)
return nil
}
func (p *charParser) parser(r *registry, parsers *idSet) (parser, error) {
if parsers.has(p.id) {
panic(cannotIncludeParsers(p.name))
} }
p.includedBy = append(p.includedBy, includedBy)
}
func (p *charParser) parser(r *registry) parser {
if _, ok := r.parser(p.name); ok { if _, ok := r.parser(p.name); ok {
return p, nil return p
} }
r.setParser(p) r.setParser(p)
return p, nil return p
} }
func (p *charParser) builder() builder { func (p *charParser) builder() builder {

View File

@ -7,6 +7,8 @@ type choiceDefinition struct {
elements []string elements []string
includedBy []int includedBy []int
cbuilder *choiceBuilder cbuilder *choiceBuilder
validated bool
initialized bool
} }
type choiceParser struct { type choiceParser struct {
@ -40,30 +42,34 @@ func (d *choiceDefinition) setID(id int) { d.id = id }
func (d *choiceDefinition) commitType() CommitType { return d.commit } func (d *choiceDefinition) commitType() CommitType { return d.commit }
func (d *choiceDefinition) setCommitType(ct CommitType) { d.commit = ct } func (d *choiceDefinition) setCommitType(ct CommitType) { d.commit = ct }
func (d *choiceDefinition) validate(r *registry, path *idSet) error { func (d *choiceDefinition) validate(r *registry) error {
if d.validated {
return nil
}
d.validated = true
for i := range d.elements { for i := range d.elements {
if _, ok := r.definitions[d.elements[i]]; !ok { e, ok := r.definitions[d.elements[i]]
if !ok {
return parserNotFound(d.elements[i]) return parserNotFound(d.elements[i])
} }
if err := e.validate(r); err != nil {
return err
}
} }
return nil return nil
} }
func (d *choiceDefinition) normalize(r *registry, path *idSet) error { func (d *choiceDefinition) init(r *registry) {
if path.has(d.id) { if d.initialized {
return nil return
} }
path.set(d.id) d.initialized = true
for i := range d.elements {
r.definitions[d.elements[i]].normalize(r, path)
}
return nil
}
func (d *choiceDefinition) init(r *registry) error {
if d.cbuilder == nil { if d.cbuilder == nil {
d.cbuilder = &choiceBuilder{ d.cbuilder = &choiceBuilder{
name: d.name, name: d.name,
@ -74,21 +80,19 @@ func (d *choiceDefinition) init(r *registry) error {
} }
for _, e := range d.elements { for _, e := range d.elements {
// TODO: handle undefined reference def := r.definitions[e]
d.cbuilder.elements = append(d.cbuilder.elements, r.definitions[e].builder()) d.cbuilder.elements = append(d.cbuilder.elements, def.builder())
def.init(r)
def.setIncludedBy(r, d.id)
} }
parsers := &idSet{}
parsers.set(d.id)
return setItemsIncludedBy(r, d.elements, d.id, parsers)
} }
func (d *choiceDefinition) setIncludedBy(r *registry, includedBy int, parsers *idSet) error { func (d *choiceDefinition) setIncludedBy(r *registry, includedBy int) {
if parsers.has(d.id) { if intsContain(d.includedBy, includedBy) {
return nil return
} }
d.includedBy = appendIfMissing(d.includedBy, includedBy) d.includedBy = append(d.includedBy, includedBy)
if d.cbuilder == nil { if d.cbuilder == nil {
d.cbuilder = &choiceBuilder{ d.cbuilder = &choiceBuilder{
@ -101,18 +105,19 @@ func (d *choiceDefinition) setIncludedBy(r *registry, includedBy int, parsers *i
d.cbuilder.includedBy.set(includedBy) d.cbuilder.includedBy.set(includedBy)
parsers.set(d.id) for _, e := range d.elements {
return setItemsIncludedBy(r, d.elements, includedBy, parsers) r.definitions[e].setIncludedBy(r, includedBy)
}
} }
// TODO: // TODO:
// - it may be possible to initialize the parsers non-recursively // - it may be possible to initialize the parsers non-recursively
// - maybe the whole definition, parser and builder can be united // - maybe the whole definition, parser and builder can be united
func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) { func (d *choiceDefinition) parser(r *registry) parser {
p, ok := r.parser(d.name) p, ok := r.parser(d.name)
if ok { if ok {
return p, nil return p
} }
cp := &choiceParser{ cp := &choiceParser{
@ -125,8 +130,6 @@ func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) {
r.setParser(cp) r.setParser(cp)
var elements []parser var elements []parser
parsers.set(d.id)
defer parsers.unset(d.id)
for _, e := range d.elements { for _, e := range d.elements {
element, ok := r.parser(e) element, ok := r.parser(e)
if ok { if ok {
@ -134,16 +137,12 @@ func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) {
continue continue
} }
element, err := r.definitions[e].parser(r, parsers) element = r.definitions[e].parser(r)
if err != nil {
return nil, err
}
elements = append(elements, element) elements = append(elements, element)
} }
cp.elements = elements cp.elements = elements
return cp, nil return cp
} }
func (d *choiceDefinition) builder() builder { func (d *choiceDefinition) builder() builder {

View File

@ -9,11 +9,10 @@ type definition interface {
commitType() CommitType commitType() CommitType
setCommitType(CommitType) setCommitType(CommitType)
setID(int) setID(int)
validate(*registry, *idSet) error validate(*registry) error
normalize(*registry, *idSet) error init(*registry)
init(*registry) error setIncludedBy(*registry, int)
setIncludedBy(*registry, int, *idSet) error parser(*registry) parser
parser(*registry, *idSet) (parser, error)
builder() builder builder() builder
} }
@ -47,36 +46,6 @@ func intsContain(is []int, i int) bool {
return false return false
} }
func appendIfMissing(is []int, i int) []int {
if intsContain(is, i) {
return is
}
return append(is, i)
}
func setItemsIncludedBy(r *registry, items []string, includedBy int, parsers *idSet) error {
for _, item := range items {
di, ok := r.definition(item)
if !ok {
return ErrNoParsersDefined
}
di.setIncludedBy(r, includedBy, parsers)
}
return nil
}
func sequenceItemNames(items []SequenceItem) []string {
names := make([]string, len(items))
for i := range items {
names[i] = items[i].Name
}
return names
}
func parse(p parser, c *context) error { func parse(p parser, c *context) error {
p.parse(c) p.parse(c)
if c.readErr != nil { if c.readErr != nil {

View File

@ -8,13 +8,19 @@ type registry struct {
parsers map[string]parser parsers map[string]parser
} }
func newRegistry() *registry { func newRegistry(defs ...definition) *registry {
return &registry{ r := &registry{
ids: make(map[string]int), ids: make(map[string]int),
names: make(map[int]string), names: make(map[int]string),
definitions: make(map[string]definition), definitions: make(map[string]definition),
parsers: make(map[string]parser), parsers: make(map[string]parser),
} }
for _, def := range defs {
r.setDefinition(def)
}
return r
} }
func (r *registry) definition(name string) (definition, bool) { func (r *registry) definition(name string) (definition, bool) {
@ -45,3 +51,12 @@ func (r *registry) setDefinition(d definition) error {
func (r *registry) setParser(p parser) { func (r *registry) setParser(p parser) {
r.parsers[p.nodeName()] = p r.parsers[p.nodeName()] = p
} }
func (r *registry) getDefinitions() []definition {
var defs []definition
for _, def := range r.definitions {
defs = append(defs, def)
}
return defs
}

View File

@ -9,6 +9,8 @@ type sequenceDefinition struct {
ranges [][]int ranges [][]int
sbuilder *sequenceBuilder sbuilder *sequenceBuilder
allChars bool allChars bool
validated bool
initialized bool
} }
type sequenceParser struct { type sequenceParser struct {
@ -46,36 +48,22 @@ func (d *sequenceDefinition) setID(id int) { d.id = id }
func (d *sequenceDefinition) commitType() CommitType { return d.commit } func (d *sequenceDefinition) commitType() CommitType { return d.commit }
func (d *sequenceDefinition) setCommitType(ct CommitType) { d.commit = ct } func (d *sequenceDefinition) setCommitType(ct CommitType) { d.commit = ct }
func (d *sequenceDefinition) validate(r *registry, path *idSet) error { func (d *sequenceDefinition) validate(r *registry) error {
if d.validated {
return nil
}
d.validated = true
for i := range d.items { for i := range d.items {
if _, ok := r.definition(d.items[i].Name); !ok { ii, ok := r.definition(d.items[i].Name)
if !ok {
return parserNotFound(d.items[i].Name) return parserNotFound(d.items[i].Name)
} }
if err := ii.validate(r); err != nil {
return err
} }
return nil
}
func (d *sequenceDefinition) normalizeItems() {
for i := range d.items {
if d.items[i].Min == 0 && d.items[i].Max == 0 {
d.items[i].Min, d.items[i].Max = 1, 1
} else if d.items[i].Max == 0 {
d.items[i].Max = -1
}
}
}
func (d *sequenceDefinition) normalize(r *registry, path *idSet) error {
if path.has(d.id) {
return nil
}
// d.normalizeItems()
path.set(d.id)
for i := range d.items {
r.definitions[d.items[i].Name].normalize(r, path)
} }
return nil return nil
@ -85,7 +73,21 @@ func (d *sequenceDefinition) includeItems() bool {
return len(d.items) == 1 && d.items[0].Min == 1 && d.items[0].Max == 1 return len(d.items) == 1 && d.items[0].Min == 1 && d.items[0].Max == 1
} }
func (d *sequenceDefinition) init(r *registry) error { func (d *sequenceDefinition) init(r *registry) {
if d.initialized {
return
}
d.initialized = true
for i := range d.items {
if d.items[i].Min == 0 && d.items[i].Max == 0 {
d.items[i].Min, d.items[i].Max = 1, 1
} else if d.items[i].Max == 0 {
d.items[i].Max = -1
}
}
if d.sbuilder == nil { if d.sbuilder == nil {
d.sbuilder = &sequenceBuilder{ d.sbuilder = &sequenceBuilder{
name: d.name, name: d.name,
@ -113,6 +115,8 @@ func (d *sequenceDefinition) init(r *registry) error {
allChars = false allChars = false
} }
} }
def.init(r)
} }
d.sbuilder.ranges = d.ranges d.sbuilder.ranges = d.ranges
@ -120,20 +124,18 @@ func (d *sequenceDefinition) init(r *registry) error {
d.allChars = allChars d.allChars = allChars
if !d.includeItems() { if !d.includeItems() {
return nil return
} }
parsers := &idSet{} r.definitions[d.items[0].Name].setIncludedBy(r, d.id)
parsers.set(d.id)
return setItemsIncludedBy(r, sequenceItemNames(d.items), d.id, parsers)
} }
func (d *sequenceDefinition) setIncludedBy(r *registry, includedBy int, parsers *idSet) error { func (d *sequenceDefinition) setIncludedBy(r *registry, includedBy int) {
if parsers.has(d.id) { if intsContain(d.includedBy, includedBy) {
return nil return
} }
d.includedBy = appendIfMissing(d.includedBy, includedBy) d.includedBy = append(d.includedBy, includedBy)
if d.sbuilder == nil { if d.sbuilder == nil {
d.sbuilder = &sequenceBuilder{ d.sbuilder = &sequenceBuilder{
@ -147,21 +149,16 @@ func (d *sequenceDefinition) setIncludedBy(r *registry, includedBy int, parsers
d.sbuilder.includedBy.set(includedBy) d.sbuilder.includedBy.set(includedBy)
if !d.includeItems() { if !d.includeItems() {
return nil return
} }
parsers.set(d.id) r.definitions[d.items[0].Name].setIncludedBy(r, includedBy)
return setItemsIncludedBy(r, sequenceItemNames(d.items), includedBy, parsers)
} }
func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error) { func (d *sequenceDefinition) parser(r *registry) parser {
if parsers.has(d.id) {
panic(cannotIncludeParsers(d.name))
}
p, ok := r.parser(d.name) p, ok := r.parser(d.name)
if ok { if ok {
return p, nil return p
} }
sp := &sequenceParser{ sp := &sequenceParser{
@ -175,8 +172,6 @@ func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error)
r.setParser(sp) r.setParser(sp)
var items []parser var items []parser
parsers.set(d.id)
defer parsers.unset(d.id)
for _, item := range d.items { for _, item := range d.items {
pi, ok := r.parser(item.Name) pi, ok := r.parser(item.Name)
if ok { if ok {
@ -184,17 +179,13 @@ func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error)
continue continue
} }
pi, err := r.definitions[item.Name].parser(r, parsers) pi = r.definitions[item.Name].parser(r)
if err != nil {
return nil, err
}
items = append(items, pi) items = append(items, pi)
} }
sp.items = items sp.items = items
sp.ranges = d.ranges sp.ranges = d.ranges
return sp, nil return sp
} }
func (d *sequenceDefinition) builder() builder { func (d *sequenceDefinition) builder() builder {

View File

@ -234,35 +234,22 @@ func (s *Syntax) Init() error {
return ErrRootWhitespace return ErrRootWhitespace
} }
s.registry = initWhitespace(s.registry) defs := s.registry.getDefinitions()
for _, def := range s.registry.definitions { if hasWhitespace(defs) {
if def.commitType()&Root != 0 { defs, s.root = applyWhitespace(defs)
s.root = def s.registry = newRegistry(defs...)
break
}
} }
if err := s.root.validate(s.registry, &idSet{}); err != nil { if err := s.root.validate(s.registry); err != nil {
return err
}
if err := s.root.normalize(s.registry, &idSet{}); err != nil {
return err
}
for _, p := range s.registry.definitions {
p.init(s.registry)
}
var err error
s.parser, err = s.root.parser(s.registry, &idSet{})
if err != nil {
s.initFailed = true s.initFailed = true
return err return err
} }
s.root.init(s.registry)
s.parser = s.root.parser(s.registry)
s.builder = s.root.builder() s.builder = s.root.builder()
s.initialized = true s.initialized = true
return nil return nil
} }

View File

@ -12,9 +12,9 @@ func brokenRegistryError(err error) error {
return fmt.Errorf("broken registry: %v", err) return fmt.Errorf("broken registry: %v", err)
} }
func splitWhitespaceDefs(all map[string]definition) ([]definition, []definition) { func splitWhitespaceDefs(defs []definition) ([]definition, []definition) {
var whitespaceDefs, nonWhitespaceDefs []definition var whitespaceDefs, nonWhitespaceDefs []definition
for _, def := range all { for _, def := range defs {
if def.commitType()&Whitespace != 0 { if def.commitType()&Whitespace != 0 {
def.setCommitType(def.commitType() | Alias) def.setCommitType(def.commitType() | Alias)
whitespaceDefs = append(whitespaceDefs, def) whitespaceDefs = append(whitespaceDefs, def)
@ -54,14 +54,10 @@ func mergeWhitespaceDefs(ws []definition) definition {
return newChoice(whitespaceName, Alias, names) return newChoice(whitespaceName, Alias, names)
} }
// TODO: validate min and max
func patchName(s ...string) string { func patchName(s ...string) string {
return strings.Join(s, ":") return strings.Join(s, ":")
} }
// TODO: check what's more useful: update quantified char classes or not
func applyWhitespaceToSeq(s *sequenceDefinition) []definition { func applyWhitespaceToSeq(s *sequenceDefinition) []definition {
var ( var (
defs []definition defs []definition
@ -113,7 +109,7 @@ func applyWhitespaceToSeq(s *sequenceDefinition) []definition {
return defs return defs
} }
func applyWhitespace(defs []definition) []definition { func applyWhitespaceToDefs(defs []definition) []definition {
var defsWS []definition var defsWS []definition
for _, def := range defs { for _, def := range defs {
if def.commitType()&NoWhitespace != 0 { if def.commitType()&NoWhitespace != 0 {
@ -133,7 +129,7 @@ func applyWhitespace(defs []definition) []definition {
return defsWS return defsWS
} }
func applyWhitespaceRoot(root definition) (definition, definition) { func applyWhitespaceToRoot(root definition) (definition, definition) {
original, name := root, root.nodeName() original, name := root, root.nodeName()
wsName := patchName(name, "wsroot") wsName := patchName(name, "wsroot")
@ -158,30 +154,34 @@ func applyWhitespaceRoot(root definition) (definition, definition) {
return original, root return original, root
} }
func registerPatched(r *registry, defs ...definition) { func hasWhitespace(defs []definition) bool {
for _, def := range defs { for i := range defs {
if err := r.setDefinition(def); err != nil { if defs[i].commitType()&Whitespace != 0 {
panic(brokenRegistryError(err)) return true
} }
} }
return false
} }
func initWhitespace(r *registry) *registry { func applyWhitespace(defs []definition) ([]definition, definition) {
whitespaceDefs, defs := splitWhitespaceDefs(r.definitions) whitespaceDefs, defs := splitWhitespaceDefs(defs)
if len(whitespaceDefs) == 0 {
return r
}
whitespace := mergeWhitespaceDefs(whitespaceDefs) whitespace := mergeWhitespaceDefs(whitespaceDefs)
defs = applyWhitespace(defs)
defs = applyWhitespaceToDefs(defs)
root, defs := splitRoot(defs) root, defs := splitRoot(defs)
originalRoot, root := applyWhitespaceRoot(root) originalRoot, root := applyWhitespaceToRoot(root)
r = newRegistry() defs = append(
registerPatched(r, whitespace) append(
registerPatched(r, whitespaceDefs...) defs,
registerPatched(r, defs...) whitespaceDefs...,
registerPatched(r, originalRoot, root) ),
return r whitespace,
originalRoot,
root,
)
return defs, root
} }