Skip to content

Commit

Permalink
fix: js parser bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Musat authored and gabotechs committed Dec 24, 2022
1 parent 9a8a9bf commit b78407b
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 49 deletions.
57 changes: 32 additions & 25 deletions internal/js/grammar/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
)

type Deconstruction struct {
Names []string `"{" @Ident (AS Ident)? ("," @Ident (AS Ident)?)* "}"`
Names []string `"{" @Ident ("as" Ident)? ("," (@Ident ("as" Ident)?)?)* "}"`
}

type AllImport struct {
Alias string `ALL (AS @Ident)?`
Alias string `ALL ("as" @Ident)?`
}

type SelectionImport struct {
Expand All @@ -25,47 +25,54 @@ type Imported struct {
}

type StaticImport struct {
Imported *Imported `IMPORT (@@ FROM)?`
Imported *Imported `"import" (@@ "from")?`
Path string `@String`
}

type DynamicImport struct {
Path string `IMPORT "(" @String ")"`
Path string `"import" "(" @String ")"`
}
type Import struct {
DynamicImport *DynamicImport `@@`
StaticImport *StaticImport `| @@`
}

type File struct {
Imports []*Import `((@@? ANY?)!)*`
Imports []*Import `((@@? (ANY | FALSE_IMPORT_1 | FALSE_IMPORT_2)?)!)*`
}

var (
lex = lexer.MustSimple([]lexer.SimpleRule{
// Keywords.
{"IMPORT", "import"},
{"AS", "as"},
{"COMMA", ","},
{"COLON", ";"},
{"FROM", "from"},
{"ALL", `\*`},
{"BRACKET_L", `{`},
{"BRACKET_R", `}`},
{"PARENTHESIS_L", `\(`},
{"PARENTHESIS_R", `\)`},
// Other.
{"Ident", `[_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*`},
{"String", `'[^']*'|"[^"]*"`},
{"Comment", `//.*|/\*.*?\*/`},
{"Whitespace", `\s+`},
// Any.
{"ANY", `.`},
lex = lexer.MustStateful(lexer.Rules{
"Root": {
{"FALSE_IMPORT_1", `import[_$a-zA-Z0-9\\xA0-\\uFFFF]+`, nil},
{"FALSE_IMPORT_2", `[_$a-zA-Z0-9\\xA0-\\uFFFF]+import`, nil},
{"IMPORT", `import`, lexer.Push("Import")},
{"Whitespace", `\s+`, nil},
{"ANY", `.`, nil},
},
"Import": {
// Keywords.
{"COMMA", ",", nil},
{"ALL", `\*`, nil},
{"BRACKET_L", `{`, nil},
{"BRACKET_R", `}`, nil},
{"PARENTHESIS_L", `\(`, nil},
{"PARENTHESIS_R", `\)`, nil},
// Other.
{"Ident", `[_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*`, nil},
{"String", `'[^']*'|"[^"]*"`, lexer.Pop()},
{"Comment", `//.*|/\*.*?\*/`, nil},
{"Whitespace", `\s+`, nil},
},
})
Parser = participle.MustBuild[File](
parser = participle.MustBuild[File](
participle.Lexer(lex),
participle.Elide("Whitespace", "Comment"),
participle.Unquote("String"),
participle.UseLookahead(2),
)
)

func Parse(content []byte) (*File, error) {
return parser.ParseBytes("", content)
}
89 changes: 81 additions & 8 deletions internal/js/grammar/grammar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ import (
func TestGrammar(t *testing.T) {
tests := []struct {
Name string
Content string
ExpectedStatic []string
ExpectedDynamic []string
}{
{
Name: "import * from 'file'",
ExpectedStatic: []string{"file"},
},
{
Name: "useless shit, import * from 'file' dumb suffix",
ExpectedStatic: []string{"file"},
},
{
Name: "import * as Something from \"file\"",
ExpectedStatic: []string{"file"},
Expand Down Expand Up @@ -56,30 +59,100 @@ func TestGrammar(t *testing.T) {
Name: "(import('something'))",
ExpectedDynamic: []string{"something"},
},
{
Name: "import ('something'); const a",
ExpectedDynamic: []string{"something"},
},
{
Name: "import { One } from 'one'\nimport \"two\"",
ExpectedStatic: []string{"one", "two"},
},
{
Name: "All imports",
Content: "import-regex.js",
Name: "import { from } from 'somewhere'",
ExpectedStatic: []string{"somewhere"},
},
{
Name: "import { from, } from 'somewhere'",
ExpectedStatic: []string{"somewhere"},
},
{
Name: "const importVariable = []\nimport 'variable'",
ExpectedStatic: []string{"variable"},
},
{
Name: "import-regex.js",
ExpectedStatic: []string{
"@angular2/core",
"module-name",
"module-name ",
" module-name",
"module-name",
"module-name",
"module-name",
"@angular2/core",
"$module-name",
"module-name",
"module-name",
"module-name ",
" module-name",
"module-name",
"module-name",
"module-name",
"@angular2/core",
"$module-name",
"module-name",
"module-name",
"module-name",
"react",
"redux-form",
"module-name",
"../geometries/Geometries.js",
"../geometries/Geometries.js",
"redux-form",
"./views/ListView",
"./views/AddView",
"./views/EditView",
"redux-form",
"./views/ListView",
"./views/AddView",
"./views/EditView",
},
ExpectedDynamic: []string{
"whatever.js",
"whatever.js",
},
},
{
Name: "test-1.js",
ExpectedStatic: []string{
"react",
"../services/apollo",
"../config",
"../styles/theme",
"history",
"../constants/routing",
"../components/dialogs/SnackBar",
"@apollo/client",
"react-router",
"./contexts/AppContext",
"@material-ui/core",
"../views/SlicerView/contexts/SlicingContext",
},
},
}

for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
a := require.New(t)
var content []byte
if strings.HasSuffix(tt.Content, ".js") {
if strings.HasSuffix(tt.Name, ".js") {
var err error
content, err = os.ReadFile(path.Join("grammar_test", tt.Content))
content, err = os.ReadFile(path.Join("grammar_test", tt.Name))
a.NoError(err)
} else if tt.Content != "" {
content = []byte(tt.Content)
} else {
content = []byte(tt.Name)
}
parsed, err := Parser.ParseBytes("", content)
parsed, err := parser.ParseBytes("", content)
a.NoError(err)

var staticResults []string
Expand Down
12 changes: 12 additions & 0 deletions internal/js/grammar/grammar_test/test-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react"
import { CustomApolloClient } from "../services/apollo";
import config from "../config";
import createTheme, { PADDING_EX } from "../styles/theme";
import { createBrowserHistory } from "history";
import { ROUTING_LOGIN } from "../constants/routing";
import { LogProvider } from "../components/dialogs/SnackBar";
import { ApolloProvider } from "@apollo/client";
import { Router as RouterProvider } from "react-router";
import { AppContextProvider } from "./contexts/AppContext";
import { MuiThemeProvider } from "@material-ui/core";
import { SlicingContextProvider } from "../views/SlicerView/contexts/SlicingContext";
36 changes: 20 additions & 16 deletions internal/js/resolve.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package js

import (
"errors"
"fmt"
"path"
"path/filepath"
Expand All @@ -15,20 +16,26 @@ type Import struct {
AbsPath string
}

func (p *Parser) ParseImport(unparsed []byte, dir string) (*Import, error) {
func (p *Parser) ParseImport(imp *grammar.Import, dir string) (*Import, error) {
result := Import{
Names: make([]string, 0),
}
matches := grammar.ParsePathFromImport(unparsed)
if len(matches) == 0 {
return nil, fmt.Errorf("could not parse import importFrom from '%s'", string(unparsed))
importFrom := ""
if imp == nil {
return nil, errors.New("programming error: nil import")
} else if imp.StaticImport != nil {
importFrom = imp.StaticImport.Path
} else if imp.DynamicImport != nil {
importFrom = imp.DynamicImport.Path
} else {
return nil, errors.New("programming error: import is neither static or dynamic")
}
importFrom := strings.Trim(string(matches[0]), " \n\"'")

// 1. If import is relative.
if importFrom[0] == '.' {
result.AbsPath = getFileAbsPath(path.Join(dir, importFrom))
if result.AbsPath == "" {
return nil, fmt.Errorf("could not perform relative import for '%s' because the file or dir was not found", string(unparsed))
return nil, fmt.Errorf("could not perform relative import for '%s' because the file or dir was not found", importFrom)
}
return &result, nil
}
Expand Down Expand Up @@ -78,23 +85,20 @@ type FileInfo struct {

func (p *Parser) ParseFileInfo(content []byte, dir string) (*FileInfo, error) {
fileInfo := FileInfo{}
for _, importMatch := range grammar.ParseImport(content) {
parsedImport, err := p.ParseImport(importMatch, dir)
jsFile, err := grammar.Parse(content)
if err != nil {
return nil, fmt.Errorf("error parsing file: %w\n\n%s", err, string(content))
}

for _, imp := range jsFile.Imports {
parsedImport, err := p.ParseImport(imp, dir)
if err != nil {
return nil, err
} else if parsedImport != nil {
fileInfo.imports = append(fileInfo.imports, parsedImport)
}
}

for _, exportMatch := range grammar.ParseExport(content) {
parsedExport, err := p.ParseExport(exportMatch, dir)
if err != nil {
return nil, err
} else if parsedExport != nil {
fileInfo.exports = append(fileInfo.exports, parsedExport)
}
}
return &fileInfo, nil
}

Expand Down

0 comments on commit b78407b

Please sign in to comment.