Skip to content

Commit a4c9ce7

Browse files
committed
initial commit
1 parent 5fb9ab9 commit a4c9ce7

21 files changed

+8300
-0
lines changed

Jsonnet.g4

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// language spec: https://jsonnet.org/ref/spec.html
2+
// language tutorial: https://jsonnet.org/learning/tutorial.html
3+
// grammar from: https://gist.github.com/ironchefpython/84380aa60871853dc86719dd598c35e4
4+
// slightly changed the slice rule
5+
grammar Jsonnet;
6+
7+
jsonnet
8+
: expr EOF
9+
;
10+
11+
expr
12+
: value=(NULL | TRUE | FALSE | SELF | DOLLAR | STRING | NUMBER ) # Value
13+
| '(' expr ')' # Parens
14+
| '{' objinside? '}' # Object
15+
| '[' ( elems+=expr (',' elems+=expr)* )? ','? ']' # Array
16+
| '[' expr ','? forspec+ ']' # ArrayComp
17+
| expr '.' ID # Index
18+
| expr '[' expr ']' # Index
19+
| expr '[' startIdx=expr? ':' endIdx=expr? (':' step=expr? )? ']' # Slice
20+
| SUPER . ID # IndexSuper
21+
| SUPER '[' expr ']' # IndexSuper
22+
| expr '(' args? ')' TAILSTRICT? # Apply
23+
| ID # Var
24+
| IF expr THEN expr ( ELSE expr )? # IfThenElse
25+
| op=(PLUS | MINUS | NOT | BITNOT) expr # UnaryExpr
26+
| expr op=(MULTIPLY | DIVIDE | MODULUS) expr # BinaryExpr
27+
| expr op=(PLUS | MINUS) expr # BinaryExpr
28+
| expr op=(SHIFTLEFT | SHIFTRIGHT) expr # BinaryExpr
29+
| expr op=(GT | GE | LT | LE | IN) expr # BinaryExpr
30+
| expr op=(EQUALS | NOTEQUALS) expr # BinaryExpr
31+
| expr op=BITAND expr # BinaryExpr
32+
| expr op=BITXOR expr # BinaryExpr
33+
| expr op=BITOR expr # BinaryExpr
34+
| expr op=AND expr # BinaryExpr
35+
| expr op=OR expr # BinaryExpr
36+
| expr '{' objinside? '}' # ApplyBrace
37+
| FUNCTION '(' params? ')' expr # Function
38+
| assertion ';' expr # Assert
39+
| IMPORT STRING # Import
40+
| IMPORTSTR STRING # Import
41+
| ERROR expr # ErrorExpr
42+
| expr IN SUPER # InSuper
43+
| LOCAL binds+=bind (',' binds+=bind)* ';' expr # LocalBind
44+
;
45+
46+
47+
objinside
48+
: members+=member (',' members+=member)* ','? # Members
49+
| ( objlocal ',' )* '[' key=expr ']' ':' value=expr ( ',' objlocal )* ','? forspec+
50+
# ObjectComp
51+
;
52+
53+
member
54+
: objlocal | assertion | field
55+
;
56+
57+
field
58+
: fieldname PLUS? visibility expr # ValueField
59+
| fieldname '(' params? ')' visibility expr # FunctionField
60+
;
61+
62+
visibility
63+
: ':'
64+
| ':' ':'
65+
| ':' ':' ':'
66+
;
67+
68+
objlocal
69+
: LOCAL bind
70+
;
71+
72+
forspec
73+
: FOR ID IN expr ifspec*
74+
;
75+
76+
ifspec
77+
: IF expr
78+
;
79+
80+
fieldname
81+
: ID
82+
| STRING
83+
| '[' expr ']'
84+
;
85+
86+
assertion
87+
: ASSERT condition=expr (':' message=expr)?
88+
;
89+
90+
91+
bind
92+
: ID '=' expr # ValueBind
93+
| ID '(' params? ')' '=' expr # FunctionBind
94+
;
95+
96+
97+
args
98+
: pos+=expr ( ',' pos+=expr )* ( ',' names+=ID '=' named+=expr )* ','?
99+
| names+=ID '=' named+=expr ( ',' names+=ID '=' named+=expr )* ','?
100+
;
101+
102+
params
103+
: pos+=ID ( ',' pos+=ID )* ( ',' names+=ID '=' defaults+=expr )* ','?
104+
| names+=ID '=' defaults+=expr ( ',' names+=ID '=' defaults+=expr )* ','?
105+
;
106+
107+
108+
DOLLAR : '$';
109+
110+
ASSERT : 'assert';
111+
ELSE : 'else';
112+
ERROR : 'error';
113+
FALSE : 'false';
114+
FOR : 'for';
115+
FUNCTION : 'function';
116+
IF : 'if';
117+
IMPORT : 'import';
118+
IMPORTSTR : 'importstr';
119+
LOCAL : 'local';
120+
NULL : 'null';
121+
SELF : 'self';
122+
SUPER : 'super';
123+
TAILSTRICT: 'tailstrict';
124+
THEN : 'then';
125+
TRUE : 'true';
126+
127+
EQUALS : '==' ;
128+
NOTEQUALS : '!=';
129+
PLUS : '+';
130+
MINUS : '-';
131+
MULTIPLY : '*';
132+
DIVIDE : '/';
133+
MODULUS : '%';
134+
AND : '&&';
135+
OR : '||';
136+
NOT : '!';
137+
GT : '>';
138+
GE : '>=';
139+
LT : '<';
140+
LE : '<=';
141+
IN : 'in';
142+
SHIFTLEFT : '<<';
143+
SHIFTRIGHT: '>>';
144+
BITNOT : '~';
145+
BITAND : '&';
146+
BITXOR : '^';
147+
BITOR : '|';
148+
149+
STRING
150+
: '"' (ESCAPES | UNICODE | ~["\\\u0000-\u001F])* '"'
151+
| '\'' (ESCAPES | UNICODE | ~['\\\u0000-\u001F])* '\''
152+
| '@' '"' ('""' | ~["])* '"'
153+
| '@' '\'' ('\'\'' | ~['])* '\''
154+
| '@' '\'' ('\'\'' | ~['])* '\''
155+
| '|||' ( ~'|' | '|' ~'|' | '||' ~'|' )* '|||'
156+
;
157+
158+
NUMBER: INT ( '.' DIGIT+ )? EXP?;
159+
160+
ID: ALPHA (ALPHA | DIGIT)*;
161+
162+
fragment ESCAPES: '\\' ["'\\/bfnrt];
163+
fragment DIGIT: [0-9];
164+
fragment ALPHA: [_a-zA-Z];
165+
fragment UNICODE: 'u' HEX HEX HEX HEX;
166+
fragment HEX: [0-9a-fA-F];
167+
fragment INT: '0' | [1-9] DIGIT*;
168+
fragment EXP: [Ee] [+\-]? DIGIT+;
169+
170+
Whitespace
171+
: [ \t]+ -> skip
172+
;
173+
174+
Newline
175+
: ( '\r' '\n'? | '\n' ) -> skip
176+
;
177+
178+
BlockComment
179+
: '/*' .*? '*/' -> skip
180+
;
181+
182+
LineComment
183+
: ('//'|'#') ~[\r\n]* -> skip
184+
;

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Language Server Indexing Format Implementation for JSonnet
2+
3+
🚨 This implementation is still in very early stage and follows the latest LSIF specification closely.
4+
5+
## Language Server Index Format
6+
7+
The purpose of the Language Server Index Format (LSIF) is to define a standard format for language servers or other programming tools to dump their knowledge about a workspace. This dump can later be used to answer language server [LSP](https://microsoft.github.io/language-server-protocol/) requests for the same workspace without running the language server itself. Since much of the information would be invalidated by a change to the workspace, the dumped information typically excludes requests used when mutating a document. So, for example, the result of a code complete request is typically not part of such a dump.
8+
9+
A first draft specification can be found [here](https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md).
10+
11+
## JSonnet
12+
13+
[JSonnet](https://jsonnet.org) is a data templating language related to JSON.
14+
15+
## Implementation
16+
17+
In true open-source spirit the implementation is made from parts from these places:
18+
19+
- The parser is generated by [Antlr4](https://www.antlr.org) from a grammar file modified from
20+
[this Github Gist](https://gist.github.com/ironchefpython/84380aa60871853dc86719dd598c35e4).
21+
22+
- The protocol.go code was borrowed and modified from [lsif-go](https://github.com/sourcegraph/lsif-go).
23+
24+
- The LSIF dumper is a modification of indexer.go from [lsif-go](https://github.com/sourcegraph/lsif-go).
25+
26+
- The scope implementation is a modification of [go.types.Scope](https://golang.org/pkg/go/types/#Scope).
27+
28+
Many thanks to these projects.
29+
30+
## Status
31+
32+
This was put together quickly to learn LSIF and JSonnet. It is missing many features, for example
33+
hover results, documentation comments (parser currently discards comments). It can serve as a starting point for
34+
more production-ready implementations for JSonnet or other languages.

cmd/lsif-jsonnet/main.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
10+
flag "github.com/spf13/pflag"
11+
"lsif-jsonnet/dumper"
12+
"lsif-jsonnet/protocol"
13+
"lsif-jsonnet/refs"
14+
)
15+
16+
const version = "1.0.0"
17+
const versionString = version + ", protocol version " + protocol.Version
18+
19+
func main() {
20+
if err := mainImpl(); err != nil {
21+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
22+
os.Exit(1)
23+
}
24+
}
25+
26+
func mainImpl() error {
27+
var help *bool = flag.BoolP("help", "h", false, "This message")
28+
var jps *[]string = flag.StringArrayP("jpath", "J", nil, "Specify an additional library search dir (right-most wins)")
29+
var outFile *string = flag.StringP("output-file", "o", "", "Write to the output file rather than stdout")
30+
31+
flag.Parse()
32+
33+
if *help || flag.NArg() != 1 {
34+
fmt.Fprintf(os.Stderr, "lsif-jsonnet %s\n", versionString)
35+
fmt.Fprintln(os.Stderr, "Basic usage: lsif-jsonnet -J <libpath> -o <out_file> <jsonnet_file>")
36+
flag.PrintDefaults()
37+
os.Exit(0)
38+
}
39+
40+
var lps []string
41+
42+
for _, p := range filepath.SplitList(os.Getenv("JSONNET_PATH")) {
43+
lps = append(lps, p)
44+
}
45+
46+
for _, pp := range *jps {
47+
for _, p := range filepath.SplitList(pp) {
48+
lps = append(lps, p)
49+
}
50+
}
51+
52+
pathResolver, err := refs.NewPathResolver(lps)
53+
if err != nil {
54+
return fmt.Errorf("failed to instantiate path resolver: %w\n", err)
55+
}
56+
57+
inFile := flag.Arg(0)
58+
59+
absInFile, err := filepath.Abs(inFile)
60+
if err != nil {
61+
return fmt.Errorf("failed to make pathh %s absolute: %w\n", inFile, err)
62+
}
63+
64+
inDir := filepath.Dir(absInFile)
65+
err = pathResolver.AddPath(inDir)
66+
if err != nil {
67+
return fmt.Errorf("failed to add %s to path resolver: %w\n", inDir, err)
68+
}
69+
70+
ll, err := refs.ParseFile(absInFile, pathResolver)
71+
if err != nil {
72+
return fmt.Errorf("failed to parse %s: %w\n", inFile, err)
73+
}
74+
75+
var outWriter io.Writer
76+
77+
if len(*outFile) > 0 {
78+
f, err := os.Create(*outFile)
79+
if err != nil {
80+
return fmt.Errorf("failed to create %s: %w\n", *outFile, err)
81+
}
82+
defer f.Close()
83+
84+
bw := bufio.NewWriter(f)
85+
defer bw.Flush()
86+
87+
outWriter = bw
88+
} else {
89+
outWriter = os.Stdout
90+
}
91+
92+
toolInfo := &protocol.ToolInfo{
93+
Name: "lsif-jsonnet",
94+
Version: version,
95+
Args: os.Args[1:],
96+
}
97+
98+
dmpr := dumper.NewDumper(inDir, outWriter)
99+
100+
return dmpr.DumpProject(toolInfo, ll)
101+
}

0 commit comments

Comments
 (0)