d2parser: Parse and format @../file imports correctly

This commit is contained in:
Anmol Sethi 2023-06-06 16:15:01 -07:00
parent 35bf5d2c41
commit a5d3cc1429
No known key found for this signature in database
GPG key ID: 8CEF1878FF10ADEB
19 changed files with 141 additions and 56 deletions

View file

@ -28,6 +28,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"path"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
@ -746,6 +747,7 @@ type Import struct {
Range Range `json:"range"` Range Range `json:"range"`
Spread bool `json:"spread"` Spread bool `json:"spread"`
Pre string `json:"pre"`
Path []*StringBox `json:"path"` Path []*StringBox `json:"path"`
} }
@ -1105,3 +1107,10 @@ func (i *Import) IDA() (ida []string) {
} }
return ida return ida
} }
func (i *Import) PathWithPre() string {
if len(i.Path) == 0 {
return ""
}
return path.Join(i.Pre, i.Path[0].Unbox().ScalarString())
}

View file

@ -1,6 +1,7 @@
package d2format package d2format
import ( import (
"path"
"strconv" "strconv"
"strings" "strings"
@ -210,6 +211,11 @@ func (p *printer) _import(i *d2ast.Import) {
p.sb.WriteString("...") p.sb.WriteString("...")
} }
p.sb.WriteString("@") p.sb.WriteString("@")
pre := path.Clean(i.Pre)
if pre != "." {
p.sb.WriteString(pre)
p.sb.WriteRune('/')
}
p.path(i.Path) p.path(i.Path)
} }

View file

@ -641,6 +641,14 @@ x: @file."d2"
x: @./file x: @./file
`, `,
exp: `x: @file exp: `x: @file
`,
},
{
name: "import/4",
in: `
x: @../file
`,
exp: `x: @../file
`, `,
}, },
} }

View file

@ -11,12 +11,12 @@ import (
) )
func (c *compiler) pushImportStack(imp *d2ast.Import) bool { func (c *compiler) pushImportStack(imp *d2ast.Import) bool {
if len(imp.Path) == 0 { if imp.PathWithPre() == "" && imp.Range.Path != "" {
c.errorf(imp, "imports must specify a path to import") c.errorf(imp, "imports must specify a path to import")
return false return false
} }
newPath := imp.Path[0].Unbox().ScalarString() newPath := imp.PathWithPre()
for i, p := range c.importStack { for i, p := range c.importStack {
if newPath == p { if newPath == p {
c.errorf(imp, "detected cyclic import chain: %s", formatCyclicChain(c.importStack[i:])) c.errorf(imp, "detected cyclic import chain: %s", formatCyclicChain(c.importStack[i:]))
@ -61,7 +61,7 @@ func (c *compiler) _import(imp *d2ast.Import) (Node, bool) {
} }
func (c *compiler) __import(imp *d2ast.Import) (*Map, bool) { func (c *compiler) __import(imp *d2ast.Import) (*Map, bool) {
impPath := imp.Path[0].Unbox().ScalarString() impPath := imp.PathWithPre()
if path.IsAbs(impPath) { if path.IsAbs(impPath) {
c.errorf(imp, "import paths must be relative") c.errorf(imp, "import paths must be relative")
return nil, false return nil, false

View file

@ -1720,12 +1720,20 @@ func (p *parser) parseImport(spread bool) *d2ast.Import {
imp.Range.Start = imp.Range.Start.SubtractString("...", p.utf16) imp.Range.Start = imp.Range.Start.SubtractString("...", p.utf16)
} }
s, eof := p.peekn(2) var pre strings.Builder
if eof || s != "./" { for {
r, eof := p.peek()
if eof {
break
}
if r != '.' && r != '/' {
p.rewind() p.rewind()
} else { break
}
pre.WriteRune(r)
p.commit() p.commit()
} }
imp.Pre = pre.String()
k := p.parseKey() k := p.parseKey()
if k == nil { if k == nil {

View file

@ -454,7 +454,9 @@ func testImport(t *testing.T) {
{ {
text: "...@../file", text: "...@../file",
assert: func(t testing.TB, ast *d2ast.Map, err error) { assert: func(t testing.TB, ast *d2ast.Map, err error) {
assert.ErrorString(t, err, "d2/testdata/d2parser/TestParse/import/#06.d2:1:5: unexpected text after import") assert.Success(t, err)
assert.True(t, ast.Nodes[0].Import.Spread)
assert.Equal(t, "../file", ast.Nodes[0].Import.PathWithPre())
}, },
}, },
{ {
@ -463,10 +465,18 @@ func testImport(t *testing.T) {
assert.ErrorString(t, err, "d2/testdata/d2parser/TestParse/import/#07.d2:1:1: @file is not a valid import, did you mean ...@file?") assert.ErrorString(t, err, "d2/testdata/d2parser/TestParse/import/#07.d2:1:1: @file is not a valid import, did you mean ...@file?")
}, },
}, },
{
text: "...@./../.././file",
assert: func(t testing.TB, ast *d2ast.Map, err error) {
assert.Success(t, err)
assert.True(t, ast.Nodes[0].Import.Spread)
assert.Equal(t, "../../file", ast.Nodes[0].Import.PathWithPre())
},
},
{ {
text: "meow: ...@file", text: "meow: ...@file",
assert: func(t testing.TB, ast *d2ast.Map, err error) { assert: func(t testing.TB, ast *d2ast.Map, err error) {
assert.ErrorString(t, err, "d2/testdata/d2parser/TestParse/import/#08.d2:1:7: unquoted strings cannot begin with ...@ as that's import spread syntax") assert.ErrorString(t, err, "d2/testdata/d2parser/TestParse/import/#09.d2:1:7: unquoted strings cannot begin with ...@ as that's import spread syntax")
}, },
}, },
} }

View file

@ -69,6 +69,7 @@
"import": { "import": {
"range": "index.d2,0:3:3-0:7:7", "range": "index.d2,0:3:3-0:7:7",
"spread": false, "spread": false,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -213,6 +213,7 @@
"import": { "import": {
"range": "index.d2,0:3:3-0:7:7", "range": "index.d2,0:3:3-0:7:7",
"spread": false, "spread": false,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -65,6 +65,7 @@
"import": { "import": {
"range": "index.d2,0:3:3-0:7:7", "range": "index.d2,0:3:3-0:7:7",
"spread": false, "spread": false,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -213,6 +213,7 @@
"import": { "import": {
"range": "index.d2,0:3:3-0:8:8", "range": "index.d2,0:3:3-0:8:8",
"spread": false, "spread": false,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -26,6 +26,7 @@
"import": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#00.d2,0:3:3-0:8:8", "range": "d2/testdata/d2parser/TestParse/import/#00.d2,0:3:3-0:8:8",
"spread": false, "spread": false,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -26,6 +26,7 @@
"import": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#01.d2,0:3:3-0:11:11", "range": "d2/testdata/d2parser/TestParse/import/#01.d2,0:3:3-0:11:11",
"spread": false, "spread": false,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -6,6 +6,7 @@
"import": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#02.d2,0:0:0-0:11:11", "range": "d2/testdata/d2parser/TestParse/import/#02.d2,0:0:0-0:11:11",
"spread": true, "spread": true,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -30,6 +30,7 @@
"import": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#03.d2,0:4:4-0:15:15", "range": "d2/testdata/d2parser/TestParse/import/#03.d2,0:4:4-0:15:15",
"spread": true, "spread": true,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -6,6 +6,7 @@
"import": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#04.d2,0:0:0-0:13:13", "range": "d2/testdata/d2parser/TestParse/import/#04.d2,0:0:0-0:13:13",
"spread": true, "spread": true,
"pre": "",
"path": [ "path": [
{ {
"double_quoted_string": { "double_quoted_string": {

View file

@ -6,6 +6,7 @@
"import": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#05.d2,0:0:0-0:13:13", "range": "d2/testdata/d2parser/TestParse/import/#05.d2,0:0:0-0:13:13",
"spread": true, "spread": true,
"pre": "",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {

View file

@ -4,19 +4,25 @@
"nodes": [ "nodes": [
{ {
"import": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#06.d2,0:0:0-0:4:4", "range": "d2/testdata/d2parser/TestParse/import/#06.d2,0:0:0-0:11:11",
"spread": true, "spread": true,
"path": null "pre": "../",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2parser/TestParse/import/#06.d2,0:7:7-0:11:11",
"value": [
{
"string": "file",
"raw_string": "file"
}
]
}
}
]
} }
} }
] ]
}, },
"err": { "err": null
"errs": [
{
"range": "d2/testdata/d2parser/TestParse/import/#06.d2,0:4:4-0:11:11",
"errmsg": "d2/testdata/d2parser/TestParse/import/#06.d2:1:5: unexpected text after import"
}
]
}
} }

View file

@ -1,48 +1,28 @@
{ {
"ast": { "ast": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:14:14", "range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:18:18",
"nodes": [ "nodes": [
{ {
"map_key": { "import": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:14:14", "range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:18:18",
"key": { "spread": true,
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:4:4", "pre": "./../.././",
"path": [ "path": [
{ {
"unquoted_string": { "unquoted_string": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:4:4", "range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:14:14-0:18:18",
"value": [ "value": [
{ {
"string": "meow", "string": "file",
"raw_string": "meow" "raw_string": "file"
}
]
}
} }
] ]
} }
} }
] ]
}, },
"primary": {}, "err": null
"value": {
"unquoted_string": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:6:6-0:14:14",
"value": [
{
"string": "...@file",
"raw_string": "...@file"
}
]
}
}
}
}
]
},
"err": {
"errs": [
{
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:6:6-0:10:10",
"errmsg": "d2/testdata/d2parser/TestParse/import/#08.d2:1:7: unquoted strings cannot begin with ...@ as that's import spread syntax"
}
]
}
} }

48
testdata/d2parser/TestParse/import/#09.exp.json generated vendored Normal file
View file

@ -0,0 +1,48 @@
{
"ast": {
"range": "d2/testdata/d2parser/TestParse/import/#09.d2,0:0:0-0:14:14",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2parser/TestParse/import/#09.d2,0:0:0-0:14:14",
"key": {
"range": "d2/testdata/d2parser/TestParse/import/#09.d2,0:0:0-0:4:4",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2parser/TestParse/import/#09.d2,0:0:0-0:4:4",
"value": [
{
"string": "meow",
"raw_string": "meow"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "d2/testdata/d2parser/TestParse/import/#09.d2,0:6:6-0:14:14",
"value": [
{
"string": "...@file",
"raw_string": "...@file"
}
]
}
}
}
}
]
},
"err": {
"errs": [
{
"range": "d2/testdata/d2parser/TestParse/import/#09.d2,0:6:6-0:10:10",
"errmsg": "d2/testdata/d2parser/TestParse/import/#09.d2:1:7: unquoted strings cannot begin with ...@ as that's import spread syntax"
}
]
}
}