github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/cmd/llgoi/llgoi.go (about) 1 //===- llgoi.go - llgo-based Go REPL --------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This is llgoi, a Go REPL based on llgo and the LLVM JIT. 11 // 12 //===----------------------------------------------------------------------===// 13 14 package main 15 16 import ( 17 "bytes" 18 "errors" 19 "fmt" 20 "go/ast" 21 "go/build" 22 "go/parser" 23 "go/scanner" 24 "go/token" 25 "io" 26 "os" 27 "os/exec" 28 "path/filepath" 29 "runtime/debug" 30 "strconv" 31 "strings" 32 "unsafe" 33 34 "llvm.org/llgo/driver" 35 "llvm.org/llgo/irgen" 36 "llvm.org/llgo/third_party/gotools/go/types" 37 "llvm.org/llgo/third_party/liner" 38 "llvm.org/llvm/bindings/go/llvm" 39 ) 40 41 func getInstPrefix() (string, error) { 42 path, err := exec.LookPath(os.Args[0]) 43 if err != nil { 44 return "", err 45 } 46 47 path, err = filepath.EvalSymlinks(path) 48 if err != nil { 49 return "", err 50 } 51 52 prefix := filepath.Join(path, "..", "..") 53 return prefix, nil 54 } 55 56 func llvmVersion() string { 57 return strings.Replace(llvm.Version, "svn", "", 1) 58 } 59 60 type line struct { 61 line string 62 isStmt bool 63 declName string 64 assigns []string 65 66 parens, bracks, braces int 67 } 68 69 type interp struct { 70 engine llvm.ExecutionEngine 71 72 liner *liner.State 73 pendingLine line 74 75 copts irgen.CompilerOptions 76 77 imports []*types.Package 78 scope map[string]types.Object 79 80 pkgmap map[string]*types.Package 81 pkgnum int 82 } 83 84 func (in *interp) makeCompilerOptions() error { 85 prefix, err := getInstPrefix() 86 if err != nil { 87 return err 88 } 89 90 importPaths := []string{filepath.Join(prefix, "lib", "go", "llgo-"+llvmVersion())} 91 in.copts = irgen.CompilerOptions{ 92 TargetTriple: llvm.DefaultTargetTriple(), 93 ImportPaths: importPaths, 94 GenerateDebug: true, 95 Packages: in.pkgmap, 96 } 97 err = in.copts.MakeImporter() 98 if err != nil { 99 return err 100 } 101 102 origImporter := in.copts.Importer 103 in.copts.Importer = func(pkgmap map[string]*types.Package, pkgpath string) (*types.Package, error) { 104 if pkg, ok := pkgmap[pkgpath]; ok && pkg.Complete() { 105 return pkg, nil 106 } 107 return origImporter(pkgmap, pkgpath) 108 } 109 return nil 110 } 111 112 func (in *interp) init() error { 113 in.liner = liner.NewLiner() 114 in.scope = make(map[string]types.Object) 115 in.pkgmap = make(map[string]*types.Package) 116 117 err := in.makeCompilerOptions() 118 if err != nil { 119 return err 120 } 121 122 return nil 123 } 124 125 func (in *interp) dispose() { 126 in.liner.Close() 127 in.engine.Dispose() 128 } 129 130 func (in *interp) loadSourcePackageFromCode(pkgcode, pkgpath string, copts irgen.CompilerOptions) (*types.Package, error) { 131 fset := token.NewFileSet() 132 file, err := parser.ParseFile(fset, "<input>", pkgcode, parser.DeclarationErrors|parser.ParseComments) 133 if err != nil { 134 return nil, err 135 } 136 137 files := []*ast.File{file} 138 139 return in.loadSourcePackage(fset, files, pkgpath, copts) 140 } 141 142 func (in *interp) loadSourcePackage(fset *token.FileSet, files []*ast.File, pkgpath string, copts irgen.CompilerOptions) (pkg *types.Package, err error) { 143 compiler, err := irgen.NewCompiler(copts) 144 if err != nil { 145 return 146 } 147 148 module, err := compiler.Compile(fset, files, pkgpath) 149 if err != nil { 150 return 151 } 152 pkg = module.Package 153 154 if in.engine.C != nil { 155 in.engine.AddModule(module.Module) 156 } else { 157 options := llvm.NewMCJITCompilerOptions() 158 in.engine, err = llvm.NewMCJITCompiler(module.Module, options) 159 if err != nil { 160 return 161 } 162 } 163 164 importname := irgen.ManglePackagePath(pkgpath) + "..import$descriptor" 165 importglobal := module.Module.NamedGlobal(importname) 166 167 var importfunc func() 168 *(*unsafe.Pointer)(unsafe.Pointer(&importfunc)) = in.engine.PointerToGlobal(importglobal) 169 170 defer func() { 171 p := recover() 172 if p != nil { 173 err = fmt.Errorf("panic: %v\n%v", p, string(debug.Stack())) 174 } 175 }() 176 importfunc() 177 in.pkgmap[pkgpath] = pkg 178 return 179 } 180 181 func (in *interp) augmentPackageScope(pkg *types.Package) { 182 for _, obj := range in.scope { 183 pkg.Scope().Insert(obj) 184 } 185 } 186 187 func (l *line) append(str string, assigns []string) { 188 var s scanner.Scanner 189 fset := token.NewFileSet() 190 file := fset.AddFile("", fset.Base(), len(str)) 191 s.Init(file, []byte(str), nil, 0) 192 193 _, tok, _ := s.Scan() 194 if l.line == "" { 195 switch tok { 196 case token.FOR, token.GO, token.IF, token.LBRACE, token.SELECT, token.SWITCH: 197 l.isStmt = true 198 case token.CONST, token.FUNC, token.TYPE, token.VAR: 199 var lit string 200 _, tok, lit = s.Scan() 201 if tok == token.IDENT { 202 l.declName = lit 203 } 204 } 205 } 206 207 for tok != token.EOF { 208 switch tok { 209 case token.LPAREN: 210 l.parens++ 211 case token.RPAREN: 212 l.parens-- 213 case token.LBRACE: 214 l.braces++ 215 case token.RBRACE: 216 l.braces-- 217 case token.LBRACK: 218 l.bracks++ 219 case token.RBRACK: 220 l.bracks-- 221 case token.DEC, token.INC, 222 token.ASSIGN, token.ADD_ASSIGN, token.SUB_ASSIGN, 223 token.MUL_ASSIGN, token.QUO_ASSIGN, token.REM_ASSIGN, 224 token.AND_ASSIGN, token.OR_ASSIGN, token.XOR_ASSIGN, 225 token.SHL_ASSIGN, token.SHR_ASSIGN, token.AND_NOT_ASSIGN: 226 if l.parens == 0 && l.bracks == 0 && l.braces == 0 { 227 l.isStmt = true 228 } 229 } 230 _, tok, _ = s.Scan() 231 } 232 233 if l.line == "" { 234 l.assigns = assigns 235 } 236 l.line += str 237 } 238 239 func (l *line) ready() bool { 240 return l.parens <= 0 && l.bracks <= 0 && l.braces <= 0 241 } 242 243 func (in *interp) readExprLine(str string, assigns []string) error { 244 in.pendingLine.append(str, assigns) 245 246 if in.pendingLine.ready() { 247 err := in.interpretLine(in.pendingLine) 248 in.pendingLine = line{} 249 return err 250 } else { 251 return nil 252 } 253 } 254 255 func (in *interp) interpretLine(l line) error { 256 pkgname := fmt.Sprintf("input%05d", in.pkgnum) 257 in.pkgnum++ 258 259 pkg := types.NewPackage(pkgname, pkgname) 260 scope := pkg.Scope() 261 262 for _, imppkg := range in.imports { 263 obj := types.NewPkgName(token.NoPos, pkg, imppkg.Name(), imppkg) 264 scope.Insert(obj) 265 } 266 267 in.augmentPackageScope(pkg) 268 269 var tv types.TypeAndValue 270 if l.declName == "" && !l.isStmt { 271 var err error 272 tv, err = types.Eval(l.line, pkg, scope) 273 if err != nil { 274 return err 275 } 276 } 277 278 var code bytes.Buffer 279 fmt.Fprintf(&code, "package %s", pkgname) 280 code.WriteString("\n\nimport __fmt__ \"fmt\"\n") 281 code.WriteString("import __os__ \"os\"\n") 282 283 for _, pkg := range in.imports { 284 fmt.Fprintf(&code, "import %q\n", pkg.Path()) 285 } 286 287 if l.declName != "" { 288 code.WriteString(l.line) 289 } else if !l.isStmt && tv.IsValue() { 290 var typs []types.Type 291 if tuple, ok := tv.Type.(*types.Tuple); ok { 292 typs = make([]types.Type, tuple.Len()) 293 for i := range typs { 294 typs[i] = tuple.At(i).Type() 295 } 296 } else { 297 typs = []types.Type{tv.Type} 298 } 299 if len(l.assigns) == 2 && tv.HasOk() { 300 typs = append(typs, types.Typ[types.Bool]) 301 } 302 if len(l.assigns) != 0 && len(l.assigns) != len(typs) { 303 return errors.New("return value mismatch") 304 } 305 306 code.WriteString("var ") 307 for i := range typs { 308 if i != 0 { 309 code.WriteString(", ") 310 } 311 if len(l.assigns) != 0 && l.assigns[i] != "" { 312 if _, ok := in.scope[l.assigns[i]]; ok { 313 fmt.Fprintf(&code, "__llgoiV%d", i) 314 } else { 315 code.WriteString(l.assigns[i]) 316 } 317 } else { 318 fmt.Fprintf(&code, "__llgoiV%d", i) 319 } 320 } 321 fmt.Fprintf(&code, " = %s\n\n", l.line) 322 323 code.WriteString("func init() {\n\t") 324 for i, t := range typs { 325 var varname, prefix string 326 if len(l.assigns) != 0 && l.assigns[i] != "" { 327 if _, ok := in.scope[l.assigns[i]]; ok { 328 fmt.Fprintf(&code, "\t%s = __llgoiV%d\n", l.assigns[i], i) 329 } 330 varname = l.assigns[i] 331 prefix = l.assigns[i] 332 } else { 333 varname = fmt.Sprintf("__llgoiV%d", i) 334 prefix = fmt.Sprintf("#%d", i) 335 } 336 if _, ok := t.Underlying().(*types.Interface); ok { 337 fmt.Fprintf(&code, "\t__fmt__.Printf(\"%s %s (%%T) = %%+v\\n\", %s, %s)\n", prefix, t.String(), varname, varname) 338 } else { 339 fmt.Fprintf(&code, "\t__fmt__.Printf(\"%s %s = %%+v\\n\", %s)\n", prefix, t.String(), varname) 340 } 341 } 342 code.WriteString("}") 343 } else { 344 if len(l.assigns) != 0 { 345 return errors.New("return value mismatch") 346 } 347 348 fmt.Fprintf(&code, "func init() {\n\t%s}", l.line) 349 } 350 351 copts := in.copts 352 copts.PackageCreated = in.augmentPackageScope 353 copts.DisableUnusedImportCheck = true 354 pkg, err := in.loadSourcePackageFromCode(code.String(), pkgname, copts) 355 if err != nil { 356 return err 357 } 358 359 in.imports = append(in.imports, pkg) 360 361 for _, assign := range l.assigns { 362 if assign != "" { 363 if _, ok := in.scope[assign]; !ok { 364 in.scope[assign] = pkg.Scope().Lookup(assign) 365 } 366 } 367 } 368 369 if l.declName != "" { 370 in.scope[l.declName] = pkg.Scope().Lookup(l.declName) 371 } 372 373 return nil 374 } 375 376 func (in *interp) maybeReadAssignment(line string, s *scanner.Scanner, initial string, base int) (bool, error) { 377 if initial == "_" { 378 initial = "" 379 } 380 assigns := []string{initial} 381 382 pos, tok, lit := s.Scan() 383 for tok == token.COMMA { 384 pos, tok, lit = s.Scan() 385 if tok != token.IDENT { 386 return false, nil 387 } 388 389 if lit == "_" { 390 lit = "" 391 } 392 assigns = append(assigns, lit) 393 394 pos, tok, lit = s.Scan() 395 } 396 397 if tok != token.DEFINE { 398 return false, nil 399 } 400 401 return true, in.readExprLine(line[int(pos)-base+2:], assigns) 402 } 403 404 func (in *interp) loadPackage(pkgpath string) (*types.Package, error) { 405 pkg, err := in.copts.Importer(in.pkgmap, pkgpath) 406 if err == nil { 407 return pkg, nil 408 } 409 410 buildpkg, err := build.Import(pkgpath, ".", 0) 411 if err != nil { 412 return nil, err 413 } 414 if len(buildpkg.CgoFiles) != 0 { 415 return nil, fmt.Errorf("%s: cannot load cgo package", pkgpath) 416 } 417 418 for _, imp := range buildpkg.Imports { 419 _, err := in.loadPackage(imp) 420 if err != nil { 421 return nil, err 422 } 423 } 424 425 fmt.Printf("# %s\n", pkgpath) 426 427 inputs := make([]string, len(buildpkg.GoFiles)) 428 for i, file := range buildpkg.GoFiles { 429 inputs[i] = filepath.Join(buildpkg.Dir, file) 430 } 431 432 fset := token.NewFileSet() 433 files, err := driver.ParseFiles(fset, inputs) 434 if err != nil { 435 return nil, err 436 } 437 438 return in.loadSourcePackage(fset, files, pkgpath, in.copts) 439 } 440 441 // readLine accumulates lines of input, including trailing newlines, 442 // executing statements as they are completed. 443 func (in *interp) readLine(line string) error { 444 if !in.pendingLine.ready() { 445 return in.readExprLine(line, nil) 446 } 447 448 var s scanner.Scanner 449 fset := token.NewFileSet() 450 file := fset.AddFile("", fset.Base(), len(line)) 451 s.Init(file, []byte(line), nil, 0) 452 453 _, tok, lit := s.Scan() 454 switch tok { 455 case token.EOF: 456 return nil 457 458 case token.IMPORT: 459 _, tok, lit = s.Scan() 460 if tok != token.STRING { 461 return errors.New("expected string literal") 462 } 463 pkgpath, err := strconv.Unquote(lit) 464 if err != nil { 465 return err 466 } 467 pkg, err := in.loadPackage(pkgpath) 468 if err != nil { 469 return err 470 } 471 in.imports = append(in.imports, pkg) 472 return nil 473 474 case token.IDENT: 475 ok, err := in.maybeReadAssignment(line, &s, lit, file.Base()) 476 if err != nil { 477 return err 478 } 479 if ok { 480 return nil 481 } 482 483 fallthrough 484 485 default: 486 return in.readExprLine(line, nil) 487 } 488 } 489 490 // formatHistory reformats the provided Go source by collapsing all lines 491 // and adding semicolons where required, suitable for adding to line history. 492 func formatHistory(input []byte) string { 493 var buf bytes.Buffer 494 var s scanner.Scanner 495 fset := token.NewFileSet() 496 file := fset.AddFile("", fset.Base(), len(input)) 497 s.Init(file, input, nil, 0) 498 pos, tok, lit := s.Scan() 499 for tok != token.EOF { 500 if int(pos)-1 > buf.Len() { 501 n := int(pos) - 1 - buf.Len() 502 buf.WriteString(strings.Repeat(" ", n)) 503 } 504 var semicolon bool 505 if tok == token.SEMICOLON { 506 semicolon = true 507 } else if lit != "" { 508 buf.WriteString(lit) 509 } else { 510 buf.WriteString(tok.String()) 511 } 512 pos, tok, lit = s.Scan() 513 if semicolon { 514 switch tok { 515 case token.RBRACE, token.RPAREN, token.EOF: 516 default: 517 buf.WriteRune(';') 518 } 519 } 520 } 521 return buf.String() 522 } 523 524 func main() { 525 llvm.LinkInMCJIT() 526 llvm.InitializeNativeTarget() 527 llvm.InitializeNativeAsmPrinter() 528 529 var in interp 530 err := in.init() 531 if err != nil { 532 panic(err) 533 } 534 defer in.dispose() 535 536 var buf bytes.Buffer 537 for { 538 if in.pendingLine.ready() && buf.Len() > 0 { 539 history := formatHistory(buf.Bytes()) 540 in.liner.AppendHistory(history) 541 buf.Reset() 542 } 543 prompt := "(llgo) " 544 if !in.pendingLine.ready() { 545 prompt = strings.Repeat(" ", len(prompt)) 546 } 547 line, err := in.liner.Prompt(prompt) 548 if err == io.EOF { 549 break 550 } else if err != nil { 551 panic(err) 552 } 553 if line == "" { 554 continue 555 } 556 buf.WriteString(line + "\n") 557 err = in.readLine(line + "\n") 558 if err != nil { 559 fmt.Println(err) 560 } 561 } 562 563 if liner.TerminalSupported() { 564 fmt.Println() 565 } 566 }