github.com/cowsed/Parser@v0.0.0-20211216032244-48b10019d380/compile.go (about)

     1  package parser
     2  
     3  import (
     4  	"math"
     5  )
     6  
     7  //Bytecode is an instruction type for the interpreter
     8  type Bytecode int
     9  
    10  //List of Bytecodes
    11  const (
    12  	AddBytecode = iota //Add two memory locations and save to third memory location
    13  	SubBytecode        //Subtract two memory locations and save to third memory location
    14  	MulBytecode
    15  	DivBytecode
    16  	PowBytecode //Raise first location to power of second location and save it at third
    17  	CosBytecode //Take the cosine of the first locartion and save it to the second
    18  	SinBytecode
    19  	LNBytecode
    20  )
    21  
    22  //MemoryManager keeps track of constants, variables and working memory
    23  //Also holds place for instructions
    24  type MemoryManager struct {
    25  	bc        []Bytecode
    26  	constants []float64
    27  	//Guide for which places to fill with which variables
    28  	varLocations map[string]int
    29  }
    30  
    31  //NewMemoryManager returns a new default memory manager
    32  func NewMemoryManager() MemoryManager {
    33  	return MemoryManager{
    34  		constants:    []float64{},
    35  		varLocations: map[string]int{},
    36  	}
    37  }
    38  
    39  //AddBytecode adds a bytecode to the instructions
    40  func (mm *MemoryManager) AddBytecode(bc []Bytecode) {
    41  	mm.bc = append(mm.bc, bc...)
    42  }
    43  
    44  //AddVariable adds a variable and tracks it to be set at execution time
    45  func (mm *MemoryManager) AddVariable(name string) int {
    46  	//Add Variable to memory and return the index to it
    47  
    48  	//If its already here
    49  	if i, ok := mm.varLocations[name]; ok {
    50  		return i
    51  	}
    52  	//if its new
    53  	index := mm.GetResultSpace()
    54  	mm.varLocations[name] = index
    55  	return index
    56  
    57  }
    58  
    59  //AddConstant adds a constant into the memory
    60  func (mm *MemoryManager) AddConstant(v float64) int {
    61  	//Add Constant to memory and return the index to it
    62  	i := len(mm.constants)
    63  	mm.constants = append(mm.constants, v)
    64  	return i
    65  }
    66  
    67  //GetResultSpace returns a location for the next variable or constant to be put in
    68  func (mm *MemoryManager) GetResultSpace() int {
    69  	//Add place in memory to store a result and return the index to it
    70  	i := len(mm.constants)
    71  	mm.constants = append(mm.constants, -8008)
    72  	return i
    73  }
    74  
    75  //CompileExpression takes an expression and turns it into bytecode
    76  func CompileExpression(e Expression) func(vs map[string]float64) float64 {
    77  	mm := NewMemoryManager()
    78  	lastResIndex := e.Compile(&mm)
    79  	vars := make([]string, len(mm.varLocations))
    80  	i := 0
    81  	for k := range mm.varLocations {
    82  		vars[i] = k
    83  		i++
    84  	}
    85  
    86  	compiledFunc := func(vs map[string]float64) float64 {
    87  		//Copy over code and constants
    88  		code := mm.bc
    89  		consts := mm.constants
    90  		//save vs to mem bank
    91  		for _, k := range vars {
    92  			index := mm.varLocations[k]
    93  			val := vs[k]
    94  			consts[index] = val
    95  
    96  		}
    97  		//Execute code
    98  		for i := 0; i < len(code); {
    99  			ins := code[i]
   100  			switch ins {
   101  			case AddBytecode:
   102  				Ai := code[i+1]
   103  				Bi := code[i+2]
   104  
   105  				Ri := code[i+3]
   106  				consts[Ri] = consts[Ai] + consts[Bi]
   107  				i += 4
   108  			case SubBytecode:
   109  				Ai := code[i+1]
   110  				Bi := code[i+2]
   111  
   112  				Ri := code[i+3]
   113  				consts[Ri] = consts[Ai] - consts[Bi]
   114  				i += 4
   115  			case MulBytecode:
   116  				Ai := code[i+1]
   117  				Bi := code[i+2]
   118  
   119  				Ri := code[i+3]
   120  				consts[Ri] = consts[Ai] * consts[Bi]
   121  				i += 4
   122  
   123  			case DivBytecode:
   124  				Ai := code[i+1]
   125  				Bi := code[i+2]
   126  
   127  				Ri := code[i+3]
   128  				consts[Ri] = consts[Ai] / consts[Bi]
   129  				i += 4
   130  			case PowBytecode:
   131  				Ai := code[i+1]
   132  				Bi := code[i+2]
   133  
   134  				Ri := code[i+3]
   135  				consts[Ri] = math.Pow(consts[Ai], consts[Bi])
   136  				i += 4
   137  			case CosBytecode:
   138  				Ai := code[i+1]
   139  
   140  				Ri := code[i+2]
   141  				consts[Ri] = math.Cos(consts[Ai])
   142  				i += 3
   143  
   144  			case SinBytecode:
   145  				Ai := code[i+1]
   146  				Ri := code[i+2]
   147  				consts[Ri] = math.Sin(consts[Ai])
   148  				i += 3
   149  			case LNBytecode:
   150  				Ai := code[i+1]
   151  				Ri := code[i+2]
   152  				consts[Ri] = math.Log(consts[Ai])
   153  				i += 3
   154  
   155  			default:
   156  				//This should really never happen but just go to next instruction
   157  				i++
   158  			}
   159  
   160  		}
   161  		return consts[lastResIndex]
   162  	}
   163  	return compiledFunc
   164  }