github.com/teddydd/sh@v2.6.4+incompatible/_js/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/gopherjs/gopherjs/js"
    12  
    13  	"mvdan.cc/sh/syntax"
    14  )
    15  
    16  func main() {
    17  	exps := js.Module.Get("exports")
    18  
    19  	exps.Set("syntax", map[string]interface{}{})
    20  
    21  	stx := exps.Get("syntax")
    22  
    23  	// Type helpers just for JS
    24  	stx.Set("NodeType", func(v interface{}) string {
    25  		if v == nil {
    26  			return "nil"
    27  		}
    28  		node, ok := v.(syntax.Node)
    29  		if !ok {
    30  			throw("NodeType requires a Node argument")
    31  		}
    32  		typ := fmt.Sprintf("%T", node)
    33  		if i := strings.LastIndexAny(typ, "*.]"); i >= 0 {
    34  			typ = typ[i+1:]
    35  		}
    36  		return typ
    37  	})
    38  
    39  	// Parser
    40  	stx.Set("NewParser", func(options ...func(interface{})) *js.Object {
    41  		p := syntax.NewParser()
    42  		jp := js.MakeFullWrapper(jsParser{p})
    43  		// Apply the options after we've wrapped the parser, as
    44  		// otherwise we cannot internalise the value.
    45  		for _, opt := range options {
    46  			opt(jp)
    47  		}
    48  		return jp
    49  	})
    50  
    51  	stx.Set("KeepComments", func(v interface{}) {
    52  		syntax.KeepComments(v.(jsParser).Parser)
    53  	})
    54  	stx.Set("Variant", func(l syntax.LangVariant) func(interface{}) {
    55  		if math.IsNaN(float64(l)) {
    56  			throw("Variant requires a LangVariant argument")
    57  		}
    58  		return func(v interface{}) {
    59  			syntax.Variant(l)(v.(jsParser).Parser)
    60  		}
    61  	})
    62  	stx.Set("LangBash", syntax.LangBash)
    63  	stx.Set("LangPOSIX", syntax.LangPOSIX)
    64  	stx.Set("LangMirBSDKorn", syntax.LangMirBSDKorn)
    65  	stx.Set("StopAt", func(word string) func(interface{}) {
    66  		return func(v interface{}) {
    67  			syntax.StopAt(word)(v.(jsParser).Parser)
    68  		}
    69  	})
    70  
    71  	// Printer
    72  	stx.Set("NewPrinter", func() *js.Object {
    73  		p := syntax.NewPrinter()
    74  		return js.MakeFullWrapper(jsPrinter{p})
    75  	})
    76  
    77  	// Syntax utilities
    78  	stx.Set("Walk", func(node syntax.Node, jsFn func(*js.Object) bool) {
    79  		f := func(node syntax.Node) bool {
    80  			if node == nil {
    81  				return jsFn(nil)
    82  			}
    83  			return jsFn(js.MakeFullWrapper(node))
    84  		}
    85  		syntax.Walk(node, f)
    86  
    87  	})
    88  	stx.Set("DebugPrint", func(node syntax.Node) {
    89  		syntax.DebugPrint(os.Stdout, node)
    90  	})
    91  }
    92  
    93  func throw(v interface{}) {
    94  	js.Global.Call("$throwRuntimeError", fmt.Sprint(v))
    95  }
    96  
    97  // streamReader is an io.Readder wrapper for Node's stream.Readable. See
    98  // https://nodejs.org/api/stream.html#stream_class_stream_readable
    99  // TODO: support https://streams.spec.whatwg.org/#rs-class too?
   100  type streamReader struct {
   101  	stream *js.Object
   102  }
   103  
   104  func (r streamReader) Read(p []byte) (n int, err error) {
   105  	obj := r.stream.Call("read", len(p))
   106  	if obj == nil {
   107  		return 0, io.EOF
   108  	}
   109  	bs := []byte(obj.String())
   110  	return copy(p, bs), nil
   111  }
   112  
   113  type jsParser struct {
   114  	*syntax.Parser
   115  }
   116  
   117  func adaptReader(src *js.Object) io.Reader {
   118  	if src.Get("read") != js.Undefined {
   119  		return streamReader{stream: src}
   120  	}
   121  	return strings.NewReader(src.String())
   122  }
   123  
   124  func (p jsParser) Parse(src *js.Object, name string) *js.Object {
   125  	f, err := p.Parser.Parse(adaptReader(src), name)
   126  	if err != nil {
   127  		throw(err)
   128  	}
   129  	return js.MakeFullWrapper(f)
   130  }
   131  
   132  func (p jsParser) Incomplete() bool {
   133  	return p.Parser.Incomplete()
   134  }
   135  
   136  func (p jsParser) Interactive(src *js.Object, fn func([]*syntax.Stmt) bool) {
   137  	err := p.Parser.Interactive(adaptReader(src), fn)
   138  	if err != nil {
   139  		throw(err)
   140  	}
   141  }
   142  
   143  type jsPrinter struct {
   144  	*syntax.Printer
   145  }
   146  
   147  func (p jsPrinter) Print(file *syntax.File) string {
   148  	var buf bytes.Buffer
   149  	if err := p.Printer.Print(&buf, file); err != nil {
   150  		throw(err)
   151  	}
   152  	return buf.String()
   153  }