blob: 977ce8a92210c6af6873750d438a02e402e0cdb9 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package state
import (
"fmt"
"regexp"
"strings"
)
var (
// https://fuchsia.dev/fuchsia-src/development/languages/fidl/reference/language.md#identifiers
fidlIdentifierPattern = `[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?`
// Although "library" can be used anywhere (e.g. as a type name), this regex
// is robust because the the library declaration must appear at the top of
// the file (only comments and whitespace can precede it).
libraryRegexp = regexp.MustCompile(`` +
`^(?:\s*//[^\n]*\n)*\s*` +
`library\s+(` +
fidlIdentifierPattern +
`(?:\.` + fidlIdentifierPattern + `)*` +
`)\s*;`)
// TODO: make this agnostic to platform
// Although "using" can be used anywhere (e.g. as a type name), this regex
// is robust because it only matches imports of platform libraries (ones
// that start with fidl, fuchsia, or test) and the special library zx. The
// only problem is it can match commented imports, so we have to check for
// this after matching.
platformImportRegexp = regexp.MustCompile(`` +
`\busing\s+(` +
`zx|` +
`(?:fidl|fuchsia|test)` +
`(?:\.` + fidlIdentifierPattern + `)+` +
`)` +
`(?:\s+as\s+` + fidlIdentifierPattern + `)?` +
`\s*;`)
)
// LibraryMatch is the result of ParseLibraryMatch and ParsePlatformImportsMatch
// and represents the location and value of a `library` or `using` declaration
// in a FIDL file.
type LibraryMatch struct {
Lib string
Range Range
}
// TODO: we should keep track of URI <-> fidlLibrary mapping, so we don't have
// to parse for `library` name each time
// LibraryOfFile extracts the name of the FIDL library of the provided FIDL
// file, by extracting its `library` declaration.
func LibraryOfFile(file string) (string, error) {
fidlLib, ok := ParseLibraryMatch(file)
if !ok {
return "", fmt.Errorf("Could not find library of file '%s'", file)
}
return fidlLib.Lib, nil
}
// ParseLibraryMatch extracts with regex the `library __;` declaration from the
// FIDL file passed in, if there is one, or returns false if not.
func ParseLibraryMatch(fidl string) (LibraryMatch, bool) {
m := libraryRegexp.FindStringSubmatchIndex(fidl)
if m == nil {
return LibraryMatch{}, false
}
return toLibraryMatch(fidl, m[2], m[3]), true
}
// ParsePlatformImportsMatch extracts with regex the `using __;` import
// declarations in the FIDL file passed in.
func ParsePlatformImportsMatch(fidl string) []LibraryMatch {
var libs []LibraryMatch
for _, m := range platformImportRegexp.FindAllStringSubmatchIndex(fidl, -1) {
// See the comment in parsePlatformImports.
i := strings.LastIndexByte(fidl[:m[2]], '\n')
if i == -1 || !strings.Contains(fidl[i:m[2]], "//") {
libs = append(libs, toLibraryMatch(fidl, m[2], m[3]))
}
}
return libs
}
func toLibraryMatch(fidl string, start, end int) LibraryMatch {
return LibraryMatch{
Lib: fidl[start:end],
Range: Range{
Start: Position{
Line: strings.Count(fidl[:start], "\n"),
Character: start - strings.LastIndexByte(fidl[:start], '\n') - 1,
},
End: Position{
Line: strings.Count(fidl[:end], "\n"),
Character: end - strings.LastIndexByte(fidl[:end], '\n') - 1,
},
},
}
}