diff --git a/common/errors.go b/common/errors.go new file mode 100644 index 0000000..bac098b --- /dev/null +++ b/common/errors.go @@ -0,0 +1,25 @@ +package common + +import ( + protocol "github.com/tliron/glsp/protocol_3_16" +) + +type LSPError struct { + Range LocationRange + Err error +} + +func (l LSPError) ToDiagnostic() protocol.Diagnostic { + return protocol.Diagnostic{ + Range: l.Range.ToLSPRange(), + Message: l.Err.Error(), + } +} + +type SyntaxError struct { + Message string +} + +func (s SyntaxError) Error() string { + return s.Message +} diff --git a/common/location.go b/common/location.go new file mode 100644 index 0000000..fd20677 --- /dev/null +++ b/common/location.go @@ -0,0 +1,65 @@ +package common + +import protocol "github.com/tliron/glsp/protocol_3_16" + +type Location struct { + Line uint32 + Character uint32 +} + +type LocationRange struct { + Start Location + End Location +} + +func (l LocationRange) ToLSPRange() protocol.Range { + return protocol.Range{ + Start: protocol.Position{ + Line: l.Start.Line, + Character: l.Start.Character, + }, + End: protocol.Position{ + Line: l.End.Line, + Character: l.End.Character, + }, + } +} + +func (l *LocationRange) ChangeBothLines(newLine uint32) { + l.Start.Line = newLine + l.End.Line = newLine +} + +func (l LocationRange) ContainsCursor(line uint32, character uint32) bool { + return line == l.Start.Line && character >= l.Start.Character && character <= l.End.Character +} + +func (l LocationRange) ContainsCursorByCharacter(character uint32) bool { + return character >= l.Start.Character && character <= l.End.Character +} + +func CreateFullLineRange(line uint32) LocationRange { + return LocationRange{ + Start: Location{ + Line: line, + Character: 0, + }, + End: Location{ + Line: line, + Character: 999999, + }, + } +} + +func CreateSingleCharRange(line uint32, character uint32) LocationRange { + return LocationRange{ + Start: Location{ + Line: line, + Character: character, + }, + End: Location{ + Line: line, + Character: character, + }, + } +} diff --git a/config-lsp b/config-lsp deleted file mode 100755 index 9b34894..0000000 Binary files a/config-lsp and /dev/null differ diff --git a/doc-values/extra-values.go b/doc-values/extra-values.go index 3a47e9c..d804c31 100644 --- a/doc-values/extra-values.go +++ b/doc-values/extra-values.go @@ -3,6 +3,7 @@ package docvalues import ( "config-lsp/utils" "os" + "regexp" "strings" ) @@ -173,3 +174,9 @@ func SingleEnumValue(value string) EnumValue { }, } } + +func DomainValue() Value { + return RegexValue{ + Regex: *regexp.MustCompile(`^.+?\..+$`), + } +} diff --git a/go.mod b/go.mod index 7d7476f..0340385 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( ) require ( + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/iancoleman/strcase v0.3.0 // indirect diff --git a/go.sum b/go.sum index b884e4f..4934342 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/handlers/hosts/Hosts.g4 b/handlers/hosts/Hosts.g4 new file mode 100644 index 0000000..7690ee3 --- /dev/null +++ b/handlers/hosts/Hosts.g4 @@ -0,0 +1,126 @@ +grammar Hosts; + +lineStatement + : SEPARATOR? entry SEPARATOR? leadingComment? EOF + ; + +entry + : ipAddress SEPARATOR hostname (SEPARATOR aliases)? + ; + +aliases + : (alias SEPARATOR?)+ + ; + +alias + : DOMAIN + ; + +hostname + : domain + ; + +domain + : DOMAIN + ; + +ipAddress + : (ipv4Address | ipv6Address) + ; + +ipv4Address + : singleIPv4Address + // Allow optional range to tell user ranges are not allowed + ipRange? + ; + +singleIPv4Address + : ipv4Digit DOT ipv4Digit DOT ipv4Digit DOT ipv4Digit + ; + +// This is not correct but fits for now +ipv6Address + : singleIPv6Address + // Allow optional range to tell user ranges are not allowed + ipRange? + ; + +singleIPv6Address + : (ipv6Octet COLON)+ ipv6Octet + ; + +ipv4Digit + : DIGITS + ; + +ipv6Octet + : OCTETS + ; + +ipRange + : SLASH ipRangeBits + ; + +ipRangeBits + : DIGITS + ; + +comment + : COMMENTLINE + ; + +leadingComment + : COMMENTLINE + ; + +COMMENTLINE + : HASHTAG ~[\r\n]+ + ; + +SLASH + : '/' + ; + +DOT + : '.' + ; + +COLON + : ':' + ; + +HASHTAG + : '#' + ; + +SEPARATOR + : [ \t]+ + ; + +NEWLINE + : [\r]? [\n] + ; + +DIGITS + : DIGIT+ + ; + +fragment DIGIT + : [0-9] + ; + +OCTETS + : OCTET+ + ; + +fragment OCTET + : [0-9a-fA-F] + ; + +DOMAIN + : ((STRING)+ (DOT [a-zA-Z]+)*) + ; + +fragment STRING + : ~(' ' | '\t' | '\n' | '\r' | '#' | '.') + ; diff --git a/handlers/hosts/fields/documentation-fields.go b/handlers/hosts/fields/documentation-fields.go new file mode 100644 index 0000000..83837a8 --- /dev/null +++ b/handlers/hosts/fields/documentation-fields.go @@ -0,0 +1,13 @@ +package fields + +import docvalues "config-lsp/doc-values" + +var IPAddressField = docvalues.IPAddressValue{ + AllowIPv4: true, + AllowIPv6: true, +} + +var HostnameField = docvalues.DocumentationValue{ + Documentation: `Host names may contain only alphanumeric characters, minus signs ("-"), and periods ("."). They must begin with an alphabetic character and end with an alphanumeric character. Optional aliases provide for name changes, alternate spellings, shorter hostnames, or generic hostnames (for example, localhost).`, + Value: docvalues.DomainValue(), +} diff --git a/handlers/hosts/handlers/analyzer/analyzer.go b/handlers/hosts/handlers/analyzer/analyzer.go new file mode 100644 index 0000000..3f512b3 --- /dev/null +++ b/handlers/hosts/handlers/analyzer/analyzer.go @@ -0,0 +1,42 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/utils" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func Analyze(parser *HostsParser) []protocol.Diagnostic { + errors := analyzeEntriesSetCorrectly(*parser) + + if len(errors) > 0 { + return utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + ) + } + + errors = analyzeEntriesAreValid(*parser) + + if len(errors) > 0 { + return utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + ) + } + + errors = append(errors, analyzeDoubleIPs(parser)...) + errors = append(errors, analyzeDoubleHostNames(parser)...) + + return utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + ) +} diff --git a/handlers/hosts/handlers/analyzer/double_ips.go b/handlers/hosts/handlers/analyzer/double_ips.go new file mode 100644 index 0000000..b3a01e7 --- /dev/null +++ b/handlers/hosts/handlers/analyzer/double_ips.go @@ -0,0 +1,39 @@ +package analyzer + +import ( + "config-lsp/common" + "net" +) + +func ipToString(ip net.IPAddr) string { + return ip.IP.String() +} + +func analyzeDoubleIPs(p *HostsParser) []common.LSPError { + errors := make([]common.LSPError, 0) + ips := make(map[string]uint32) + + p.DoubleIPs = make(map[uint32]DuplicateIPDeclaration) + + for lineNumber, entry := range p.Tree.Entries { + if entry.IPAddress != nil { + key := ipToString(entry.IPAddress.Value) + + if foundLine, found := ips[key]; found { + err := DuplicateIPDeclaration{ + AlreadyFoundAt: foundLine, + } + + p.DoubleIPs[lineNumber] = err + errors = append(errors, common.LSPError{ + Range: entry.IPAddress.Location, + Err: err, + }) + } else { + ips[key] = lineNumber + } + } + } + + return errors +} diff --git a/handlers/hosts/handlers/analyzer/double_ips_test.go b/handlers/hosts/handlers/analyzer/double_ips_test.go new file mode 100644 index 0000000..979dd0b --- /dev/null +++ b/handlers/hosts/handlers/analyzer/double_ips_test.go @@ -0,0 +1,61 @@ +package analyzer + +import ( + "config-lsp/utils" + "testing" +) + +func TestWorksWithNonDoubleIPs( + t *testing.T, +) { + input := utils.Dedent(` +1.2.3.4 hello.com +5.5.5.5 world.com +1.2.3.5 foo.com +1.2.3.6 bar.com +`) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors) + } + + errors = analyzeDoubleIPs(&parser) + + if len(errors) != 0 { + t.Errorf("Expected no errors, but got %v", errors) + } +} + +func TestWorksWithDoubleIPs( + t *testing.T, +) { + input := utils.Dedent(` +1.2.3.4 hello.com +5.5.5.5 world.com +1.2.3.4 foo.com +`) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors) + } + + errors = analyzeDoubleIPs(&parser) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, but got %v", len(errors)) + } + + if !(errors[0].Range.Start.Line == 2) { + t.Errorf("Expected error on line 2, but got %v", errors[0].Range.Start.Line) + } + + if !(errors[0].Err.(DuplicateIPDeclaration).AlreadyFoundAt == 0) { + t.Errorf("Expected error on line 0, but got %v", errors[0].Err.(DuplicateIPDeclaration).AlreadyFoundAt) + } +} diff --git a/handlers/hosts/handlers/analyzer/errors.go b/handlers/hosts/handlers/analyzer/errors.go new file mode 100644 index 0000000..9b9a7ff --- /dev/null +++ b/handlers/hosts/handlers/analyzer/errors.go @@ -0,0 +1,20 @@ +package analyzer + +import "fmt" + +type DuplicateHostEntry struct { + AlreadyFoundAt uint32 + Hostname string +} + +func (d DuplicateHostEntry) Error() string { + return fmt.Sprintf("'%s' already defined on line %d", d.Hostname, d.AlreadyFoundAt+1) +} + +type DuplicateIPDeclaration struct { + AlreadyFoundAt uint32 +} + +func (d DuplicateIPDeclaration) Error() string { + return fmt.Sprintf("This IP address is already defined on line %d", d.AlreadyFoundAt+1) +} diff --git a/handlers/hosts/handlers/analyzer/handler.go b/handlers/hosts/handlers/analyzer/handler.go new file mode 100644 index 0000000..33adbcd --- /dev/null +++ b/handlers/hosts/handlers/analyzer/handler.go @@ -0,0 +1,82 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/handlers/hosts/parser" + "config-lsp/utils" + "regexp" + + "github.com/antlr4-go/antlr/v4" +) + +func (p *HostsParser) Clear() { + p.Tree = HostsTree{ + Entries: make(map[uint32]*HostsEntry), + } + p.CommentLines = make(map[uint32]struct{}) +} + +var commentPattern = regexp.MustCompile(`^\s*#.*$`) +var emptyPattern = regexp.MustCompile(`^\s*$`) + +func (p *HostsParser) parseStatement( + line uint32, + input string, +) []common.LSPError { + stream := antlr.NewInputStream(input) + + errorListener := createErrorListener(line) + lexer := parser.NewHostsLexer(stream) + lexer.RemoveErrorListeners() + lexer.AddErrorListener(&errorListener) + + errors := errorListener.Errors + + errorListener = createErrorListener(line) + tokenStream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) + antlrParser := parser.NewHostsParser(tokenStream) + antlrParser.RemoveErrorListeners() + antlrParser.AddErrorListener(&errorListener) + + listener := createHostsFileListener(p, line) + antlr.ParseTreeWalkerDefault.Walk( + &listener, + antlrParser.LineStatement(), + ) + + errors = append(errors, errorListener.Errors...) + + return errors +} + +func (p *HostsParser) Parse(input string) []common.LSPError { + errors := make([]common.LSPError, 0) + lines := utils.SplitIntoLines(input) + + for rawLineNumber, line := range lines { + lineNumber := uint32(rawLineNumber) + + if commentPattern.MatchString(line) { + p.CommentLines[lineNumber] = struct{}{} + continue + } + + if emptyPattern.MatchString(line) { + continue + } + + errors = append( + errors, + p.parseStatement(lineNumber, line)..., + ) + } + + return errors +} + +func CreateNewHostsParser() HostsParser { + p := HostsParser{} + p.Clear() + + return p +} diff --git a/handlers/hosts/handlers/analyzer/handler_test.go b/handlers/hosts/handlers/analyzer/handler_test.go new file mode 100644 index 0000000..3f1b00f --- /dev/null +++ b/handlers/hosts/handlers/analyzer/handler_test.go @@ -0,0 +1,144 @@ +package analyzer + +import ( + "config-lsp/utils" + "net" + "testing" +) + +func TestValidSimpleExampleWorks( + t *testing.T, +) { + input := utils.Dedent(` +1.2.3.4 hello.com + `) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Errorf("Expected no errors, but got %v", errors) + } + + if !(len(parser.Tree.Entries) == 1) { + t.Errorf("Expected 1 entry, but got %v", len(parser.Tree.Entries)) + } + + if parser.Tree.Entries[0].IPAddress == nil { + t.Errorf("Expected IP address to be present, but got nil") + } + + if !(parser.Tree.Entries[0].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) { + t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[0].IPAddress.Value) + } + + if !(parser.Tree.Entries[0].Hostname.Value == "hello.com") { + t.Errorf("Expected hostname to be hello.com, but got %v", parser.Tree.Entries[0].Hostname.Value) + } + + if !(parser.Tree.Entries[0].Aliases == nil) { + t.Errorf("Expected no aliases, but got %v", parser.Tree.Entries[0].Aliases) + } + + if !(parser.Tree.Entries[0].Location.Start.Line == 0) { + t.Errorf("Expected line to be 1, but got %v", parser.Tree.Entries[0].Location.Start.Line) + } + + if !(parser.Tree.Entries[0].Location.Start.Character == 0) { + t.Errorf("Expected start to be 0, but got %v", parser.Tree.Entries[0].Location.Start) + } + + if !(parser.Tree.Entries[0].Location.End.Character == 17) { + t.Errorf("Expected end to be 17, but got %v", parser.Tree.Entries[0].Location.End.Character) + } + + if !(parser.Tree.Entries[0].IPAddress.Location.Start.Line == 0) { + t.Errorf("Expected IP address line to be 1, but got %v", parser.Tree.Entries[0].IPAddress.Location.Start.Line) + } + + if !(parser.Tree.Entries[0].IPAddress.Location.Start.Character == 0) { + t.Errorf("Expected IP address start to be 0, but got %v", parser.Tree.Entries[0].IPAddress.Location.Start.Character) + } + + if !(parser.Tree.Entries[0].IPAddress.Location.End.Character == 7) { + t.Errorf("Expected IP address end to be 7, but got %v", parser.Tree.Entries[0].IPAddress.Location.End.Character) + } + + if !(len(parser.CommentLines) == 0) { + t.Errorf("Expected no comment lines, but got %v", len(parser.CommentLines)) + } +} + +func TestValidComplexExampleWorks( + t *testing.T, +) { + input := utils.Dedent(` + +# This is a comment +1.2.3.4 hello.com test.com # This is another comment +5.5.5.5 test.com +1.2.3.4 example.com check.com +`) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Errorf("Expected no errors, but got %v", errors) + } + + if !(len(parser.Tree.Entries) == 3) { + t.Errorf("Expected 3 entries, but got %v", len(parser.Tree.Entries)) + } + + if parser.Tree.Entries[2].IPAddress == nil { + t.Errorf("Expected IP address to be present, but got nil") + } + + if !(parser.Tree.Entries[2].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) { + t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[2].IPAddress.Value) + } + + if !(len(parser.CommentLines) == 1) { + t.Errorf("Expected 1 comment line, but got %v", len(parser.CommentLines)) + } + + if !(utils.KeyExists(parser.CommentLines, 1)) { + t.Errorf("Expected comment line 2 to exist, but it does not") + } +} + +func TestInvalidExampleWorks( + t *testing.T, +) { + input := utils.Dedent(` +1.2.3.4 + `) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) == 0 { + t.Errorf("Expected errors, but got none") + } + + if !(len(parser.Tree.Entries) == 1) { + t.Errorf("Expected 1 entries, but got %v", len(parser.Tree.Entries)) + } + + if !(len(parser.CommentLines) == 0) { + t.Errorf("Expected no comment lines, but got %v", len(parser.CommentLines)) + } + + if !(parser.Tree.Entries[0].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) { + t.Errorf("Expected IP address to be nil, but got %v", parser.Tree.Entries[0].IPAddress) + } + + if !(parser.Tree.Entries[0].Hostname == nil) { + t.Errorf("Expected hostname to be nil, but got %v", parser.Tree.Entries[0].Hostname) + } + + if !(parser.Tree.Entries[0].Aliases == nil) { + t.Errorf("Expected aliases to be nil, but got %v", parser.Tree.Entries[0].Aliases) + } +} diff --git a/handlers/hosts/handlers/analyzer/hosts.go b/handlers/hosts/handlers/analyzer/hosts.go new file mode 100644 index 0000000..41e2739 --- /dev/null +++ b/handlers/hosts/handlers/analyzer/hosts.go @@ -0,0 +1,77 @@ +package analyzer + +import ( + "config-lsp/common" + "fmt" + "net" + + "github.com/antlr4-go/antlr/v4" +) + +func characterRangeFromCtx( + ctx antlr.BaseParserRuleContext, +) common.LocationRange { + line := uint32(ctx.GetStart().GetLine()) + start := uint32(ctx.GetStart().GetStart()) + end := uint32(ctx.GetStop().GetStop()) + + return common.LocationRange{ + Start: common.Location{ + Line: line, + Character: start, + }, + End: common.Location{ + Line: line, + Character: end + 1, + }, + } +} + +type HostsParser struct { + Tree HostsTree + CommentLines map[uint32]struct{} + Resolver *Resolver + // [line]error + DoubleIPs map[uint32]DuplicateIPDeclaration +} + +type HostsTree struct { + // [line]entry + Entries map[uint32]*HostsEntry +} + +type HostsEntry struct { + Location common.LocationRange + + IPAddress *HostsIPAddress + Hostname *HostsHostname + Aliases []*HostsHostname +} + +func (p HostsEntry) String() string { + str := fmt.Sprintf("HostsEntry(%v)", p.Location) + + if p.IPAddress != nil { + str += " " + p.IPAddress.Value.String() + } + + if p.Hostname != nil { + str += " " + p.Hostname.Value + } + + if p.Aliases != nil { + str += " " + fmt.Sprintf("%v", p.Aliases) + } + + return str +} + +type HostsIPAddress struct { + Location common.LocationRange + Value net.IPAddr +} + +type HostsHostname struct { + Location common.LocationRange + Value string +} diff --git a/handlers/hosts/handlers/analyzer/listener.go b/handlers/hosts/handlers/analyzer/listener.go new file mode 100644 index 0000000..9409533 --- /dev/null +++ b/handlers/hosts/handlers/analyzer/listener.go @@ -0,0 +1,152 @@ +package analyzer + +import ( + "config-lsp/common" + docvalues "config-lsp/doc-values" + "config-lsp/handlers/hosts/parser" + "net" + + "github.com/antlr4-go/antlr/v4" +) + +type hostsListenerContext struct { + line uint32 +} + +type hostsParserListener struct { + *parser.BaseHostsListener + Parser *HostsParser + Errors []common.LSPError + hostsContext hostsListenerContext +} + +func (s *hostsParserListener) EnterComment(ctx *parser.CommentContext) { + line := uint32(s.hostsContext.line) + s.Parser.CommentLines[line] = struct{}{} +} + +func (s *hostsParserListener) EnterEntry(ctx *parser.EntryContext) { + location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.hostsContext.line) + + s.Parser.Tree.Entries[location.Start.Line] = &HostsEntry{ + Location: location, + } +} + +func (s *hostsParserListener) EnterIpAddress(ctx *parser.IpAddressContext) { + location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.hostsContext.line) + + ip := net.ParseIP(ctx.GetText()) + + if ip == nil { + s.Errors = append(s.Errors, common.LSPError{ + Range: location, + Err: docvalues.InvalidIPAddress{}, + }) + return + } + + ipAddr, err := net.ResolveIPAddr("ip", ip.String()) + + if err != nil { + s.Errors = append(s.Errors, common.LSPError{ + Range: location, + Err: docvalues.InvalidIPAddress{}, + }) + } + + entry := s.Parser.Tree.Entries[location.Start.Line] + + entry.IPAddress = &HostsIPAddress{ + Location: location, + Value: *ipAddr, + } +} + +func (s *hostsParserListener) EnterHostname(ctx *parser.HostnameContext) { + location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.hostsContext.line) + + entry := s.Parser.Tree.Entries[location.Start.Line] + + entry.Hostname = &HostsHostname{ + Location: location, + Value: ctx.GetText(), + } + + s.Parser.Tree.Entries[location.Start.Line] = entry +} + +func (s *hostsParserListener) EnterAliases(ctx *parser.AliasesContext) { + location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.hostsContext.line) + + entry := s.Parser.Tree.Entries[location.Start.Line] + + aliases := make([]*HostsHostname, 0) + + entry.Aliases = aliases +} + +func (s *hostsParserListener) EnterAlias(ctx *parser.AliasContext) { + location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.hostsContext.line) + + entry := s.Parser.Tree.Entries[location.Start.Line] + + alias := HostsHostname{ + Location: location, + Value: ctx.GetText(), + } + + entry.Aliases = append(entry.Aliases, &alias) +} + +func createHostsFileListener( + parser *HostsParser, + line uint32, +) hostsParserListener { + return hostsParserListener{ + Parser: parser, + hostsContext: hostsListenerContext{ + line: line, + }, + Errors: make([]common.LSPError, 0), + } +} + +type errorListener struct { + *antlr.DefaultErrorListener + Errors []common.LSPError + hostsContext hostsListenerContext +} + +func createErrorListener( + line uint32, +) errorListener { + return errorListener{ + Errors: make([]common.LSPError, 0), + hostsContext: hostsListenerContext{ + line: line, + }, + } +} + +func (d *errorListener) SyntaxError( + recognizer antlr.Recognizer, + offendingSymbol interface{}, + _ int, + character int, + message string, + error antlr.RecognitionException, +) { + line := d.hostsContext.line + d.Errors = append(d.Errors, common.LSPError{ + Range: common.CreateSingleCharRange(uint32(line), uint32(character)), + Err: common.SyntaxError{ + Message: message, + }, + }) +} diff --git a/handlers/hosts/handlers/analyzer/resolver.go b/handlers/hosts/handlers/analyzer/resolver.go new file mode 100644 index 0000000..c06ce96 --- /dev/null +++ b/handlers/hosts/handlers/analyzer/resolver.go @@ -0,0 +1,108 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/utils" + "net" +) + +type ResolverEntry struct { + IPv4Address net.IP + IPv6Address net.IP + Line uint32 +} + +func (e ResolverEntry) GetInfo() string { + if e.IPv4Address != nil { + return e.IPv4Address.String() + } + + return e.IPv6Address.String() +} + +type Resolver struct { + Entries map[string]ResolverEntry +} + +func createEntry( + line uint32, + ip net.IP, +) ResolverEntry { + entry := ResolverEntry{ + Line: line, + } + + if ipv4 := ip.To4(); ipv4 != nil { + entry.IPv4Address = ipv4 + } else if ipv6 := ip.To16(); ipv6 != nil { + entry.IPv6Address = ipv6 + } + + return entry +} + +type hostnameEntry struct { + Location common.LocationRange + HostName string +} + +func createResolverFromParser(p HostsParser) (Resolver, []common.LSPError) { + errors := make([]common.LSPError, 0) + resolver := Resolver{ + Entries: make(map[string]ResolverEntry), + } + + for lineNumber, entry := range p.Tree.Entries { + if entry.IPAddress != nil && entry.Hostname != nil { + hostNames := append( + []hostnameEntry{ + { + Location: entry.Hostname.Location, + HostName: entry.Hostname.Value, + }, + }, + utils.Map( + entry.Aliases, + func(alias *HostsHostname) hostnameEntry { + return hostnameEntry{ + Location: alias.Location, + HostName: alias.Value, + } + }, + )..., + ) + + for _, hostName := range hostNames { + entry := createEntry( + lineNumber, + entry.IPAddress.Value.IP, + ) + + if resolv, found := resolver.Entries[hostName.HostName]; found { + errors = append( + errors, + common.LSPError{ + Range: hostName.Location, + Err: DuplicateHostEntry{ + AlreadyFoundAt: resolv.Line, + Hostname: hostName.HostName, + }, + }, + ) + } else { + resolver.Entries[hostName.HostName] = entry + } + } + } + } + + return resolver, errors +} + +func analyzeDoubleHostNames(p *HostsParser) []common.LSPError { + resolver, errors := createResolverFromParser(*p) + + p.Resolver = &resolver + + return errors +} diff --git a/handlers/hosts/handlers/analyzer/resolver_test.go b/handlers/hosts/handlers/analyzer/resolver_test.go new file mode 100644 index 0000000..f2a7247 --- /dev/null +++ b/handlers/hosts/handlers/analyzer/resolver_test.go @@ -0,0 +1,116 @@ +package analyzer + +import ( + "config-lsp/utils" + "testing" +) + +func TestResolverEntriesWorksWithNonOverlapping( + t *testing.T, +) { + input := utils.Dedent(` +1.2.3.4 hello.com +5.5.5.5 world.com +`) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors) + } + + resolver, errors := createResolverFromParser(parser) + + if len(errors) != 0 { + t.Errorf("Expected no errors, but got %v", errors) + } + + if len(resolver.Entries) != 2 { + t.Errorf("Expected 2 entries, but got %v", len(resolver.Entries)) + } + + if !(resolver.Entries["hello.com"].IPv4Address.String() == "1.2.3.4") { + t.Errorf("Expected hello.com to be 1.2.3.4, but got %v", resolver.Entries["hello.com"].IPv4Address) + } + + if !(resolver.Entries["world.com"].IPv4Address.String() == "5.5.5.5") { + t.Errorf("Expected world.com to be 5.5.5.5, but got %v", resolver.Entries["world.com"].IPv4Address) + } + + if !(resolver.Entries["hello.com"].IPv6Address == nil) { + t.Errorf("Expected hello.com to have no IPv6 address, but got %v", resolver.Entries["hello.com"].IPv6Address) + } + + if !(resolver.Entries["world.com"].IPv6Address == nil) { + t.Errorf("Expected world.com to have no IPv6 address, but got %v", resolver.Entries["world.com"].IPv6Address) + } +} + +func TestResolverEntriesWithSimpleOverlapping( + t *testing.T, +) { + input := utils.Dedent(` +1.2.3.4 hello.com +5.5.5.5 hello.com +`) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors) + } + + resolver, errors := createResolverFromParser(parser) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, but got %v", len(errors)) + } + + if len(resolver.Entries) != 1 { + t.Errorf("Expected 1 entry, but got %v", len(resolver.Entries)) + } + + if !(resolver.Entries["hello.com"].IPv4Address.String() == "1.2.3.4") { + t.Errorf("Expected hello.com to be 1.2.3.4, but got %v", resolver.Entries["hello.com"].IPv4Address) + } +} + +func TestResolverEntriesWithComplexOverlapping( + t *testing.T, +) { + input := utils.Dedent(` +1.2.3.4 hello.com test.com +5.5.5.5 check.com test.com +`) + + parser := CreateNewHostsParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors) + } + + resolver, errors := createResolverFromParser(parser) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, but got %v", len(errors)) + } + + if len(resolver.Entries) != 3 { + t.Errorf("Expected 3 entries, but got %v", len(resolver.Entries)) + } + + if !(resolver.Entries["hello.com"].IPv4Address.String() == "1.2.3.4") { + t.Errorf("Expected hello.com to be 1.2.3.4, but got %v", resolver.Entries["hello.com"].IPv4Address) + } + + if !(resolver.Entries["check.com"].IPv4Address.String() == "5.5.5.5") { + t.Errorf("Expected check.com to be 5.5.5.5, but got %v", resolver.Entries["check.com"].IPv4Address) + } + + if !(resolver.Entries["test.com"].IPv4Address.String() == "1.2.3.4") { + t.Errorf("Expected test.com to have no IPv4 address, but got %v", resolver.Entries["test.com"].IPv4Address) + } +} diff --git a/handlers/hosts/handlers/analyzer/values.go b/handlers/hosts/handlers/analyzer/values.go new file mode 100644 index 0000000..d04c415 --- /dev/null +++ b/handlers/hosts/handlers/analyzer/values.go @@ -0,0 +1,86 @@ +package analyzer + +import ( + "config-lsp/common" + docvalues "config-lsp/doc-values" + "config-lsp/handlers/hosts/fields" + "config-lsp/utils" + "errors" +) + +func analyzeEntriesSetCorrectly( + parser HostsParser, +) []common.LSPError { + err := make([]common.LSPError, 0) + + for lineNumber, entry := range parser.Tree.Entries { + if entry.IPAddress == nil { + err = append(err, common.LSPError{ + Range: common.CreateFullLineRange(lineNumber), + Err: errors.New("IP Address is required"), + }) + continue + } + + if entry.Hostname == nil { + err = append(err, common.LSPError{ + Range: common.CreateFullLineRange(lineNumber), + Err: errors.New("Hostname is required"), + }) + continue + } + } + + return err +} + +func analyzeEntriesAreValid( + parser HostsParser, +) []common.LSPError { + err := make([]common.LSPError, 0) + + for _, entry := range parser.Tree.Entries { + err = append( + err, + utils.Map( + fields.IPAddressField.CheckIsValid(entry.IPAddress.Value.String()), + func(val *docvalues.InvalidValue) common.LSPError { + return common.LSPError{ + Range: entry.IPAddress.Location, + Err: val.Err, + } + }, + )..., + ) + + err = append( + err, + utils.Map( + fields.HostnameField.CheckIsValid(entry.Hostname.Value), + func(val *docvalues.InvalidValue) common.LSPError { + return common.LSPError{ + Range: entry.Hostname.Location, + Err: val.Err, + } + }, + )..., + ) + + for _, alias := range entry.Aliases { + err = append( + err, + utils.Map( + fields.HostnameField.CheckIsValid(alias.Value), + func(val *docvalues.InvalidValue) common.LSPError { + return common.LSPError{ + Range: alias.Location, + Err: val.Err, + } + }, + )..., + ) + } + } + + return err +} diff --git a/handlers/hosts/handlers/code-actions.go b/handlers/hosts/handlers/code-actions.go new file mode 100644 index 0000000..7de425a --- /dev/null +++ b/handlers/hosts/handlers/code-actions.go @@ -0,0 +1,105 @@ +package handlers + +import ( + "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/utils" + "fmt" + "strings" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +type CodeActionName string + +const ( + CodeActionInlineAliases CodeActionName = "inlineAliases" +) + +type CodeAction interface { + RunCommand(analyzer.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) +} + +type CodeActionArgs interface{} + +type CodeActionInlineAliasesArgs struct { + URI protocol.DocumentUri + FromLine uint32 + ToLine uint32 +} + +func CodeActionInlineAliasesArgsFromArguments(arguments map[string]any) CodeActionInlineAliasesArgs { + return CodeActionInlineAliasesArgs{ + URI: arguments["URI"].(protocol.DocumentUri), + FromLine: uint32(arguments["FromLine"].(float64)), + ToLine: uint32(arguments["ToLine"].(float64)), + } +} + +func (args CodeActionInlineAliasesArgs) RunCommand(hostsParser analyzer.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) { + fromEntry := hostsParser.Tree.Entries[args.FromLine] + toEntry := hostsParser.Tree.Entries[args.ToLine] + + if fromEntry == nil || toEntry == nil { + // Weird + return nil, nil + } + + var insertCharacter uint32 + + if toEntry.Aliases != nil { + insertCharacter = toEntry.Aliases[len(toEntry.Aliases)-1].Location.End.Character + } else { + insertCharacter = toEntry.Hostname.Location.End.Character + } + + hostnames := append( + []string{ + fromEntry.Hostname.Value, + }, + utils.Map( + fromEntry.Aliases, + func(alias *analyzer.HostsHostname) string { + return alias.Value + }, + )..., + ) + + label := fmt.Sprintf("Inline aliases from %d to %d", args.FromLine, args.ToLine) + return &protocol.ApplyWorkspaceEditParams{ + Label: &label, + Edit: protocol.WorkspaceEdit{ + Changes: map[protocol.DocumentUri][]protocol.TextEdit{ + args.URI: { + // Delete old line + { + Range: protocol.Range{ + Start: protocol.Position{ + Line: args.FromLine, + Character: 0, + }, + End: protocol.Position{ + Line: args.FromLine + 1, + Character: 0, + }, + }, + NewText: "", + }, + // Insert aliases + { + Range: protocol.Range{ + Start: protocol.Position{ + Line: args.ToLine, + Character: insertCharacter, + }, + End: protocol.Position{ + Line: args.ToLine, + Character: insertCharacter, + }, + }, + NewText: " " + strings.Join(hostnames, " "), + }, + }, + }, + }, + }, nil +} diff --git a/handlers/hosts/handlers/fetch-code-actions.go b/handlers/hosts/handlers/fetch-code-actions.go new file mode 100644 index 0000000..6374c5e --- /dev/null +++ b/handlers/hosts/handlers/fetch-code-actions.go @@ -0,0 +1,37 @@ +package handlers + +import ( + "config-lsp/handlers/hosts/handlers/analyzer" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func GetInlineAliasesCodeAction( + p analyzer.HostsParser, + params *protocol.CodeActionParams, +) []protocol.CodeAction { + line := params.Range.Start.Line + + if duplicateInfo, found := p.DoubleIPs[line]; found { + commandID := "hosts." + CodeActionInlineAliases + command := protocol.Command{ + Title: "Inline Aliases", + Command: string(commandID), + Arguments: []any{ + CodeActionInlineAliasesArgs{ + URI: params.TextDocument.URI, + FromLine: line, + ToLine: duplicateInfo.AlreadyFoundAt, + }, + }, + } + + return []protocol.CodeAction{ + { + Title: "Inline Aliases", + Command: &command, + }, + } + } + + return []protocol.CodeAction{} +} diff --git a/handlers/hosts/handlers/hover.go b/handlers/hosts/handlers/hover.go new file mode 100644 index 0000000..168ba47 --- /dev/null +++ b/handlers/hosts/handlers/hover.go @@ -0,0 +1,50 @@ +package handlers + +import ( + "config-lsp/handlers/hosts/handlers/analyzer" + "fmt" +) + +type HoverTarget string + +const ( + HoverTargetIPAddress HoverTarget = "ip_address" + HoverTargetHostname HoverTarget = "hostname" + HoverTargetAlias HoverTarget = "alias" +) + +func GetHoverTargetInEntry( + e analyzer.HostsEntry, + cursor uint32, +) *HoverTarget { + if e.IPAddress != nil && e.IPAddress.Location.ContainsCursorByCharacter(cursor) { + target := HoverTargetIPAddress + return &target + } + + if e.Hostname != nil && e.Hostname.Location.ContainsCursorByCharacter(cursor) { + target := HoverTargetHostname + return &target + } + + for _, alias := range e.Aliases { + if alias.Location.ContainsCursorByCharacter(cursor) { + target := HoverTargetAlias + return &target + } + } + + return nil +} + +func GetHoverInfoForHostname( + parser analyzer.HostsParser, + hostname analyzer.HostsHostname, + cursor uint32, +) []string { + ipAddress := parser.Resolver.Entries[hostname.Value] + + return []string{ + fmt.Sprintf("**%s** maps to _%s_", hostname.Value, ipAddress.GetInfo()), + } +} diff --git a/handlers/hosts/lsp/shared.go b/handlers/hosts/lsp/shared.go new file mode 100644 index 0000000..c231057 --- /dev/null +++ b/handlers/hosts/lsp/shared.go @@ -0,0 +1,9 @@ +package lsp + +import ( + "config-lsp/handlers/hosts/handlers/analyzer" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +var documentParserMap = map[protocol.DocumentUri]*analyzer.HostsParser{} diff --git a/handlers/hosts/lsp/text-document-code-action.go b/handlers/hosts/lsp/text-document-code-action.go new file mode 100644 index 0000000..d15740e --- /dev/null +++ b/handlers/hosts/lsp/text-document-code-action.go @@ -0,0 +1,22 @@ +package lsp + +import ( + "config-lsp/handlers/hosts/handlers" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) { + parser := documentParserMap[params.TextDocument.URI] + + actions := make([]protocol.CodeAction, 0, 1) + + actions = append(actions, handlers.GetInlineAliasesCodeAction(*parser, params)...) + + if len(actions) > 0 { + return actions, nil + } + + return nil, nil +} diff --git a/handlers/hosts/lsp/text-document-completion.go b/handlers/hosts/lsp/text-document-completion.go new file mode 100644 index 0000000..8c0754b --- /dev/null +++ b/handlers/hosts/lsp/text-document-completion.go @@ -0,0 +1,12 @@ +package lsp + +import ( + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) { + // p := documentParserMap[params.TextDocument.URI] + + return nil, nil +} diff --git a/handlers/hosts/lsp/text-document-did-change.go b/handlers/hosts/lsp/text-document-did-change.go new file mode 100644 index 0000000..4aa141e --- /dev/null +++ b/handlers/hosts/lsp/text-document-did-change.go @@ -0,0 +1,41 @@ +package lsp + +import ( + "config-lsp/common" + "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/utils" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDidChange( + context *glsp.Context, + params *protocol.DidChangeTextDocumentParams, +) error { + content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text + common.ClearDiagnostics(context, params.TextDocument.URI) + + parser := documentParserMap[params.TextDocument.URI] + parser.Clear() + + diagnostics := make([]protocol.Diagnostic, 0) + errors := parser.Parse(content) + + if len(errors) > 0 { + diagnostics = append(diagnostics, utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + )...) + } + + diagnostics = append(diagnostics, analyzer.Analyze(parser)...) + + if len(diagnostics) > 0 { + common.SendDiagnostics(context, params.TextDocument.URI, diagnostics) + } + + return nil +} diff --git a/handlers/hosts/lsp/text-document-did-close.go b/handlers/hosts/lsp/text-document-did-close.go new file mode 100644 index 0000000..3961658 --- /dev/null +++ b/handlers/hosts/lsp/text-document-did-close.go @@ -0,0 +1,12 @@ +package lsp + +import ( + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { + delete(documentParserMap, params.TextDocument.URI) + + return nil +} diff --git a/handlers/hosts/lsp/text-document-did-open.go b/handlers/hosts/lsp/text-document-did-open.go new file mode 100644 index 0000000..7be0663 --- /dev/null +++ b/handlers/hosts/lsp/text-document-did-open.go @@ -0,0 +1,40 @@ +package lsp + +import ( + "config-lsp/common" + "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/utils" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDidOpen( + context *glsp.Context, + params *protocol.DidOpenTextDocumentParams, +) error { + common.ClearDiagnostics(context, params.TextDocument.URI) + + parser := analyzer.CreateNewHostsParser() + documentParserMap[params.TextDocument.URI] = &parser + + errors := parser.Parse(params.TextDocument.Text) + + diagnostics := utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + ) + + diagnostics = append( + diagnostics, + analyzer.Analyze(&parser)..., + ) + + if len(diagnostics) > 0 { + common.SendDiagnostics(context, params.TextDocument.URI, diagnostics) + } + + return nil +} diff --git a/handlers/hosts/lsp/text-document-hover.go b/handlers/hosts/lsp/text-document-hover.go new file mode 100644 index 0000000..39dc2aa --- /dev/null +++ b/handlers/hosts/lsp/text-document-hover.go @@ -0,0 +1,89 @@ +package lsp + +import ( + "config-lsp/handlers/hosts/fields" + "config-lsp/handlers/hosts/handlers" + "config-lsp/handlers/hosts/handlers/analyzer" + "strings" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentHover( + context *glsp.Context, + params *protocol.HoverParams, +) (*protocol.Hover, error) { + parser := documentParserMap[params.TextDocument.URI] + + line := params.Position.Line + character := params.Position.Character + + if _, found := parser.CommentLines[line]; found { + // Comment + return nil, nil + } + + entry, found := parser.Tree.Entries[line] + + if !found { + // Empty line + return nil, nil + } + + target := handlers.GetHoverTargetInEntry(*entry, character) + + var hostname *analyzer.HostsHostname + + switch *target { + case handlers.HoverTargetIPAddress: + relativeCursor := character - entry.IPAddress.Location.Start.Character + hover := fields.IPAddressField.FetchHoverInfo(entry.IPAddress.Value.String(), relativeCursor) + + return &protocol.Hover{ + Contents: hover, + }, nil + case handlers.HoverTargetHostname: + hostname = entry.Hostname + case handlers.HoverTargetAlias: + for _, alias := range entry.Aliases { + if alias.Location.Start.Character <= character && character <= alias.Location.End.Character { + hostname = alias + break + } + } + } + + if hostname != nil { + contents := []string{ + "## Hostname", + } + contents = append( + contents, + fields.HostnameField.GetTypeDescription()..., + ) + contents = append( + contents, + []string{ + "", + }..., + ) + contents = append( + contents, + fields.HostnameField.Documentation, + ) + contents = append( + contents, + handlers.GetHoverInfoForHostname(*parser, *hostname, character)..., + ) + + return &protocol.Hover{ + Contents: &protocol.MarkupContent{ + Kind: protocol.MarkupKindMarkdown, + Value: strings.Join(contents, "\n"), + }, + }, nil + } + + return nil, nil +} diff --git a/handlers/hosts/lsp/workspace-execute-command.go b/handlers/hosts/lsp/workspace-execute-command.go new file mode 100644 index 0000000..75cb9b6 --- /dev/null +++ b/handlers/hosts/lsp/workspace-execute-command.go @@ -0,0 +1,24 @@ +package lsp + +import ( + "config-lsp/handlers/hosts/handlers" + "strings" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteCommandParams) (*protocol.ApplyWorkspaceEditParams, error) { + _, command, _ := strings.Cut(params.Command, ".") + + switch command { + case string(handlers.CodeActionInlineAliases): + args := handlers.CodeActionInlineAliasesArgsFromArguments(params.Arguments[0].(map[string]any)) + + parser := documentParserMap[args.URI] + + return args.RunCommand(*parser) + } + + return nil, nil +} diff --git a/handlers/hosts/parser/Hosts.interp b/handlers/hosts/parser/Hosts.interp new file mode 100644 index 0000000..9417135 --- /dev/null +++ b/handlers/hosts/parser/Hosts.interp @@ -0,0 +1,48 @@ +token literal names: +null +null +'/' +'.' +':' +'#' +null +null +null +null +null + +token symbolic names: +null +COMMENTLINE +SLASH +DOT +COLON +HASHTAG +SEPARATOR +NEWLINE +DIGITS +OCTETS +DOMAIN + +rule names: +lineStatement +entry +aliases +alias +hostname +domain +ipAddress +ipv4Address +singleIPv4Address +ipv6Address +singleIPv6Address +ipv4Digit +ipv6Octet +ipRange +ipRangeBits +comment +leadingComment + + +atn: +[4, 1, 10, 110, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 1, 0, 3, 0, 36, 8, 0, 1, 0, 1, 0, 3, 0, 40, 8, 0, 1, 0, 3, 0, 43, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 52, 8, 1, 1, 2, 1, 2, 3, 2, 56, 8, 2, 4, 2, 58, 8, 2, 11, 2, 12, 2, 59, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 3, 6, 70, 8, 6, 1, 7, 1, 7, 3, 7, 74, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 3, 9, 86, 8, 9, 1, 10, 1, 10, 1, 10, 4, 10, 91, 8, 10, 11, 10, 12, 10, 92, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 0, 102, 0, 35, 1, 0, 0, 0, 2, 46, 1, 0, 0, 0, 4, 57, 1, 0, 0, 0, 6, 61, 1, 0, 0, 0, 8, 63, 1, 0, 0, 0, 10, 65, 1, 0, 0, 0, 12, 69, 1, 0, 0, 0, 14, 71, 1, 0, 0, 0, 16, 75, 1, 0, 0, 0, 18, 83, 1, 0, 0, 0, 20, 90, 1, 0, 0, 0, 22, 96, 1, 0, 0, 0, 24, 98, 1, 0, 0, 0, 26, 100, 1, 0, 0, 0, 28, 103, 1, 0, 0, 0, 30, 105, 1, 0, 0, 0, 32, 107, 1, 0, 0, 0, 34, 36, 5, 6, 0, 0, 35, 34, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 39, 3, 2, 1, 0, 38, 40, 5, 6, 0, 0, 39, 38, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 1, 0, 0, 0, 41, 43, 3, 32, 16, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 45, 5, 0, 0, 1, 45, 1, 1, 0, 0, 0, 46, 47, 3, 12, 6, 0, 47, 48, 5, 6, 0, 0, 48, 51, 3, 8, 4, 0, 49, 50, 5, 6, 0, 0, 50, 52, 3, 4, 2, 0, 51, 49, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 3, 1, 0, 0, 0, 53, 55, 3, 6, 3, 0, 54, 56, 5, 6, 0, 0, 55, 54, 1, 0, 0, 0, 55, 56, 1, 0, 0, 0, 56, 58, 1, 0, 0, 0, 57, 53, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 57, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 5, 1, 0, 0, 0, 61, 62, 5, 10, 0, 0, 62, 7, 1, 0, 0, 0, 63, 64, 3, 10, 5, 0, 64, 9, 1, 0, 0, 0, 65, 66, 5, 10, 0, 0, 66, 11, 1, 0, 0, 0, 67, 70, 3, 14, 7, 0, 68, 70, 3, 18, 9, 0, 69, 67, 1, 0, 0, 0, 69, 68, 1, 0, 0, 0, 70, 13, 1, 0, 0, 0, 71, 73, 3, 16, 8, 0, 72, 74, 3, 26, 13, 0, 73, 72, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 15, 1, 0, 0, 0, 75, 76, 3, 22, 11, 0, 76, 77, 5, 3, 0, 0, 77, 78, 3, 22, 11, 0, 78, 79, 5, 3, 0, 0, 79, 80, 3, 22, 11, 0, 80, 81, 5, 3, 0, 0, 81, 82, 3, 22, 11, 0, 82, 17, 1, 0, 0, 0, 83, 85, 3, 20, 10, 0, 84, 86, 3, 26, 13, 0, 85, 84, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 19, 1, 0, 0, 0, 87, 88, 3, 24, 12, 0, 88, 89, 5, 4, 0, 0, 89, 91, 1, 0, 0, 0, 90, 87, 1, 0, 0, 0, 91, 92, 1, 0, 0, 0, 92, 90, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 94, 1, 0, 0, 0, 94, 95, 3, 24, 12, 0, 95, 21, 1, 0, 0, 0, 96, 97, 5, 8, 0, 0, 97, 23, 1, 0, 0, 0, 98, 99, 5, 9, 0, 0, 99, 25, 1, 0, 0, 0, 100, 101, 5, 2, 0, 0, 101, 102, 3, 28, 14, 0, 102, 27, 1, 0, 0, 0, 103, 104, 5, 8, 0, 0, 104, 29, 1, 0, 0, 0, 105, 106, 5, 1, 0, 0, 106, 31, 1, 0, 0, 0, 107, 108, 5, 1, 0, 0, 108, 33, 1, 0, 0, 0, 10, 35, 39, 42, 51, 55, 59, 69, 73, 85, 92] \ No newline at end of file diff --git a/handlers/hosts/parser/Hosts.tokens b/handlers/hosts/parser/Hosts.tokens new file mode 100644 index 0000000..c20b3c1 --- /dev/null +++ b/handlers/hosts/parser/Hosts.tokens @@ -0,0 +1,14 @@ +COMMENTLINE=1 +SLASH=2 +DOT=3 +COLON=4 +HASHTAG=5 +SEPARATOR=6 +NEWLINE=7 +DIGITS=8 +OCTETS=9 +DOMAIN=10 +'/'=2 +'.'=3 +':'=4 +'#'=5 diff --git a/handlers/hosts/parser/HostsLexer.interp b/handlers/hosts/parser/HostsLexer.interp new file mode 100644 index 0000000..e92d869 --- /dev/null +++ b/handlers/hosts/parser/HostsLexer.interp @@ -0,0 +1,50 @@ +token literal names: +null +null +'/' +'.' +':' +'#' +null +null +null +null +null + +token symbolic names: +null +COMMENTLINE +SLASH +DOT +COLON +HASHTAG +SEPARATOR +NEWLINE +DIGITS +OCTETS +DOMAIN + +rule names: +COMMENTLINE +SLASH +DOT +COLON +HASHTAG +SEPARATOR +NEWLINE +DIGITS +DIGIT +OCTETS +OCTET +DOMAIN +STRING + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 10, 83, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 1, 0, 1, 0, 4, 0, 30, 8, 0, 11, 0, 12, 0, 31, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 4, 5, 43, 8, 5, 11, 5, 12, 5, 44, 1, 6, 3, 6, 48, 8, 6, 1, 6, 1, 6, 1, 7, 4, 7, 53, 8, 7, 11, 7, 12, 7, 54, 1, 8, 1, 8, 1, 9, 4, 9, 60, 8, 9, 11, 9, 12, 9, 61, 1, 10, 1, 10, 1, 11, 4, 11, 67, 8, 11, 11, 11, 12, 11, 68, 1, 11, 1, 11, 4, 11, 73, 8, 11, 11, 11, 12, 11, 74, 5, 11, 77, 8, 11, 10, 11, 12, 11, 80, 9, 11, 1, 12, 1, 12, 0, 0, 13, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 0, 19, 9, 21, 0, 23, 10, 25, 0, 1, 0, 8, 2, 0, 10, 10, 13, 13, 2, 0, 9, 9, 32, 32, 1, 0, 13, 13, 1, 0, 10, 10, 1, 0, 48, 57, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 90, 97, 122, 5, 0, 9, 10, 13, 13, 32, 32, 35, 35, 46, 46, 87, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 1, 27, 1, 0, 0, 0, 3, 33, 1, 0, 0, 0, 5, 35, 1, 0, 0, 0, 7, 37, 1, 0, 0, 0, 9, 39, 1, 0, 0, 0, 11, 42, 1, 0, 0, 0, 13, 47, 1, 0, 0, 0, 15, 52, 1, 0, 0, 0, 17, 56, 1, 0, 0, 0, 19, 59, 1, 0, 0, 0, 21, 63, 1, 0, 0, 0, 23, 66, 1, 0, 0, 0, 25, 81, 1, 0, 0, 0, 27, 29, 3, 9, 4, 0, 28, 30, 8, 0, 0, 0, 29, 28, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 29, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 2, 1, 0, 0, 0, 33, 34, 5, 47, 0, 0, 34, 4, 1, 0, 0, 0, 35, 36, 5, 46, 0, 0, 36, 6, 1, 0, 0, 0, 37, 38, 5, 58, 0, 0, 38, 8, 1, 0, 0, 0, 39, 40, 5, 35, 0, 0, 40, 10, 1, 0, 0, 0, 41, 43, 7, 1, 0, 0, 42, 41, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 12, 1, 0, 0, 0, 46, 48, 7, 2, 0, 0, 47, 46, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 50, 7, 3, 0, 0, 50, 14, 1, 0, 0, 0, 51, 53, 3, 17, 8, 0, 52, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 52, 1, 0, 0, 0, 54, 55, 1, 0, 0, 0, 55, 16, 1, 0, 0, 0, 56, 57, 7, 4, 0, 0, 57, 18, 1, 0, 0, 0, 58, 60, 3, 21, 10, 0, 59, 58, 1, 0, 0, 0, 60, 61, 1, 0, 0, 0, 61, 59, 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 20, 1, 0, 0, 0, 63, 64, 7, 5, 0, 0, 64, 22, 1, 0, 0, 0, 65, 67, 3, 25, 12, 0, 66, 65, 1, 0, 0, 0, 67, 68, 1, 0, 0, 0, 68, 66, 1, 0, 0, 0, 68, 69, 1, 0, 0, 0, 69, 78, 1, 0, 0, 0, 70, 72, 3, 5, 2, 0, 71, 73, 7, 6, 0, 0, 72, 71, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 75, 1, 0, 0, 0, 75, 77, 1, 0, 0, 0, 76, 70, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 24, 1, 0, 0, 0, 80, 78, 1, 0, 0, 0, 81, 82, 8, 7, 0, 0, 82, 26, 1, 0, 0, 0, 9, 0, 31, 44, 47, 54, 61, 68, 74, 78, 0] \ No newline at end of file diff --git a/handlers/hosts/parser/HostsLexer.tokens b/handlers/hosts/parser/HostsLexer.tokens new file mode 100644 index 0000000..c20b3c1 --- /dev/null +++ b/handlers/hosts/parser/HostsLexer.tokens @@ -0,0 +1,14 @@ +COMMENTLINE=1 +SLASH=2 +DOT=3 +COLON=4 +HASHTAG=5 +SEPARATOR=6 +NEWLINE=7 +DIGITS=8 +OCTETS=9 +DOMAIN=10 +'/'=2 +'.'=3 +':'=4 +'#'=5 diff --git a/handlers/hosts/parser/hosts_base_listener.go b/handlers/hosts/parser/hosts_base_listener.go new file mode 100644 index 0000000..01d1e3a --- /dev/null +++ b/handlers/hosts/parser/hosts_base_listener.go @@ -0,0 +1,124 @@ +// Code generated from Hosts.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Hosts + +import "github.com/antlr4-go/antlr/v4" + +// BaseHostsListener is a complete listener for a parse tree produced by HostsParser. +type BaseHostsListener struct{} + +var _ HostsListener = &BaseHostsListener{} + +// VisitTerminal is called when a terminal node is visited. +func (s *BaseHostsListener) VisitTerminal(node antlr.TerminalNode) {} + +// VisitErrorNode is called when an error node is visited. +func (s *BaseHostsListener) VisitErrorNode(node antlr.ErrorNode) {} + +// EnterEveryRule is called when any rule is entered. +func (s *BaseHostsListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} + +// ExitEveryRule is called when any rule is exited. +func (s *BaseHostsListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} + +// EnterLineStatement is called when production lineStatement is entered. +func (s *BaseHostsListener) EnterLineStatement(ctx *LineStatementContext) {} + +// ExitLineStatement is called when production lineStatement is exited. +func (s *BaseHostsListener) ExitLineStatement(ctx *LineStatementContext) {} + +// EnterEntry is called when production entry is entered. +func (s *BaseHostsListener) EnterEntry(ctx *EntryContext) {} + +// ExitEntry is called when production entry is exited. +func (s *BaseHostsListener) ExitEntry(ctx *EntryContext) {} + +// EnterAliases is called when production aliases is entered. +func (s *BaseHostsListener) EnterAliases(ctx *AliasesContext) {} + +// ExitAliases is called when production aliases is exited. +func (s *BaseHostsListener) ExitAliases(ctx *AliasesContext) {} + +// EnterAlias is called when production alias is entered. +func (s *BaseHostsListener) EnterAlias(ctx *AliasContext) {} + +// ExitAlias is called when production alias is exited. +func (s *BaseHostsListener) ExitAlias(ctx *AliasContext) {} + +// EnterHostname is called when production hostname is entered. +func (s *BaseHostsListener) EnterHostname(ctx *HostnameContext) {} + +// ExitHostname is called when production hostname is exited. +func (s *BaseHostsListener) ExitHostname(ctx *HostnameContext) {} + +// EnterDomain is called when production domain is entered. +func (s *BaseHostsListener) EnterDomain(ctx *DomainContext) {} + +// ExitDomain is called when production domain is exited. +func (s *BaseHostsListener) ExitDomain(ctx *DomainContext) {} + +// EnterIpAddress is called when production ipAddress is entered. +func (s *BaseHostsListener) EnterIpAddress(ctx *IpAddressContext) {} + +// ExitIpAddress is called when production ipAddress is exited. +func (s *BaseHostsListener) ExitIpAddress(ctx *IpAddressContext) {} + +// EnterIpv4Address is called when production ipv4Address is entered. +func (s *BaseHostsListener) EnterIpv4Address(ctx *Ipv4AddressContext) {} + +// ExitIpv4Address is called when production ipv4Address is exited. +func (s *BaseHostsListener) ExitIpv4Address(ctx *Ipv4AddressContext) {} + +// EnterSingleIPv4Address is called when production singleIPv4Address is entered. +func (s *BaseHostsListener) EnterSingleIPv4Address(ctx *SingleIPv4AddressContext) {} + +// ExitSingleIPv4Address is called when production singleIPv4Address is exited. +func (s *BaseHostsListener) ExitSingleIPv4Address(ctx *SingleIPv4AddressContext) {} + +// EnterIpv6Address is called when production ipv6Address is entered. +func (s *BaseHostsListener) EnterIpv6Address(ctx *Ipv6AddressContext) {} + +// ExitIpv6Address is called when production ipv6Address is exited. +func (s *BaseHostsListener) ExitIpv6Address(ctx *Ipv6AddressContext) {} + +// EnterSingleIPv6Address is called when production singleIPv6Address is entered. +func (s *BaseHostsListener) EnterSingleIPv6Address(ctx *SingleIPv6AddressContext) {} + +// ExitSingleIPv6Address is called when production singleIPv6Address is exited. +func (s *BaseHostsListener) ExitSingleIPv6Address(ctx *SingleIPv6AddressContext) {} + +// EnterIpv4Digit is called when production ipv4Digit is entered. +func (s *BaseHostsListener) EnterIpv4Digit(ctx *Ipv4DigitContext) {} + +// ExitIpv4Digit is called when production ipv4Digit is exited. +func (s *BaseHostsListener) ExitIpv4Digit(ctx *Ipv4DigitContext) {} + +// EnterIpv6Octet is called when production ipv6Octet is entered. +func (s *BaseHostsListener) EnterIpv6Octet(ctx *Ipv6OctetContext) {} + +// ExitIpv6Octet is called when production ipv6Octet is exited. +func (s *BaseHostsListener) ExitIpv6Octet(ctx *Ipv6OctetContext) {} + +// EnterIpRange is called when production ipRange is entered. +func (s *BaseHostsListener) EnterIpRange(ctx *IpRangeContext) {} + +// ExitIpRange is called when production ipRange is exited. +func (s *BaseHostsListener) ExitIpRange(ctx *IpRangeContext) {} + +// EnterIpRangeBits is called when production ipRangeBits is entered. +func (s *BaseHostsListener) EnterIpRangeBits(ctx *IpRangeBitsContext) {} + +// ExitIpRangeBits is called when production ipRangeBits is exited. +func (s *BaseHostsListener) ExitIpRangeBits(ctx *IpRangeBitsContext) {} + +// EnterComment is called when production comment is entered. +func (s *BaseHostsListener) EnterComment(ctx *CommentContext) {} + +// ExitComment is called when production comment is exited. +func (s *BaseHostsListener) ExitComment(ctx *CommentContext) {} + +// EnterLeadingComment is called when production leadingComment is entered. +func (s *BaseHostsListener) EnterLeadingComment(ctx *LeadingCommentContext) {} + +// ExitLeadingComment is called when production leadingComment is exited. +func (s *BaseHostsListener) ExitLeadingComment(ctx *LeadingCommentContext) {} diff --git a/handlers/hosts/parser/hosts_lexer.go b/handlers/hosts/parser/hosts_lexer.go new file mode 100644 index 0000000..db0d9e1 --- /dev/null +++ b/handlers/hosts/parser/hosts_lexer.go @@ -0,0 +1,145 @@ +// Code generated from Hosts.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser + +import ( + "fmt" + "github.com/antlr4-go/antlr/v4" + "sync" + "unicode" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = sync.Once{} +var _ = unicode.IsLetter + +type HostsLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +var HostsLexerLexerStaticData struct { + once sync.Once + serializedATN []int32 + ChannelNames []string + ModeNames []string + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func hostslexerLexerInit() { + staticData := &HostsLexerLexerStaticData + staticData.ChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", + } + staticData.ModeNames = []string{ + "DEFAULT_MODE", + } + staticData.LiteralNames = []string{ + "", "", "'/'", "'.'", "':'", "'#'", + } + staticData.SymbolicNames = []string{ + "", "COMMENTLINE", "SLASH", "DOT", "COLON", "HASHTAG", "SEPARATOR", + "NEWLINE", "DIGITS", "OCTETS", "DOMAIN", + } + staticData.RuleNames = []string{ + "COMMENTLINE", "SLASH", "DOT", "COLON", "HASHTAG", "SEPARATOR", "NEWLINE", + "DIGITS", "DIGIT", "OCTETS", "OCTET", "DOMAIN", "STRING", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 0, 10, 83, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, + 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, + 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 1, 0, 1, 0, 4, 0, 30, 8, 0, 11, + 0, 12, 0, 31, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 4, + 5, 43, 8, 5, 11, 5, 12, 5, 44, 1, 6, 3, 6, 48, 8, 6, 1, 6, 1, 6, 1, 7, + 4, 7, 53, 8, 7, 11, 7, 12, 7, 54, 1, 8, 1, 8, 1, 9, 4, 9, 60, 8, 9, 11, + 9, 12, 9, 61, 1, 10, 1, 10, 1, 11, 4, 11, 67, 8, 11, 11, 11, 12, 11, 68, + 1, 11, 1, 11, 4, 11, 73, 8, 11, 11, 11, 12, 11, 74, 5, 11, 77, 8, 11, 10, + 11, 12, 11, 80, 9, 11, 1, 12, 1, 12, 0, 0, 13, 1, 1, 3, 2, 5, 3, 7, 4, + 9, 5, 11, 6, 13, 7, 15, 8, 17, 0, 19, 9, 21, 0, 23, 10, 25, 0, 1, 0, 8, + 2, 0, 10, 10, 13, 13, 2, 0, 9, 9, 32, 32, 1, 0, 13, 13, 1, 0, 10, 10, 1, + 0, 48, 57, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 90, 97, 122, 5, 0, + 9, 10, 13, 13, 32, 32, 35, 35, 46, 46, 87, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, + 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, + 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 23, 1, + 0, 0, 0, 1, 27, 1, 0, 0, 0, 3, 33, 1, 0, 0, 0, 5, 35, 1, 0, 0, 0, 7, 37, + 1, 0, 0, 0, 9, 39, 1, 0, 0, 0, 11, 42, 1, 0, 0, 0, 13, 47, 1, 0, 0, 0, + 15, 52, 1, 0, 0, 0, 17, 56, 1, 0, 0, 0, 19, 59, 1, 0, 0, 0, 21, 63, 1, + 0, 0, 0, 23, 66, 1, 0, 0, 0, 25, 81, 1, 0, 0, 0, 27, 29, 3, 9, 4, 0, 28, + 30, 8, 0, 0, 0, 29, 28, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 29, 1, 0, 0, + 0, 31, 32, 1, 0, 0, 0, 32, 2, 1, 0, 0, 0, 33, 34, 5, 47, 0, 0, 34, 4, 1, + 0, 0, 0, 35, 36, 5, 46, 0, 0, 36, 6, 1, 0, 0, 0, 37, 38, 5, 58, 0, 0, 38, + 8, 1, 0, 0, 0, 39, 40, 5, 35, 0, 0, 40, 10, 1, 0, 0, 0, 41, 43, 7, 1, 0, + 0, 42, 41, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, + 1, 0, 0, 0, 45, 12, 1, 0, 0, 0, 46, 48, 7, 2, 0, 0, 47, 46, 1, 0, 0, 0, + 47, 48, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 50, 7, 3, 0, 0, 50, 14, 1, + 0, 0, 0, 51, 53, 3, 17, 8, 0, 52, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, + 52, 1, 0, 0, 0, 54, 55, 1, 0, 0, 0, 55, 16, 1, 0, 0, 0, 56, 57, 7, 4, 0, + 0, 57, 18, 1, 0, 0, 0, 58, 60, 3, 21, 10, 0, 59, 58, 1, 0, 0, 0, 60, 61, + 1, 0, 0, 0, 61, 59, 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 20, 1, 0, 0, 0, + 63, 64, 7, 5, 0, 0, 64, 22, 1, 0, 0, 0, 65, 67, 3, 25, 12, 0, 66, 65, 1, + 0, 0, 0, 67, 68, 1, 0, 0, 0, 68, 66, 1, 0, 0, 0, 68, 69, 1, 0, 0, 0, 69, + 78, 1, 0, 0, 0, 70, 72, 3, 5, 2, 0, 71, 73, 7, 6, 0, 0, 72, 71, 1, 0, 0, + 0, 73, 74, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 75, 1, 0, 0, 0, 75, 77, + 1, 0, 0, 0, 76, 70, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, + 78, 79, 1, 0, 0, 0, 79, 24, 1, 0, 0, 0, 80, 78, 1, 0, 0, 0, 81, 82, 8, + 7, 0, 0, 82, 26, 1, 0, 0, 0, 9, 0, 31, 44, 47, 54, 61, 68, 74, 78, 0, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// HostsLexerInit initializes any static state used to implement HostsLexer. By default the +// static state used to implement the lexer is lazily initialized during the first call to +// NewHostsLexer(). You can call this function if you wish to initialize the static state ahead +// of time. +func HostsLexerInit() { + staticData := &HostsLexerLexerStaticData + staticData.once.Do(hostslexerLexerInit) +} + +// NewHostsLexer produces a new lexer instance for the optional input antlr.CharStream. +func NewHostsLexer(input antlr.CharStream) *HostsLexer { + HostsLexerInit() + l := new(HostsLexer) + l.BaseLexer = antlr.NewBaseLexer(input) + staticData := &HostsLexerLexerStaticData + l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + l.channelNames = staticData.ChannelNames + l.modeNames = staticData.ModeNames + l.RuleNames = staticData.RuleNames + l.LiteralNames = staticData.LiteralNames + l.SymbolicNames = staticData.SymbolicNames + l.GrammarFileName = "Hosts.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// HostsLexer tokens. +const ( + HostsLexerCOMMENTLINE = 1 + HostsLexerSLASH = 2 + HostsLexerDOT = 3 + HostsLexerCOLON = 4 + HostsLexerHASHTAG = 5 + HostsLexerSEPARATOR = 6 + HostsLexerNEWLINE = 7 + HostsLexerDIGITS = 8 + HostsLexerOCTETS = 9 + HostsLexerDOMAIN = 10 +) diff --git a/handlers/hosts/parser/hosts_listener.go b/handlers/hosts/parser/hosts_listener.go new file mode 100644 index 0000000..fbf964f --- /dev/null +++ b/handlers/hosts/parser/hosts_listener.go @@ -0,0 +1,112 @@ +// Code generated from Hosts.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Hosts + +import "github.com/antlr4-go/antlr/v4" + +// HostsListener is a complete listener for a parse tree produced by HostsParser. +type HostsListener interface { + antlr.ParseTreeListener + + // EnterLineStatement is called when entering the lineStatement production. + EnterLineStatement(c *LineStatementContext) + + // EnterEntry is called when entering the entry production. + EnterEntry(c *EntryContext) + + // EnterAliases is called when entering the aliases production. + EnterAliases(c *AliasesContext) + + // EnterAlias is called when entering the alias production. + EnterAlias(c *AliasContext) + + // EnterHostname is called when entering the hostname production. + EnterHostname(c *HostnameContext) + + // EnterDomain is called when entering the domain production. + EnterDomain(c *DomainContext) + + // EnterIpAddress is called when entering the ipAddress production. + EnterIpAddress(c *IpAddressContext) + + // EnterIpv4Address is called when entering the ipv4Address production. + EnterIpv4Address(c *Ipv4AddressContext) + + // EnterSingleIPv4Address is called when entering the singleIPv4Address production. + EnterSingleIPv4Address(c *SingleIPv4AddressContext) + + // EnterIpv6Address is called when entering the ipv6Address production. + EnterIpv6Address(c *Ipv6AddressContext) + + // EnterSingleIPv6Address is called when entering the singleIPv6Address production. + EnterSingleIPv6Address(c *SingleIPv6AddressContext) + + // EnterIpv4Digit is called when entering the ipv4Digit production. + EnterIpv4Digit(c *Ipv4DigitContext) + + // EnterIpv6Octet is called when entering the ipv6Octet production. + EnterIpv6Octet(c *Ipv6OctetContext) + + // EnterIpRange is called when entering the ipRange production. + EnterIpRange(c *IpRangeContext) + + // EnterIpRangeBits is called when entering the ipRangeBits production. + EnterIpRangeBits(c *IpRangeBitsContext) + + // EnterComment is called when entering the comment production. + EnterComment(c *CommentContext) + + // EnterLeadingComment is called when entering the leadingComment production. + EnterLeadingComment(c *LeadingCommentContext) + + // ExitLineStatement is called when exiting the lineStatement production. + ExitLineStatement(c *LineStatementContext) + + // ExitEntry is called when exiting the entry production. + ExitEntry(c *EntryContext) + + // ExitAliases is called when exiting the aliases production. + ExitAliases(c *AliasesContext) + + // ExitAlias is called when exiting the alias production. + ExitAlias(c *AliasContext) + + // ExitHostname is called when exiting the hostname production. + ExitHostname(c *HostnameContext) + + // ExitDomain is called when exiting the domain production. + ExitDomain(c *DomainContext) + + // ExitIpAddress is called when exiting the ipAddress production. + ExitIpAddress(c *IpAddressContext) + + // ExitIpv4Address is called when exiting the ipv4Address production. + ExitIpv4Address(c *Ipv4AddressContext) + + // ExitSingleIPv4Address is called when exiting the singleIPv4Address production. + ExitSingleIPv4Address(c *SingleIPv4AddressContext) + + // ExitIpv6Address is called when exiting the ipv6Address production. + ExitIpv6Address(c *Ipv6AddressContext) + + // ExitSingleIPv6Address is called when exiting the singleIPv6Address production. + ExitSingleIPv6Address(c *SingleIPv6AddressContext) + + // ExitIpv4Digit is called when exiting the ipv4Digit production. + ExitIpv4Digit(c *Ipv4DigitContext) + + // ExitIpv6Octet is called when exiting the ipv6Octet production. + ExitIpv6Octet(c *Ipv6OctetContext) + + // ExitIpRange is called when exiting the ipRange production. + ExitIpRange(c *IpRangeContext) + + // ExitIpRangeBits is called when exiting the ipRangeBits production. + ExitIpRangeBits(c *IpRangeBitsContext) + + // ExitComment is called when exiting the comment production. + ExitComment(c *CommentContext) + + // ExitLeadingComment is called when exiting the leadingComment production. + ExitLeadingComment(c *LeadingCommentContext) +} diff --git a/handlers/hosts/parser/hosts_parser.go b/handlers/hosts/parser/hosts_parser.go new file mode 100644 index 0000000..81dca33 --- /dev/null +++ b/handlers/hosts/parser/hosts_parser.go @@ -0,0 +1,2369 @@ +// Code generated from Hosts.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Hosts + +import ( + "fmt" + "strconv" + "sync" + + "github.com/antlr4-go/antlr/v4" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = strconv.Itoa +var _ = sync.Once{} + +type HostsParser struct { + *antlr.BaseParser +} + +var HostsParserStaticData struct { + once sync.Once + serializedATN []int32 + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func hostsParserInit() { + staticData := &HostsParserStaticData + staticData.LiteralNames = []string{ + "", "", "'/'", "'.'", "':'", "'#'", + } + staticData.SymbolicNames = []string{ + "", "COMMENTLINE", "SLASH", "DOT", "COLON", "HASHTAG", "SEPARATOR", + "NEWLINE", "DIGITS", "OCTETS", "DOMAIN", + } + staticData.RuleNames = []string{ + "lineStatement", "entry", "aliases", "alias", "hostname", "domain", + "ipAddress", "ipv4Address", "singleIPv4Address", "ipv6Address", "singleIPv6Address", + "ipv4Digit", "ipv6Octet", "ipRange", "ipRangeBits", "comment", "leadingComment", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 1, 10, 110, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, + 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, + 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, + 2, 16, 7, 16, 1, 0, 3, 0, 36, 8, 0, 1, 0, 1, 0, 3, 0, 40, 8, 0, 1, 0, 3, + 0, 43, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 52, 8, 1, + 1, 2, 1, 2, 3, 2, 56, 8, 2, 4, 2, 58, 8, 2, 11, 2, 12, 2, 59, 1, 3, 1, + 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 3, 6, 70, 8, 6, 1, 7, 1, 7, 3, 7, + 74, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 3, + 9, 86, 8, 9, 1, 10, 1, 10, 1, 10, 4, 10, 91, 8, 10, 11, 10, 12, 10, 92, + 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, + 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 0, 102, 0, 35, 1, 0, 0, 0, 2, + 46, 1, 0, 0, 0, 4, 57, 1, 0, 0, 0, 6, 61, 1, 0, 0, 0, 8, 63, 1, 0, 0, 0, + 10, 65, 1, 0, 0, 0, 12, 69, 1, 0, 0, 0, 14, 71, 1, 0, 0, 0, 16, 75, 1, + 0, 0, 0, 18, 83, 1, 0, 0, 0, 20, 90, 1, 0, 0, 0, 22, 96, 1, 0, 0, 0, 24, + 98, 1, 0, 0, 0, 26, 100, 1, 0, 0, 0, 28, 103, 1, 0, 0, 0, 30, 105, 1, 0, + 0, 0, 32, 107, 1, 0, 0, 0, 34, 36, 5, 6, 0, 0, 35, 34, 1, 0, 0, 0, 35, + 36, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 39, 3, 2, 1, 0, 38, 40, 5, 6, 0, + 0, 39, 38, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 1, 0, 0, 0, 41, 43, + 3, 32, 16, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, + 0, 44, 45, 5, 0, 0, 1, 45, 1, 1, 0, 0, 0, 46, 47, 3, 12, 6, 0, 47, 48, + 5, 6, 0, 0, 48, 51, 3, 8, 4, 0, 49, 50, 5, 6, 0, 0, 50, 52, 3, 4, 2, 0, + 51, 49, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 3, 1, 0, 0, 0, 53, 55, 3, 6, + 3, 0, 54, 56, 5, 6, 0, 0, 55, 54, 1, 0, 0, 0, 55, 56, 1, 0, 0, 0, 56, 58, + 1, 0, 0, 0, 57, 53, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 57, 1, 0, 0, 0, + 59, 60, 1, 0, 0, 0, 60, 5, 1, 0, 0, 0, 61, 62, 5, 10, 0, 0, 62, 7, 1, 0, + 0, 0, 63, 64, 3, 10, 5, 0, 64, 9, 1, 0, 0, 0, 65, 66, 5, 10, 0, 0, 66, + 11, 1, 0, 0, 0, 67, 70, 3, 14, 7, 0, 68, 70, 3, 18, 9, 0, 69, 67, 1, 0, + 0, 0, 69, 68, 1, 0, 0, 0, 70, 13, 1, 0, 0, 0, 71, 73, 3, 16, 8, 0, 72, + 74, 3, 26, 13, 0, 73, 72, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 15, 1, 0, + 0, 0, 75, 76, 3, 22, 11, 0, 76, 77, 5, 3, 0, 0, 77, 78, 3, 22, 11, 0, 78, + 79, 5, 3, 0, 0, 79, 80, 3, 22, 11, 0, 80, 81, 5, 3, 0, 0, 81, 82, 3, 22, + 11, 0, 82, 17, 1, 0, 0, 0, 83, 85, 3, 20, 10, 0, 84, 86, 3, 26, 13, 0, + 85, 84, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 19, 1, 0, 0, 0, 87, 88, 3, + 24, 12, 0, 88, 89, 5, 4, 0, 0, 89, 91, 1, 0, 0, 0, 90, 87, 1, 0, 0, 0, + 91, 92, 1, 0, 0, 0, 92, 90, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 94, 1, + 0, 0, 0, 94, 95, 3, 24, 12, 0, 95, 21, 1, 0, 0, 0, 96, 97, 5, 8, 0, 0, + 97, 23, 1, 0, 0, 0, 98, 99, 5, 9, 0, 0, 99, 25, 1, 0, 0, 0, 100, 101, 5, + 2, 0, 0, 101, 102, 3, 28, 14, 0, 102, 27, 1, 0, 0, 0, 103, 104, 5, 8, 0, + 0, 104, 29, 1, 0, 0, 0, 105, 106, 5, 1, 0, 0, 106, 31, 1, 0, 0, 0, 107, + 108, 5, 1, 0, 0, 108, 33, 1, 0, 0, 0, 10, 35, 39, 42, 51, 55, 59, 69, 73, + 85, 92, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// HostsParserInit initializes any static state used to implement HostsParser. By default the +// static state used to implement the parser is lazily initialized during the first call to +// NewHostsParser(). You can call this function if you wish to initialize the static state ahead +// of time. +func HostsParserInit() { + staticData := &HostsParserStaticData + staticData.once.Do(hostsParserInit) +} + +// NewHostsParser produces a new parser instance for the optional input antlr.TokenStream. +func NewHostsParser(input antlr.TokenStream) *HostsParser { + HostsParserInit() + this := new(HostsParser) + this.BaseParser = antlr.NewBaseParser(input) + staticData := &HostsParserStaticData + this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + this.RuleNames = staticData.RuleNames + this.LiteralNames = staticData.LiteralNames + this.SymbolicNames = staticData.SymbolicNames + this.GrammarFileName = "Hosts.g4" + + return this +} + +// HostsParser tokens. +const ( + HostsParserEOF = antlr.TokenEOF + HostsParserCOMMENTLINE = 1 + HostsParserSLASH = 2 + HostsParserDOT = 3 + HostsParserCOLON = 4 + HostsParserHASHTAG = 5 + HostsParserSEPARATOR = 6 + HostsParserNEWLINE = 7 + HostsParserDIGITS = 8 + HostsParserOCTETS = 9 + HostsParserDOMAIN = 10 +) + +// HostsParser rules. +const ( + HostsParserRULE_lineStatement = 0 + HostsParserRULE_entry = 1 + HostsParserRULE_aliases = 2 + HostsParserRULE_alias = 3 + HostsParserRULE_hostname = 4 + HostsParserRULE_domain = 5 + HostsParserRULE_ipAddress = 6 + HostsParserRULE_ipv4Address = 7 + HostsParserRULE_singleIPv4Address = 8 + HostsParserRULE_ipv6Address = 9 + HostsParserRULE_singleIPv6Address = 10 + HostsParserRULE_ipv4Digit = 11 + HostsParserRULE_ipv6Octet = 12 + HostsParserRULE_ipRange = 13 + HostsParserRULE_ipRangeBits = 14 + HostsParserRULE_comment = 15 + HostsParserRULE_leadingComment = 16 +) + +// ILineStatementContext is an interface to support dynamic dispatch. +type ILineStatementContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Entry() IEntryContext + EOF() antlr.TerminalNode + AllSEPARATOR() []antlr.TerminalNode + SEPARATOR(i int) antlr.TerminalNode + LeadingComment() ILeadingCommentContext + + // IsLineStatementContext differentiates from other interfaces. + IsLineStatementContext() +} + +type LineStatementContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyLineStatementContext() *LineStatementContext { + var p = new(LineStatementContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_lineStatement + return p +} + +func InitEmptyLineStatementContext(p *LineStatementContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_lineStatement +} + +func (*LineStatementContext) IsLineStatementContext() {} + +func NewLineStatementContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *LineStatementContext { + var p = new(LineStatementContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_lineStatement + + return p +} + +func (s *LineStatementContext) GetParser() antlr.Parser { return s.parser } + +func (s *LineStatementContext) Entry() IEntryContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IEntryContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IEntryContext) +} + +func (s *LineStatementContext) EOF() antlr.TerminalNode { + return s.GetToken(HostsParserEOF, 0) +} + +func (s *LineStatementContext) AllSEPARATOR() []antlr.TerminalNode { + return s.GetTokens(HostsParserSEPARATOR) +} + +func (s *LineStatementContext) SEPARATOR(i int) antlr.TerminalNode { + return s.GetToken(HostsParserSEPARATOR, i) +} + +func (s *LineStatementContext) LeadingComment() ILeadingCommentContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ILeadingCommentContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ILeadingCommentContext) +} + +func (s *LineStatementContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LineStatementContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *LineStatementContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterLineStatement(s) + } +} + +func (s *LineStatementContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitLineStatement(s) + } +} + +func (p *HostsParser) LineStatement() (localctx ILineStatementContext) { + localctx = NewLineStatementContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, HostsParserRULE_lineStatement) + var _la int + + p.EnterOuterAlt(localctx, 1) + p.SetState(35) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HostsParserSEPARATOR { + { + p.SetState(34) + p.Match(HostsParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(37) + p.Entry() + } + p.SetState(39) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HostsParserSEPARATOR { + { + p.SetState(38) + p.Match(HostsParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(42) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HostsParserCOMMENTLINE { + { + p.SetState(41) + p.LeadingComment() + } + + } + { + p.SetState(44) + p.Match(HostsParserEOF) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IEntryContext is an interface to support dynamic dispatch. +type IEntryContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + IpAddress() IIpAddressContext + AllSEPARATOR() []antlr.TerminalNode + SEPARATOR(i int) antlr.TerminalNode + Hostname() IHostnameContext + Aliases() IAliasesContext + + // IsEntryContext differentiates from other interfaces. + IsEntryContext() +} + +type EntryContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyEntryContext() *EntryContext { + var p = new(EntryContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_entry + return p +} + +func InitEmptyEntryContext(p *EntryContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_entry +} + +func (*EntryContext) IsEntryContext() {} + +func NewEntryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EntryContext { + var p = new(EntryContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_entry + + return p +} + +func (s *EntryContext) GetParser() antlr.Parser { return s.parser } + +func (s *EntryContext) IpAddress() IIpAddressContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpAddressContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIpAddressContext) +} + +func (s *EntryContext) AllSEPARATOR() []antlr.TerminalNode { + return s.GetTokens(HostsParserSEPARATOR) +} + +func (s *EntryContext) SEPARATOR(i int) antlr.TerminalNode { + return s.GetToken(HostsParserSEPARATOR, i) +} + +func (s *EntryContext) Hostname() IHostnameContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IHostnameContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IHostnameContext) +} + +func (s *EntryContext) Aliases() IAliasesContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IAliasesContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IAliasesContext) +} + +func (s *EntryContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *EntryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *EntryContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterEntry(s) + } +} + +func (s *EntryContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitEntry(s) + } +} + +func (p *HostsParser) Entry() (localctx IEntryContext) { + localctx = NewEntryContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 2, HostsParserRULE_entry) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(46) + p.IpAddress() + } + { + p.SetState(47) + p.Match(HostsParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(48) + p.Hostname() + } + p.SetState(51) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 { + { + p.SetState(49) + p.Match(HostsParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(50) + p.Aliases() + } + + } else if p.HasError() { // JIM + goto errorExit + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IAliasesContext is an interface to support dynamic dispatch. +type IAliasesContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllAlias() []IAliasContext + Alias(i int) IAliasContext + AllSEPARATOR() []antlr.TerminalNode + SEPARATOR(i int) antlr.TerminalNode + + // IsAliasesContext differentiates from other interfaces. + IsAliasesContext() +} + +type AliasesContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyAliasesContext() *AliasesContext { + var p = new(AliasesContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_aliases + return p +} + +func InitEmptyAliasesContext(p *AliasesContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_aliases +} + +func (*AliasesContext) IsAliasesContext() {} + +func NewAliasesContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AliasesContext { + var p = new(AliasesContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_aliases + + return p +} + +func (s *AliasesContext) GetParser() antlr.Parser { return s.parser } + +func (s *AliasesContext) AllAlias() []IAliasContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IAliasContext); ok { + len++ + } + } + + tst := make([]IAliasContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IAliasContext); ok { + tst[i] = t.(IAliasContext) + i++ + } + } + + return tst +} + +func (s *AliasesContext) Alias(i int) IAliasContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IAliasContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IAliasContext) +} + +func (s *AliasesContext) AllSEPARATOR() []antlr.TerminalNode { + return s.GetTokens(HostsParserSEPARATOR) +} + +func (s *AliasesContext) SEPARATOR(i int) antlr.TerminalNode { + return s.GetToken(HostsParserSEPARATOR, i) +} + +func (s *AliasesContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AliasesContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *AliasesContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterAliases(s) + } +} + +func (s *AliasesContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitAliases(s) + } +} + +func (p *HostsParser) Aliases() (localctx IAliasesContext) { + localctx = NewAliasesContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 4, HostsParserRULE_aliases) + var _la int + + p.EnterOuterAlt(localctx, 1) + p.SetState(57) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for ok := true; ok; ok = _la == HostsParserDOMAIN { + { + p.SetState(53) + p.Alias() + } + p.SetState(55) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) == 1 { + { + p.SetState(54) + p.Match(HostsParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } else if p.HasError() { // JIM + goto errorExit + } + + p.SetState(59) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IAliasContext is an interface to support dynamic dispatch. +type IAliasContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + DOMAIN() antlr.TerminalNode + + // IsAliasContext differentiates from other interfaces. + IsAliasContext() +} + +type AliasContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyAliasContext() *AliasContext { + var p = new(AliasContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_alias + return p +} + +func InitEmptyAliasContext(p *AliasContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_alias +} + +func (*AliasContext) IsAliasContext() {} + +func NewAliasContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AliasContext { + var p = new(AliasContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_alias + + return p +} + +func (s *AliasContext) GetParser() antlr.Parser { return s.parser } + +func (s *AliasContext) DOMAIN() antlr.TerminalNode { + return s.GetToken(HostsParserDOMAIN, 0) +} + +func (s *AliasContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AliasContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *AliasContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterAlias(s) + } +} + +func (s *AliasContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitAlias(s) + } +} + +func (p *HostsParser) Alias() (localctx IAliasContext) { + localctx = NewAliasContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 6, HostsParserRULE_alias) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(61) + p.Match(HostsParserDOMAIN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IHostnameContext is an interface to support dynamic dispatch. +type IHostnameContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Domain() IDomainContext + + // IsHostnameContext differentiates from other interfaces. + IsHostnameContext() +} + +type HostnameContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyHostnameContext() *HostnameContext { + var p = new(HostnameContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_hostname + return p +} + +func InitEmptyHostnameContext(p *HostnameContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_hostname +} + +func (*HostnameContext) IsHostnameContext() {} + +func NewHostnameContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *HostnameContext { + var p = new(HostnameContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_hostname + + return p +} + +func (s *HostnameContext) GetParser() antlr.Parser { return s.parser } + +func (s *HostnameContext) Domain() IDomainContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IDomainContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IDomainContext) +} + +func (s *HostnameContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *HostnameContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *HostnameContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterHostname(s) + } +} + +func (s *HostnameContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitHostname(s) + } +} + +func (p *HostsParser) Hostname() (localctx IHostnameContext) { + localctx = NewHostnameContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 8, HostsParserRULE_hostname) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(63) + p.Domain() + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IDomainContext is an interface to support dynamic dispatch. +type IDomainContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + DOMAIN() antlr.TerminalNode + + // IsDomainContext differentiates from other interfaces. + IsDomainContext() +} + +type DomainContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyDomainContext() *DomainContext { + var p = new(DomainContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_domain + return p +} + +func InitEmptyDomainContext(p *DomainContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_domain +} + +func (*DomainContext) IsDomainContext() {} + +func NewDomainContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *DomainContext { + var p = new(DomainContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_domain + + return p +} + +func (s *DomainContext) GetParser() antlr.Parser { return s.parser } + +func (s *DomainContext) DOMAIN() antlr.TerminalNode { + return s.GetToken(HostsParserDOMAIN, 0) +} + +func (s *DomainContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *DomainContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *DomainContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterDomain(s) + } +} + +func (s *DomainContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitDomain(s) + } +} + +func (p *HostsParser) Domain() (localctx IDomainContext) { + localctx = NewDomainContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 10, HostsParserRULE_domain) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(65) + p.Match(HostsParserDOMAIN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIpAddressContext is an interface to support dynamic dispatch. +type IIpAddressContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Ipv4Address() IIpv4AddressContext + Ipv6Address() IIpv6AddressContext + + // IsIpAddressContext differentiates from other interfaces. + IsIpAddressContext() +} + +type IpAddressContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIpAddressContext() *IpAddressContext { + var p = new(IpAddressContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipAddress + return p +} + +func InitEmptyIpAddressContext(p *IpAddressContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipAddress +} + +func (*IpAddressContext) IsIpAddressContext() {} + +func NewIpAddressContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IpAddressContext { + var p = new(IpAddressContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_ipAddress + + return p +} + +func (s *IpAddressContext) GetParser() antlr.Parser { return s.parser } + +func (s *IpAddressContext) Ipv4Address() IIpv4AddressContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpv4AddressContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIpv4AddressContext) +} + +func (s *IpAddressContext) Ipv6Address() IIpv6AddressContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpv6AddressContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIpv6AddressContext) +} + +func (s *IpAddressContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IpAddressContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *IpAddressContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterIpAddress(s) + } +} + +func (s *IpAddressContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitIpAddress(s) + } +} + +func (p *HostsParser) IpAddress() (localctx IIpAddressContext) { + localctx = NewIpAddressContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 12, HostsParserRULE_ipAddress) + p.EnterOuterAlt(localctx, 1) + p.SetState(69) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetTokenStream().LA(1) { + case HostsParserDIGITS: + { + p.SetState(67) + p.Ipv4Address() + } + + case HostsParserOCTETS: + { + p.SetState(68) + p.Ipv6Address() + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIpv4AddressContext is an interface to support dynamic dispatch. +type IIpv4AddressContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + SingleIPv4Address() ISingleIPv4AddressContext + IpRange() IIpRangeContext + + // IsIpv4AddressContext differentiates from other interfaces. + IsIpv4AddressContext() +} + +type Ipv4AddressContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIpv4AddressContext() *Ipv4AddressContext { + var p = new(Ipv4AddressContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv4Address + return p +} + +func InitEmptyIpv4AddressContext(p *Ipv4AddressContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv4Address +} + +func (*Ipv4AddressContext) IsIpv4AddressContext() {} + +func NewIpv4AddressContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *Ipv4AddressContext { + var p = new(Ipv4AddressContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_ipv4Address + + return p +} + +func (s *Ipv4AddressContext) GetParser() antlr.Parser { return s.parser } + +func (s *Ipv4AddressContext) SingleIPv4Address() ISingleIPv4AddressContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ISingleIPv4AddressContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ISingleIPv4AddressContext) +} + +func (s *Ipv4AddressContext) IpRange() IIpRangeContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpRangeContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIpRangeContext) +} + +func (s *Ipv4AddressContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *Ipv4AddressContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *Ipv4AddressContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterIpv4Address(s) + } +} + +func (s *Ipv4AddressContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitIpv4Address(s) + } +} + +func (p *HostsParser) Ipv4Address() (localctx IIpv4AddressContext) { + localctx = NewIpv4AddressContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 14, HostsParserRULE_ipv4Address) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(71) + p.SingleIPv4Address() + } + p.SetState(73) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HostsParserSLASH { + { + p.SetState(72) + p.IpRange() + } + + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ISingleIPv4AddressContext is an interface to support dynamic dispatch. +type ISingleIPv4AddressContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllIpv4Digit() []IIpv4DigitContext + Ipv4Digit(i int) IIpv4DigitContext + AllDOT() []antlr.TerminalNode + DOT(i int) antlr.TerminalNode + + // IsSingleIPv4AddressContext differentiates from other interfaces. + IsSingleIPv4AddressContext() +} + +type SingleIPv4AddressContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptySingleIPv4AddressContext() *SingleIPv4AddressContext { + var p = new(SingleIPv4AddressContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_singleIPv4Address + return p +} + +func InitEmptySingleIPv4AddressContext(p *SingleIPv4AddressContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_singleIPv4Address +} + +func (*SingleIPv4AddressContext) IsSingleIPv4AddressContext() {} + +func NewSingleIPv4AddressContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SingleIPv4AddressContext { + var p = new(SingleIPv4AddressContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_singleIPv4Address + + return p +} + +func (s *SingleIPv4AddressContext) GetParser() antlr.Parser { return s.parser } + +func (s *SingleIPv4AddressContext) AllIpv4Digit() []IIpv4DigitContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IIpv4DigitContext); ok { + len++ + } + } + + tst := make([]IIpv4DigitContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IIpv4DigitContext); ok { + tst[i] = t.(IIpv4DigitContext) + i++ + } + } + + return tst +} + +func (s *SingleIPv4AddressContext) Ipv4Digit(i int) IIpv4DigitContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpv4DigitContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IIpv4DigitContext) +} + +func (s *SingleIPv4AddressContext) AllDOT() []antlr.TerminalNode { + return s.GetTokens(HostsParserDOT) +} + +func (s *SingleIPv4AddressContext) DOT(i int) antlr.TerminalNode { + return s.GetToken(HostsParserDOT, i) +} + +func (s *SingleIPv4AddressContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *SingleIPv4AddressContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *SingleIPv4AddressContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterSingleIPv4Address(s) + } +} + +func (s *SingleIPv4AddressContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitSingleIPv4Address(s) + } +} + +func (p *HostsParser) SingleIPv4Address() (localctx ISingleIPv4AddressContext) { + localctx = NewSingleIPv4AddressContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 16, HostsParserRULE_singleIPv4Address) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(75) + p.Ipv4Digit() + } + { + p.SetState(76) + p.Match(HostsParserDOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(77) + p.Ipv4Digit() + } + { + p.SetState(78) + p.Match(HostsParserDOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(79) + p.Ipv4Digit() + } + { + p.SetState(80) + p.Match(HostsParserDOT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(81) + p.Ipv4Digit() + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIpv6AddressContext is an interface to support dynamic dispatch. +type IIpv6AddressContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + SingleIPv6Address() ISingleIPv6AddressContext + IpRange() IIpRangeContext + + // IsIpv6AddressContext differentiates from other interfaces. + IsIpv6AddressContext() +} + +type Ipv6AddressContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIpv6AddressContext() *Ipv6AddressContext { + var p = new(Ipv6AddressContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv6Address + return p +} + +func InitEmptyIpv6AddressContext(p *Ipv6AddressContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv6Address +} + +func (*Ipv6AddressContext) IsIpv6AddressContext() {} + +func NewIpv6AddressContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *Ipv6AddressContext { + var p = new(Ipv6AddressContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_ipv6Address + + return p +} + +func (s *Ipv6AddressContext) GetParser() antlr.Parser { return s.parser } + +func (s *Ipv6AddressContext) SingleIPv6Address() ISingleIPv6AddressContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ISingleIPv6AddressContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ISingleIPv6AddressContext) +} + +func (s *Ipv6AddressContext) IpRange() IIpRangeContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpRangeContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIpRangeContext) +} + +func (s *Ipv6AddressContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *Ipv6AddressContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *Ipv6AddressContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterIpv6Address(s) + } +} + +func (s *Ipv6AddressContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitIpv6Address(s) + } +} + +func (p *HostsParser) Ipv6Address() (localctx IIpv6AddressContext) { + localctx = NewIpv6AddressContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 18, HostsParserRULE_ipv6Address) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(83) + p.SingleIPv6Address() + } + p.SetState(85) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == HostsParserSLASH { + { + p.SetState(84) + p.IpRange() + } + + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ISingleIPv6AddressContext is an interface to support dynamic dispatch. +type ISingleIPv6AddressContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllIpv6Octet() []IIpv6OctetContext + Ipv6Octet(i int) IIpv6OctetContext + AllCOLON() []antlr.TerminalNode + COLON(i int) antlr.TerminalNode + + // IsSingleIPv6AddressContext differentiates from other interfaces. + IsSingleIPv6AddressContext() +} + +type SingleIPv6AddressContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptySingleIPv6AddressContext() *SingleIPv6AddressContext { + var p = new(SingleIPv6AddressContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_singleIPv6Address + return p +} + +func InitEmptySingleIPv6AddressContext(p *SingleIPv6AddressContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_singleIPv6Address +} + +func (*SingleIPv6AddressContext) IsSingleIPv6AddressContext() {} + +func NewSingleIPv6AddressContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SingleIPv6AddressContext { + var p = new(SingleIPv6AddressContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_singleIPv6Address + + return p +} + +func (s *SingleIPv6AddressContext) GetParser() antlr.Parser { return s.parser } + +func (s *SingleIPv6AddressContext) AllIpv6Octet() []IIpv6OctetContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IIpv6OctetContext); ok { + len++ + } + } + + tst := make([]IIpv6OctetContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IIpv6OctetContext); ok { + tst[i] = t.(IIpv6OctetContext) + i++ + } + } + + return tst +} + +func (s *SingleIPv6AddressContext) Ipv6Octet(i int) IIpv6OctetContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpv6OctetContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IIpv6OctetContext) +} + +func (s *SingleIPv6AddressContext) AllCOLON() []antlr.TerminalNode { + return s.GetTokens(HostsParserCOLON) +} + +func (s *SingleIPv6AddressContext) COLON(i int) antlr.TerminalNode { + return s.GetToken(HostsParserCOLON, i) +} + +func (s *SingleIPv6AddressContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *SingleIPv6AddressContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *SingleIPv6AddressContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterSingleIPv6Address(s) + } +} + +func (s *SingleIPv6AddressContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitSingleIPv6Address(s) + } +} + +func (p *HostsParser) SingleIPv6Address() (localctx ISingleIPv6AddressContext) { + localctx = NewSingleIPv6AddressContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 20, HostsParserRULE_singleIPv6Address) + var _alt int + + p.EnterOuterAlt(localctx, 1) + p.SetState(90) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = 1 + for ok := true; ok; ok = _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + switch _alt { + case 1: + { + p.SetState(87) + p.Ipv6Octet() + } + { + p.SetState(88) + p.Match(HostsParserCOLON) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + + p.SetState(92) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 9, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + { + p.SetState(94) + p.Ipv6Octet() + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIpv4DigitContext is an interface to support dynamic dispatch. +type IIpv4DigitContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + DIGITS() antlr.TerminalNode + + // IsIpv4DigitContext differentiates from other interfaces. + IsIpv4DigitContext() +} + +type Ipv4DigitContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIpv4DigitContext() *Ipv4DigitContext { + var p = new(Ipv4DigitContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv4Digit + return p +} + +func InitEmptyIpv4DigitContext(p *Ipv4DigitContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv4Digit +} + +func (*Ipv4DigitContext) IsIpv4DigitContext() {} + +func NewIpv4DigitContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *Ipv4DigitContext { + var p = new(Ipv4DigitContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_ipv4Digit + + return p +} + +func (s *Ipv4DigitContext) GetParser() antlr.Parser { return s.parser } + +func (s *Ipv4DigitContext) DIGITS() antlr.TerminalNode { + return s.GetToken(HostsParserDIGITS, 0) +} + +func (s *Ipv4DigitContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *Ipv4DigitContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *Ipv4DigitContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterIpv4Digit(s) + } +} + +func (s *Ipv4DigitContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitIpv4Digit(s) + } +} + +func (p *HostsParser) Ipv4Digit() (localctx IIpv4DigitContext) { + localctx = NewIpv4DigitContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 22, HostsParserRULE_ipv4Digit) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(96) + p.Match(HostsParserDIGITS) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIpv6OctetContext is an interface to support dynamic dispatch. +type IIpv6OctetContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + OCTETS() antlr.TerminalNode + + // IsIpv6OctetContext differentiates from other interfaces. + IsIpv6OctetContext() +} + +type Ipv6OctetContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIpv6OctetContext() *Ipv6OctetContext { + var p = new(Ipv6OctetContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv6Octet + return p +} + +func InitEmptyIpv6OctetContext(p *Ipv6OctetContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipv6Octet +} + +func (*Ipv6OctetContext) IsIpv6OctetContext() {} + +func NewIpv6OctetContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *Ipv6OctetContext { + var p = new(Ipv6OctetContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_ipv6Octet + + return p +} + +func (s *Ipv6OctetContext) GetParser() antlr.Parser { return s.parser } + +func (s *Ipv6OctetContext) OCTETS() antlr.TerminalNode { + return s.GetToken(HostsParserOCTETS, 0) +} + +func (s *Ipv6OctetContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *Ipv6OctetContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *Ipv6OctetContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterIpv6Octet(s) + } +} + +func (s *Ipv6OctetContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitIpv6Octet(s) + } +} + +func (p *HostsParser) Ipv6Octet() (localctx IIpv6OctetContext) { + localctx = NewIpv6OctetContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 24, HostsParserRULE_ipv6Octet) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(98) + p.Match(HostsParserOCTETS) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIpRangeContext is an interface to support dynamic dispatch. +type IIpRangeContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + SLASH() antlr.TerminalNode + IpRangeBits() IIpRangeBitsContext + + // IsIpRangeContext differentiates from other interfaces. + IsIpRangeContext() +} + +type IpRangeContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIpRangeContext() *IpRangeContext { + var p = new(IpRangeContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipRange + return p +} + +func InitEmptyIpRangeContext(p *IpRangeContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipRange +} + +func (*IpRangeContext) IsIpRangeContext() {} + +func NewIpRangeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IpRangeContext { + var p = new(IpRangeContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_ipRange + + return p +} + +func (s *IpRangeContext) GetParser() antlr.Parser { return s.parser } + +func (s *IpRangeContext) SLASH() antlr.TerminalNode { + return s.GetToken(HostsParserSLASH, 0) +} + +func (s *IpRangeContext) IpRangeBits() IIpRangeBitsContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIpRangeBitsContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIpRangeBitsContext) +} + +func (s *IpRangeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IpRangeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *IpRangeContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterIpRange(s) + } +} + +func (s *IpRangeContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitIpRange(s) + } +} + +func (p *HostsParser) IpRange() (localctx IIpRangeContext) { + localctx = NewIpRangeContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 26, HostsParserRULE_ipRange) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(100) + p.Match(HostsParserSLASH) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(101) + p.IpRangeBits() + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IIpRangeBitsContext is an interface to support dynamic dispatch. +type IIpRangeBitsContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + DIGITS() antlr.TerminalNode + + // IsIpRangeBitsContext differentiates from other interfaces. + IsIpRangeBitsContext() +} + +type IpRangeBitsContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIpRangeBitsContext() *IpRangeBitsContext { + var p = new(IpRangeBitsContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipRangeBits + return p +} + +func InitEmptyIpRangeBitsContext(p *IpRangeBitsContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_ipRangeBits +} + +func (*IpRangeBitsContext) IsIpRangeBitsContext() {} + +func NewIpRangeBitsContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IpRangeBitsContext { + var p = new(IpRangeBitsContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_ipRangeBits + + return p +} + +func (s *IpRangeBitsContext) GetParser() antlr.Parser { return s.parser } + +func (s *IpRangeBitsContext) DIGITS() antlr.TerminalNode { + return s.GetToken(HostsParserDIGITS, 0) +} + +func (s *IpRangeBitsContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IpRangeBitsContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *IpRangeBitsContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterIpRangeBits(s) + } +} + +func (s *IpRangeBitsContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitIpRangeBits(s) + } +} + +func (p *HostsParser) IpRangeBits() (localctx IIpRangeBitsContext) { + localctx = NewIpRangeBitsContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 28, HostsParserRULE_ipRangeBits) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(103) + p.Match(HostsParserDIGITS) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ICommentContext is an interface to support dynamic dispatch. +type ICommentContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + COMMENTLINE() antlr.TerminalNode + + // IsCommentContext differentiates from other interfaces. + IsCommentContext() +} + +type CommentContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyCommentContext() *CommentContext { + var p = new(CommentContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_comment + return p +} + +func InitEmptyCommentContext(p *CommentContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_comment +} + +func (*CommentContext) IsCommentContext() {} + +func NewCommentContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *CommentContext { + var p = new(CommentContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_comment + + return p +} + +func (s *CommentContext) GetParser() antlr.Parser { return s.parser } + +func (s *CommentContext) COMMENTLINE() antlr.TerminalNode { + return s.GetToken(HostsParserCOMMENTLINE, 0) +} + +func (s *CommentContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *CommentContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *CommentContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterComment(s) + } +} + +func (s *CommentContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitComment(s) + } +} + +func (p *HostsParser) Comment() (localctx ICommentContext) { + localctx = NewCommentContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 30, HostsParserRULE_comment) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(105) + p.Match(HostsParserCOMMENTLINE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ILeadingCommentContext is an interface to support dynamic dispatch. +type ILeadingCommentContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + COMMENTLINE() antlr.TerminalNode + + // IsLeadingCommentContext differentiates from other interfaces. + IsLeadingCommentContext() +} + +type LeadingCommentContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyLeadingCommentContext() *LeadingCommentContext { + var p = new(LeadingCommentContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_leadingComment + return p +} + +func InitEmptyLeadingCommentContext(p *LeadingCommentContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = HostsParserRULE_leadingComment +} + +func (*LeadingCommentContext) IsLeadingCommentContext() {} + +func NewLeadingCommentContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *LeadingCommentContext { + var p = new(LeadingCommentContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = HostsParserRULE_leadingComment + + return p +} + +func (s *LeadingCommentContext) GetParser() antlr.Parser { return s.parser } + +func (s *LeadingCommentContext) COMMENTLINE() antlr.TerminalNode { + return s.GetToken(HostsParserCOMMENTLINE, 0) +} + +func (s *LeadingCommentContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LeadingCommentContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *LeadingCommentContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.EnterLeadingComment(s) + } +} + +func (s *LeadingCommentContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(HostsListener); ok { + listenerT.ExitLeadingComment(s) + } +} + +func (p *HostsParser) LeadingComment() (localctx ILeadingCommentContext) { + localctx = NewLeadingCommentContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 32, HostsParserRULE_leadingComment) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(107) + p.Match(HostsParserCOMMENTLINE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} diff --git a/root-handler/lsp-utils.go b/root-handler/lsp-utils.go index 2074510..a4337f3 100644 --- a/root-handler/lsp-utils.go +++ b/root-handler/lsp-utils.go @@ -16,12 +16,14 @@ const ( LanguageSSHDConfig SupportedLanguage = "sshd_config" LanguageFstab SupportedLanguage = "fstab" LanguageWireguard SupportedLanguage = "languagewireguard" + LanguageHosts SupportedLanguage = "hosts" ) var AllSupportedLanguages = []string{ string(LanguageSSHDConfig), string(LanguageFstab), string(LanguageWireguard), + string(LanguageHosts), } type FatalFileNotReadableError struct { @@ -59,11 +61,19 @@ var valueToLanguageMap = map[string]SupportedLanguage{ "wireguard": LanguageWireguard, "wg": LanguageWireguard, "languagewireguard": LanguageWireguard, + "host": LanguageHosts, + "hosts": LanguageHosts, + "etc/hosts": LanguageHosts, } -var typeOverwriteRegex = regexp.MustCompile(`^#\?\s*lsp\.language\s*=\s*(\w+)\s*$`) +var typeOverwriteRegex = regexp.MustCompile(`#\?\s*lsp\.language\s*=\s*(\w+)\s*`) var wireguardPattern = regexp.MustCompile(`/wg\d+\.conf$`) +var undetectableError = common.ParseError{ + Line: 0, + Err: LanguageUndetectableError{}, +} + func DetectLanguage( content string, advertisedLanguage string, @@ -99,14 +109,13 @@ func DetectLanguage( return LanguageSSHDConfig, nil case "file:///etc/fstab": return LanguageFstab, nil + case "file:///etc/hosts": + return LanguageHosts, nil } if strings.HasPrefix(uri, "file:///etc/wireguard/") || wireguardPattern.MatchString(uri) { return LanguageWireguard, nil } - return "", common.ParseError{ - Line: 0, - Err: LanguageUndetectableError{}, - } + return "", undetectableError } diff --git a/root-handler/singleton.go b/root-handler/singleton.go index 2fe7073..f06aae0 100644 --- a/root-handler/singleton.go +++ b/root-handler/singleton.go @@ -20,8 +20,14 @@ func (h *RootHandler) AddDocument(uri protocol.DocumentUri, language SupportedLa h.languageMap[uri] = language } -func (h *RootHandler) GetLanguageForDocument(uri protocol.DocumentUri) SupportedLanguage { - return h.languageMap[uri] +func (h *RootHandler) GetLanguageForDocument(uri protocol.DocumentUri) *SupportedLanguage { + language, found := h.languageMap[uri] + + if !found { + return nil + } + + return &language } func (h *RootHandler) RemoveDocument(uri protocol.DocumentUri) { diff --git a/root-handler/text-document-code-action.go b/root-handler/text-document-code-action.go index 5504676..6a01a26 100644 --- a/root-handler/text-document-code-action.go +++ b/root-handler/text-document-code-action.go @@ -1,7 +1,9 @@ package roothandler import ( + hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" + "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) @@ -9,14 +11,26 @@ import ( func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) (any, error) { language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) - switch language { + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return nil, undetectableError.Err + } + + switch *language { case LanguageFstab: - return nil, nil + fallthrough + case LanguageHosts: + return hosts.TextDocumentCodeAction(context, params) case LanguageSSHDConfig: return nil, nil case LanguageWireguard: return wireguard.TextDocumentCodeAction(context, params) } - panic("root-handler/TextDocumentCompletion: unexpected language" + language) + panic("root-handler/TextDocumentCompletion: unexpected language" + *language) } diff --git a/root-handler/text-document-completion.go b/root-handler/text-document-completion.go index 0ee3292..63eb383 100644 --- a/root-handler/text-document-completion.go +++ b/root-handler/text-document-completion.go @@ -2,6 +2,7 @@ package roothandler import ( "config-lsp/handlers/fstab" + hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" "github.com/tliron/glsp" @@ -11,14 +12,26 @@ import ( func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) { language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) - switch language { + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return nil, undetectableError.Err + } + + switch *language { case LanguageFstab: return fstab.TextDocumentCompletion(context, params) case LanguageSSHDConfig: return nil, nil case LanguageWireguard: return wireguard.TextDocumentCompletion(context, params) + case LanguageHosts: + return hosts.TextDocumentCompletion(context, params) } - panic("root-handler/TextDocumentCompletion: unexpected language" + language) + panic("root-handler/TextDocumentCompletion: unexpected language" + *language) } diff --git a/root-handler/text-document-did-change.go b/root-handler/text-document-did-change.go index 5b4af9e..841dac3 100644 --- a/root-handler/text-document-did-change.go +++ b/root-handler/text-document-did-change.go @@ -2,6 +2,7 @@ package roothandler import ( "config-lsp/handlers/fstab" + hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" "github.com/tliron/glsp" @@ -11,14 +12,52 @@ import ( func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error { language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) - switch language { + if language == nil { + content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text + newLanguage, err := initFile( + context, + content, + params.TextDocument.URI, + "", + ) + + if err != nil { + return err + } + + language = newLanguage + + params := &protocol.DidOpenTextDocumentParams{ + TextDocument: protocol.TextDocumentItem{ + URI: params.TextDocument.URI, + Text: content, + Version: params.TextDocument.Version, + LanguageID: string(*language), + }, + } + + switch *language { + case LanguageFstab: + return fstab.TextDocumentDidOpen(context, params) + case LanguageSSHDConfig: + break + case LanguageWireguard: + return wireguard.TextDocumentDidOpen(context, params) + case LanguageHosts: + return hosts.TextDocumentDidOpen(context, params) + } + } + + switch *language { case LanguageFstab: return fstab.TextDocumentDidChange(context, params) case LanguageSSHDConfig: return nil case LanguageWireguard: return wireguard.TextDocumentDidChange(context, params) + case LanguageHosts: + return hosts.TextDocumentDidChange(context, params) } - panic("root-handler/TextDocumentDidChange: unexpected language" + language) + panic("root-handler/TextDocumentDidChange: unexpected language" + *language) } diff --git a/root-handler/text-document-did-close.go b/root-handler/text-document-did-close.go index fbf90e1..7185980 100644 --- a/root-handler/text-document-did-close.go +++ b/root-handler/text-document-did-close.go @@ -1,6 +1,7 @@ package roothandler import ( + hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" "github.com/tliron/glsp" @@ -10,14 +11,26 @@ import ( func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return undetectableError.Err + } + delete(openedFiles, params.TextDocument.URI) rootHandler.RemoveDocument(params.TextDocument.URI) - switch language { + switch *language { case LanguageFstab: case LanguageSSHDConfig: case LanguageWireguard: return wireguard.TextDocumentDidClose(context, params) + case LanguageHosts: + return hosts.TextDocumentDidClose(context, params) default: } diff --git a/root-handler/text-document-did-open.go b/root-handler/text-document-did-open.go index 5623fd8..caaddd7 100644 --- a/root-handler/text-document-did-open.go +++ b/root-handler/text-document-did-open.go @@ -3,6 +3,7 @@ package roothandler import ( "config-lsp/common" fstab "config-lsp/handlers/fstab" + hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" "fmt" @@ -15,31 +16,26 @@ func TextDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocu // Find the file type content := params.TextDocument.Text - language, err := DetectLanguage(content, params.TextDocument.LanguageID, params.TextDocument.URI) + language, err := initFile( + context, + content, + params.TextDocument.URI, + params.TextDocument.LanguageID, + ) if err != nil { - parseError := err.(common.ParseError) - showParseError( - context, - params.TextDocument.URI, - parseError, - ) - - return parseError.Err + return err } - openedFiles[params.TextDocument.URI] = struct{}{} - - // Everything okay, now we can handle the file - rootHandler.AddDocument(params.TextDocument.URI, language) - - switch language { + switch *language { case LanguageFstab: return fstab.TextDocumentDidOpen(context, params) case LanguageSSHDConfig: break case LanguageWireguard: return wireguard.TextDocumentDidOpen(context, params) + case LanguageHosts: + return hosts.TextDocumentDidOpen(context, params) } panic(fmt.Sprintf("unexpected roothandler.SupportedLanguage: %#v", language)) @@ -73,3 +69,30 @@ func showParseError( }, ) } + +func initFile( + context *glsp.Context, + content string, + uri protocol.DocumentUri, + advertisedLanguage string, +) (*SupportedLanguage, error) { + language, err := DetectLanguage(content, advertisedLanguage, uri) + + if err != nil { + parseError := err.(common.ParseError) + showParseError( + context, + uri, + parseError, + ) + + return nil, parseError.Err + } + + openedFiles[uri] = struct{}{} + + // Everything okay, now we can handle the file + rootHandler.AddDocument(uri, language) + + return &language, nil +} diff --git a/root-handler/text-document-hover.go b/root-handler/text-document-hover.go index a973f46..31c67aa 100644 --- a/root-handler/text-document-hover.go +++ b/root-handler/text-document-hover.go @@ -2,6 +2,7 @@ package roothandler import ( "config-lsp/handlers/fstab" + hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" "github.com/tliron/glsp" @@ -11,14 +12,26 @@ import ( func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) { language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) - switch language { - case LanguageFstab: - return fstab.TextDocumentHover(context, params) + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return nil, undetectableError.Err + } + + switch *language { + case LanguageHosts: + return hosts.TextDocumentHover(context, params) case LanguageSSHDConfig: return nil, nil + case LanguageFstab: + return fstab.TextDocumentHover(context, params) case LanguageWireguard: return wireguard.TextDocumentHover(context, params) } - panic("root-handler/TextDocumentHover: unexpected language" + language) + panic("root-handler/TextDocumentHover: unexpected language" + *language) } diff --git a/root-handler/workspace-execute-command.go b/root-handler/workspace-execute-command.go index 934fd9e..e159353 100644 --- a/root-handler/workspace-execute-command.go +++ b/root-handler/workspace-execute-command.go @@ -1,6 +1,7 @@ package roothandler import ( + hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" "strings" @@ -17,6 +18,8 @@ func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteComm switch commandSection { case "wireguard": edit, err = wireguard.WorkspaceExecuteCommand(context, params) + case "hosts": + edit, err = hosts.WorkspaceExecuteCommand(context, params) } if err != nil { diff --git a/utils/strings.go b/utils/strings.go index 1832fbf..4e85934 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -15,3 +15,7 @@ func GetTrimIndex(s string) []int { return indexes[2:4] } + +func SplitIntoLines(s string) []string { + return regexp.MustCompile("\r?\n").Split(s, -1) +} diff --git a/utils/tests.go b/utils/tests.go index 0c09cf8..157f26f 100644 --- a/utils/tests.go +++ b/utils/tests.go @@ -1,9 +1,7 @@ package utils -import "strings" - func Dedent(s string) string { - return strings.TrimLeft(s, "\n") + return s[len("\n"):] } func KeyExists[T comparable, V any](keys map[T]V, key T) bool {