github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/astcomp/astcomp.go (about)

     1  package astcomp
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/arnodel/golua/ast"
     7  	"github.com/arnodel/golua/ir"
     8  )
     9  
    10  // CompileLuaChunk compiles the given block statement to IR code and returns a
    11  // slice or ir.Contant values and the index to the main code constant.
    12  func CompileLuaChunk(source string, s ast.BlockStat) (kidx uint, consts []ir.Constant, err error) {
    13  	defer func() {
    14  		if r := recover(); r != nil {
    15  			compErr, ok := r.(Error)
    16  			if !ok {
    17  				panic(r)
    18  			}
    19  			err = compErr
    20  		}
    21  	}()
    22  	kp := ir.NewConstantPool()
    23  	rootIrC := ir.NewCodeBuilder("<global chunk>", kp)
    24  	rootIrC.DeclareLocal("_ENV", rootIrC.GetFreeRegister())
    25  	irC := rootIrC.NewChild("<main chunk>")
    26  	c := &compiler{CodeBuilder: irC}
    27  	c.compileFunctionBody(ast.Function{
    28  		ParList: ast.ParList{HasDots: true},
    29  		Body:    s,
    30  	})
    31  	kidx, _ = irC.Close()
    32  	return kidx, kp.Constants(), nil
    33  }
    34  
    35  type compiler struct {
    36  	*ir.CodeBuilder
    37  }
    38  
    39  func (c *compiler) NewChild(name string) *compiler {
    40  	return &compiler{
    41  		CodeBuilder: c.CodeBuilder.NewChild(name),
    42  	}
    43  }
    44  
    45  // Names of various labels and registers used during compilation.
    46  const (
    47  	breakLblName    = ir.Name("<break>")
    48  	ellipsisRegName = ir.Name("...")
    49  	callerRegName   = ir.Name("<caller>")
    50  	loopFRegName    = ir.Name("<f>")
    51  	loopSRegName    = ir.Name("<s>")
    52  	loopVarRegName  = ir.Name("<var>")
    53  )
    54  
    55  // Error that results from a valid AST which does not form a valid program.
    56  type Error struct {
    57  	Where   ast.Locator
    58  	Message string
    59  }
    60  
    61  func (e Error) Error() string {
    62  	loc := e.Where.Locate().StartPos()
    63  	return fmt.Sprintf("%d:%d: %s", loc.Line, loc.Column, e.Message)
    64  }
    65  
    66  // The compiler uses other packages that may return non nil errors only if there
    67  // is a bug in the compiler.  Such errors are wrapped in must() so that the bugs
    68  // are not silently ignored.
    69  type compilerBug struct {
    70  	err error
    71  }
    72  
    73  func (b compilerBug) Error() string {
    74  	return b.err.Error()
    75  }
    76  
    77  func must(err error) {
    78  	if err != nil {
    79  		panic(compilerBug{err: err})
    80  	}
    81  }