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"
"fmt"
"math/big"
"path"
"strconv"
"strings"
"unicode"
@ -746,6 +747,7 @@ type Import struct {
Range Range `json:"range"`
Spread bool `json:"spread"`
Pre string `json:"pre"`
Path []*StringBox `json:"path"`
}
@ -1105,3 +1107,10 @@ func (i *Import) IDA() (ida []string) {
}
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
import (
"path"
"strconv"
"strings"
@ -210,6 +211,11 @@ func (p *printer) _import(i *d2ast.Import) {
p.sb.WriteString("...")
}
p.sb.WriteString("@")
pre := path.Clean(i.Pre)
if pre != "." {
p.sb.WriteString(pre)
p.sb.WriteRune('/')
}
p.path(i.Path)
}

View file

@ -641,6 +641,14 @@ x: @file."d2"
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 {
if len(imp.Path) == 0 {
if imp.PathWithPre() == "" && imp.Range.Path != "" {
c.errorf(imp, "imports must specify a path to import")
return false
}
newPath := imp.Path[0].Unbox().ScalarString()
newPath := imp.PathWithPre()
for i, p := range c.importStack {
if newPath == p {
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) {
impPath := imp.Path[0].Unbox().ScalarString()
impPath := imp.PathWithPre()
if path.IsAbs(impPath) {
c.errorf(imp, "import paths must be relative")
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)
}
s, eof := p.peekn(2)
if eof || s != "./" {
p.rewind()
} else {
var pre strings.Builder
for {
r, eof := p.peek()
if eof {
break
}
if r != '.' && r != '/' {
p.rewind()
break
}
pre.WriteRune(r)
p.commit()
}
imp.Pre = pre.String()
k := p.parseKey()
if k == nil {

View file

@ -454,7 +454,9 @@ func testImport(t *testing.T) {
{
text: "...@../file",
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?")
},
},
{
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",
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": {
"range": "index.d2,0:3:3-0:7:7",
"spread": false,
"pre": "",
"path": [
{
"unquoted_string": {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,19 +4,25 @@
"nodes": [
{
"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,
"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": {
"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"
}
]
}
"err": null
}

View file

@ -1,48 +1,28 @@
{
"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": [
{
"map_key": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:14:14",
"key": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:4:4",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:4:4",
"value": [
{
"string": "meow",
"raw_string": "meow"
}
]
}
"import": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:0:0-0:18:18",
"spread": true,
"pre": "./../.././",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2parser/TestParse/import/#08.d2,0:14:14-0:18:18",
"value": [
{
"string": "file",
"raw_string": "file"
}
]
}
]
},
"primary": {},
"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"
}
]
}
"err": null
}

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"
}
]
}
}