blob: bdb71bb7d4d9a1a0a4fc456b4279c7d646ad8943 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Todd Wang232d6492015-02-25 18:04:54 -08005package parse
6
7import (
8 "fmt"
9 "path"
10 "strconv"
11 "strings"
12
Jiri Simsaffceefa2015-02-28 11:03:34 -080013 "v.io/x/ref/lib/vdl/vdlutil"
Todd Wang232d6492015-02-25 18:04:54 -080014)
15
16// Pos captures positional information during parsing.
17type Pos struct {
18 Line int // Line number, starting at 1
19 Col int // Column number (character count), starting at 1
20}
21
22// StringPos holds a string and a Pos.
23type StringPos struct {
24 String string
25 Pos Pos
26}
27
28// Returns true iff this Pos has been initialized. The zero Pos is invalid.
29func (p Pos) IsValid() bool {
30 return p.Line > 0 && p.Col > 0
31}
32
33func (p Pos) String() string {
34 if !p.IsValid() {
35 return "[no pos]"
36 }
37 return fmt.Sprintf("%v:%v", p.Line, p.Col)
38}
39
40// InferPackageName returns the package name from a group of files. Every file
41// must specify the same package name, otherwise an error is reported in errs.
42func InferPackageName(files []*File, errs *vdlutil.Errors) (pkgName string) {
43 var firstFile string
44 for _, f := range files {
45 switch {
46 case pkgName == "":
47 firstFile = f.BaseName
48 pkgName = f.PackageDef.Name
49 case pkgName != f.PackageDef.Name:
50 errs.Errorf("Files in the same directory must be in the same package; %v has package %v, but %v has package %v", firstFile, pkgName, f.BaseName, f.PackageDef.Name)
51 }
52 }
53 return
54}
55
56// Representation of the components of an vdl file. These data types represent
57// the parse tree generated by the parse.
58
59// File represents a parsed vdl file.
60type File struct {
61 BaseName string // Base name of the vdl file, e.g. "foo.vdl"
Todd Wangb90b8de2015-03-31 20:04:13 -070062 Doc string // Top-level file documentation
Todd Wang232d6492015-02-25 18:04:54 -080063 PackageDef NamePos // Name, position and docs of the "package" clause
Todd Wangb90b8de2015-03-31 20:04:13 -070064 Imports []*Import // Imports listed in this file
Todd Wang232d6492015-02-25 18:04:54 -080065 ErrorDefs []*ErrorDef // Errors defined in this file
66 TypeDefs []*TypeDef // Types defined in this file
67 ConstDefs []*ConstDef // Consts defined in this file
68 Interfaces []*Interface // Interfaces defined in this file
69}
70
71// Config represents a parsed config file. Config files use a similar syntax as
72// vdl files, with similar concepts.
73type Config struct {
74 FileName string // Config file name, e.g. "a/b/foo.config"
Todd Wangb90b8de2015-03-31 20:04:13 -070075 Doc string // Top-level config file documentation
Todd Wang232d6492015-02-25 18:04:54 -080076 ConfigDef NamePos // Name, position and docs of the "config" clause
77 Imports []*Import // Imports listed in this file.
78 Config ConstExpr // Const expression exported from this config.
79 ConstDefs []*ConstDef // Consts defined in this file.
80}
81
82// AddImports adds the path imports that don't already exist to c.
83func (c *Config) AddImports(path ...string) {
84 for _, p := range path {
85 if !c.HasImport(p) {
86 c.Imports = append(c.Imports, &Import{Path: p})
87 }
88 }
89}
90
91// HasImport returns true iff path exists in c.Imports.
92func (c *Config) HasImport(path string) bool {
93 for _, imp := range c.Imports {
94 if imp.Path == path {
95 return true
96 }
97 }
98 return false
99}
100
101// Import represents an import definition, which is used to import other
102// packages into an vdl file. An example of the syntax in the vdl file:
103// import foo "some/package/path"
104type Import struct {
105 NamePos // e.g. foo (from above), or typically empty
106 Path string // e.g. "some/package/path" (from above)
107}
108
109// LocalName returns the name used locally within the File to refer to the
110// imported package.
111func (i *Import) LocalName() string {
112 if i.Name != "" {
113 return i.Name
114 }
115 return path.Base(i.Path)
116}
117
118// ErrorDef represents an error definition.
119type ErrorDef struct {
120 NamePos // error name, pos and doc
121 Params []*Field // list of positional parameters
122 Actions []StringPos // list of action code identifiers
123 Formats []LangFmt // list of language / format pairs
124}
125
126// LangFmt represents a language / format string pair.
127type LangFmt struct {
128 Lang StringPos // IETF language tag
129 Fmt StringPos // i18n format string in the given language
130}
131
132// Pos returns the position of the LangFmt.
133func (x LangFmt) Pos() Pos {
134 if x.Lang.Pos.IsValid() {
135 return x.Lang.Pos
136 }
137 return x.Fmt.Pos
138}
139
140// Interface represents a set of embedded interfaces and methods.
141type Interface struct {
142 NamePos // interface name, pos and doc
143 Embeds []*NamePos // names of embedded interfaces
144 Methods []*Method // list of methods
145}
146
147// Method represents a method in an interface.
148type Method struct {
149 NamePos // method name, pos and doc
150 InArgs []*Field // list of positional in-args
151 OutArgs []*Field // list of positional out-args
152 InStream Type // in-stream type, may be nil
153 OutStream Type // out-stream type, may be nil
154 Tags []ConstExpr // list of method tags
155}
156
157// Field represents fields in structs as well as method arguments.
158type Field struct {
159 NamePos // field name, pos and doc
160 Type Type // field type, never nil
161}
162
163// NamePos represents a name, its associated position and documentation.
164type NamePos struct {
165 Name string
166 Pos Pos // position of first character in name
167 Doc string // docs that occur before the item
168 DocSuffix string // docs that occur on the same line after the item
169}
170
171func (x *File) String() string { return fmt.Sprintf("%+v", *x) }
172func (x *Import) String() string { return fmt.Sprintf("%+v", *x) }
173func (x *ErrorDef) String() string { return fmt.Sprintf("%+v", *x) }
174func (x *Interface) String() string { return fmt.Sprintf("%+v", *x) }
175func (x *Method) String() string { return fmt.Sprintf("%+v", *x) }
176func (x *Field) String() string { return fmt.Sprintf("%+v", *x) }
177func (x *NamePos) String() string { return fmt.Sprintf("%+v", *x) }
178
179// QuoteStripDoc takes a Doc string, which includes comment markers /**/ and
180// double-slash, and returns a raw-quoted string.
181//
182// TODO(toddw): This should remove comment markers. This is non-trivial, since
183// we should handle removing leading whitespace "rectangles", and might want to
184// retain inline /**/ or adjacent /**/ on the same line. For now we just leave
185// them in the output.
186func QuoteStripDoc(doc string) string {
187 trimmed := strings.Trim(doc, "\n")
188 if strconv.CanBackquote(doc) {
189 return "`" + trimmed + "`"
190 }
191 return strconv.Quote(trimmed)
192}