Skip to content

Commit

Permalink
make go2v parse js_parser.go
Browse files Browse the repository at this point in the history
  • Loading branch information
medvednikov committed Dec 28, 2024
1 parent 4535ac3 commit cbd50c7
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 36 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ jobs:
./go2v complex_tests/esbuild/linker/linker.go
./go2v complex_tests/esbuild/bundler/bundler.go
./go2v complex_tests/esbuild/css_parser/css_parser.go
./go2v complex_tests/esbuild/js_parser/js_parser.go
79 changes: 45 additions & 34 deletions complex_tests/esbuild/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1895,21 +1895,23 @@ func (p *parser) logAssignToDefine(r logger.Range, name string, expr js_ast.Expr
if id, ok := expr.Data.(*js_ast.EIdentifier); ok {
parts = append(parts, p.loadNameFromRef(id.Ref))
break
} else if dot, ok := expr.Data.(*js_ast.EDot); ok {
parts = append(parts, dot.Name)
parts = append(parts, ".")
expr = dot.Target
} else if index, ok := expr.Data.(*js_ast.EIndex); ok {
if str, ok := index.Index.Data.(*js_ast.EString); ok {
parts = append(parts, "]")
parts = append(parts, string(helpers.QuoteSingle(helpers.UTF16ToString(str.Value), false)))
parts = append(parts, "[")
expr = index.Target
} else {
if dot, ok := expr.Data.(*js_ast.EDot); ok {
parts = append(parts, dot.Name)
parts = append(parts, ".")
expr = dot.Target
} else {
return
if index, ok := expr.Data.(*js_ast.EIndex); ok {
if str, ok := index.Index.Data.(*js_ast.EString); ok {
parts = append(parts, "]")
parts = append(parts, string(helpers.QuoteSingle(helpers.UTF16ToString(str.Value), false)))
parts = append(parts, "[")
expr = index.Target
} else {
return
}
}
}
} else {
return
}
}
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
Expand Down Expand Up @@ -2069,7 +2071,7 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op
p.lexer.Next()

case js_lexer.TPrivateIdentifier:
if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.True && len(opts.decorators) > 0 {
if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.true_ && len(opts.decorators) > 0 {
p.log.AddError(&p.tracker, p.lexer.Range(), "TypeScript experimental decorators cannot be used on private identifiers")
} else if !opts.isClass {
p.lexer.Expected(js_lexer.TIdentifier)
Expand Down Expand Up @@ -2182,7 +2184,7 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op

if prop, ok := p.parseProperty(startLoc, kind, opts, nil); ok &&
prop.Kind == js_ast.PropertyField && prop.ValueOrNil.Data == nil &&
(p.options.ts.Config.ExperimentalDecorators == config.True && len(opts.decorators) > 0) {
(p.options.ts.Config.ExperimentalDecorators == config.true_ && len(opts.decorators) > 0) {
// If this is a well-formed class field with the "declare" keyword,
// only keep the declaration to preserve its side-effects when
// there are TypeScript experimental decorators present:
Expand Down Expand Up @@ -2219,7 +2221,7 @@ func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, op

if prop, ok := p.parseProperty(startLoc, kind, opts, nil); ok &&
prop.Kind == js_ast.PropertyField && prop.ValueOrNil.Data == nil &&
(p.options.ts.Config.ExperimentalDecorators == config.True && len(opts.decorators) > 0) {
(p.options.ts.Config.ExperimentalDecorators == config.true_ && len(opts.decorators) > 0) {
// If this is a well-formed class field with the "abstract" keyword,
// only keep the declaration to preserve its side-effects when
// there are TypeScript experimental decorators present:
Expand Down Expand Up @@ -3318,8 +3320,8 @@ type exprFlag uint8

const (
exprFlagDecorator exprFlag = 1 << iota
exprFlagForLoopInit
exprFlagForAwaitLoopInit
exprFlagForLoopInit = 2 << iota
exprFlagForAwaitLoopInit = 3 << iota
)

func (p *parser) parsePrefix(level js_ast.L, errors *deferredErrors, flags exprFlag) js_ast.Expr {
Expand Down Expand Up @@ -5234,10 +5236,10 @@ func (p *parser) parseJSXElement(loc logger.Loc) js_ast.Expr {
case js_lexer.TStringLiteral:
if p.options.jsx.Preserve {
nullableChildren = append(nullableChildren, js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EJSXText{Raw: p.lexer.Raw()}})
} else if str := p.lexer.StringLiteral(); len(str) > 0 {
} else {
if str := p.lexer.StringLiteral(); len(str) > 0 {
nullableChildren = append(nullableChildren, js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EString{Value: str}})
} else {
// Skip this token if it turned out to be empty after trimming
}
}
p.lexer.NextJSXElementChild()

Expand Down Expand Up @@ -6381,7 +6383,7 @@ func (p *parser) parseClass(classKeyword logger.Range, name *ast.LocRef, classOp
// Users that want the wrong behavior can either set "useDefineForClassFields"
// to false in "tsconfig.json" explicitly, or set TypeScript's "target" to
// "ES2021" or earlier in their in "tsconfig.json" file.
useDefineForClassFields := !p.options.ts.Parse || p.options.ts.Config.UseDefineForClassFields == config.True ||
useDefineForClassFields := !p.options.ts.Parse || p.options.ts.Config.UseDefineForClassFields == config.true_ ||
(p.options.ts.Config.UseDefineForClassFields == config.Unspecified && p.options.ts.Config.Target != config.TSTargetBelowES2022)

return js_ast.Class{
Expand All @@ -6399,7 +6401,7 @@ func (p *parser) parseClass(classKeyword logger.Range, name *ast.LocRef, classOp
// class fields, and so we must lower decorators so they behave correctly.
ShouldLowerStandardDecorators: (len(classOpts.decorators) > 0 || hasPropertyDecorator) &&
((!p.options.ts.Parse && p.options.unsupportedJSFeatures.Has(compat.Decorators)) ||
(p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators != config.True &&
(p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators != config.true_ &&
(p.options.unsupportedJSFeatures.Has(compat.Decorators) || !useDefineForClassFields))),

UseDefineForClassFields: useDefineForClassFields,
Expand Down Expand Up @@ -6654,22 +6656,22 @@ type decoratorContextFlags uint8

const (
decoratorBeforeClassExpr = 1 << iota
decoratorInClassExpr
decoratorInFnArgs
decoratorInClassExpr = 2 << iota
decoratorInFnArgs = 3 << iota
)

func (p *parser) parseDecorators(decoratorScope *js_ast.Scope, classKeyword logger.Range, context decoratorContextFlags) (decorators []js_ast.Decorator) {
if p.lexer.Token == js_lexer.TAt {
if p.options.ts.Parse {
if p.options.ts.Config.ExperimentalDecorators == config.True {
if p.options.ts.Config.ExperimentalDecorators == config.true_ {
if (context & decoratorInClassExpr) != 0 {
p.lexer.AddRangeErrorWithNotes(p.lexer.Range(), "TypeScript experimental decorators can only be used with class declarations",
[]logger.MsgData{p.tracker.MsgData(classKeyword, "This is a class expression, not a class declaration:")})
} else if (context & decoratorBeforeClassExpr) != 0 {
p.log.AddError(&p.tracker, p.lexer.Range(), "TypeScript experimental decorators cannot be used in expression position")
}
} else {
if (context&decoratorInFnArgs) != 0 && p.options.ts.Config.ExperimentalDecorators != config.True {
if (context&decoratorInFnArgs) != 0 && p.options.ts.Config.ExperimentalDecorators != config.True_ {
p.log.AddErrorWithNotes(&p.tracker, p.lexer.Range(), "Parameter decorators only work when experimental decorators are enabled", []logger.MsgData{{
Text: "You can enable experimental decorators by adding \"experimentalDecorators\": true to your \"tsconfig.json\" file.",
}})
Expand All @@ -6693,7 +6695,7 @@ func (p *parser) parseDecorators(decoratorScope *js_ast.Scope, classKeyword logg
p.lexer.Next()

var value js_ast.Expr
if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.True {
if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.True_ {
// TypeScript's experimental decorator syntax is more permissive than
// JavaScript. Parse a new/call expression with "exprFlagDecorator" so
// we ignore EIndex expressions, since they may be part of a computed
Expand Down Expand Up @@ -9085,10 +9087,12 @@ func (p *parser) mangleStmts(stmts []js_ast.Stmt, kind stmtsKind) []js_ast.Stmt
result[len(result)-1] = stmt
s.InitOrNil = js_ast.Stmt{Loc: prevStmt.Loc, Data: &js_ast.SExpr{Value: prevS.Value}}
continue
} else if s2, ok := s.InitOrNil.Data.(*js_ast.SExpr); ok {
} else {
if s2, ok := s.InitOrNil.Data.(*js_ast.SExpr); ok {
result[len(result)-1] = stmt
s.InitOrNil = js_ast.Stmt{Loc: prevStmt.Loc, Data: &js_ast.SExpr{Value: js_ast.JoinWithComma(prevS.Value, s2.Value)}}
continue
}
}
} else {
// Insert the previous variable declaration into the for loop
Expand Down Expand Up @@ -9237,7 +9241,8 @@ func (p *parser) mangleStmts(stmts []js_ast.Stmt, kind stmtsKind) []js_ast.Stmt
break returnLoop
}
}
} else if lastThrow, ok := lastStmt.Data.(*js_ast.SThrow); ok {
} else {
if lastThrow, ok := lastStmt.Data.(*js_ast.SThrow); ok {
// "if (a) throw b; if (c) throw d; throw e;" => "throw a ? b : c ? d : e;"
throwLoop:
for len(result) >= 2 {
Expand Down Expand Up @@ -9294,6 +9299,7 @@ func (p *parser) mangleStmts(stmts []js_ast.Stmt, kind stmtsKind) []js_ast.Stmt
}
}
}
}
}

return result
Expand Down Expand Up @@ -12108,6 +12114,7 @@ func (p *parser) warnAboutTypeofAndString(a js_ast.Expr, b js_ast.Expr, order ty
}
}

/*
if typeof, ok := a.Data.(*js_ast.EUnary); ok && typeof.Op == js_ast.UnOpTypeof {
if str, ok := b.Data.(*js_ast.EString); ok {
value := helpers.UTF16ToString(str.Value)
Expand All @@ -12134,6 +12141,7 @@ func (p *parser) warnAboutTypeofAndString(a js_ast.Expr, b js_ast.Expr, order ty
}
}
}
*/
}

func (p *parser) warnAboutEqualityCheck(op string, value js_ast.Expr, afterOpLoc logger.Loc) bool {
Expand Down Expand Up @@ -13308,17 +13316,17 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
// "__source"
args = append(args, js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EObject{
Properties: []js_ast.Property{
{
js_ast.Property{
Kind: js_ast.PropertyField,
Key: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("fileName")}},
ValueOrNil: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(p.source.PrettyPath)}},
},
{
js_ast.Property{
Kind: js_ast.PropertyField,
Key: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("lineNumber")}},
ValueOrNil: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: float64(jsxSourceLine + 1)}}, // 1-based lines
},
{
js_ast.Property{
Kind: js_ast.PropertyField,
Key: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("columnNumber")}},
ValueOrNil: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: float64(jsxSourceColumn + 1)}}, // 1-based columns
Expand Down Expand Up @@ -13866,9 +13874,12 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
e.Value, _ = p.visitExprInOut(e.Value, exprIn{assignTarget: e.Op.UnaryAssignTarget()})

// Compile-time "typeof" evaluation
/*
// XTODO
if typeof, ok := js_ast.TypeofWithoutSideEffects(e.Value.Data); ok {
return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(typeof)}}, exprOut{}
}
*/

case js_ast.UnOpDelete:
// Warn about code that tries to do "delete super.foo"
Expand Down Expand Up @@ -16981,7 +16992,7 @@ func newParser(log logger.Log, source logger.Source, lexer js_lexer.Lexer, optio
if len(options.dropLabels) > 0 {
p.dropLabelsMap = make(map[string]struct{})
for _, name := range options.dropLabels {
p.dropLabelsMap[name] = struct{}{}
//p.dropLabelsMap[name] = struct{}{}
}
}

Expand Down
5 changes: 5 additions & 0 deletions expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ fn quoted_lit(s string, quote string) string {
prefix = 'r'
}

// Handle '`' => `\``
if go_quote == `'` {
no_quotes = no_quotes.replace('`', '\\`')
}

return '${prefix}${quote2}${no_quotes}${quote2}'
}

Expand Down
3 changes: 2 additions & 1 deletion fn_decl.v
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,14 @@ fn (mut app App) func_params(params FieldList) {
app.typ(param.typ)
} else {
for j, name in param.names {
app.gen(name.name)
app.gen(app.go2v_ident(name.name))
app.gen(' ')
app.force_upper = true
app.typ(param.typ)
if j < param.names.len - 1 {
app.gen(',')
}
app.cur_fn_names[name.name] = true // Register the parameter in this scope to fix shadowin
}
}
// app.gen(type_or_ident(param.typ))
Expand Down
11 changes: 11 additions & 0 deletions stmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,22 @@ fn (mut app App) if_stmt(node IfStmt) {
// else if ... {
if node.else_ is IfStmt {
app.genln('else')
if node.else_.init.tok != '' {
// handle `else if x, ok := ...; ok {` => `else { mut ok ... if ... }`
app.genln('{')
// app.genln('//LOOL0')
}
app.if_stmt(node.else_)
if node.else_.init.tok != '' {
app.genln('}')
}
}
// else {
else if node.else_ is BlockStmt {
app.genln('else')
if node.else_.list.len > 0 && node.else_.list[0] is IfStmt {
app.genln('//LOOL')
}
app.block_stmt(node.else_)
}
}
Expand Down
2 changes: 2 additions & 0 deletions todo.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
- Fix shadowing ("redefinition of xxx")
- Fix `else if x := .... ; x {`
- Implement `if obj, ok := json.Data.(*js_ast.EObject); ok {`
if str, ok := b.Data.(*js_ast.EString); ok {
- []u8(byteptr) cast ???
- `for x := ` variable shadowing
- `x := struct{}{}`
- `^uint32(chunkIndex)`
- `[]sourcemap.SourceMapShift{{}}`
- `const x = 1 << iota`

2 changes: 1 addition & 1 deletion util.v
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fn (mut app App) go2v_ident(ident string) string {
*/
}

const v_keywords_which_are_not_go_keywords = ['match', 'lock', 'fn', 'enum']
const v_keywords_which_are_not_go_keywords = ['match', 'lock', 'fn', 'enum', 'in', 'as']

fn go2v_ident2(ident string) string {
x := ident.camel_to_snake() // to_lower()) // TODO ?
Expand Down

0 comments on commit cbd50c7

Please sign in to comment.