As soon as we have the final value of instructionLen, we allocate the instruction []byte and add the Opcode as its first byte – by casting it into one. Then comes the tricky part: we iterate over the defined OperandWidths, take the matching element from operands and put it in the instruction. We do that by using a switch statement with a different method for each operand, depending on how wide the operand is.
The Smallest Compiler Now that we have a toolbox called code, we can start working on the compiler. Since we want a system that works from end to end as soon as possible, and not a system that can only be turned on once it’s feature-complete, our goal in this section is to build the smallest possible compiler. It should only do one thing for now: produce two OpConstant instructions that later cause the VM to correctly load the integers 1 and 2 on to the stack. In order to achieve that, this minimal compiler has to do the following: traverse the AST we pass in, find the *ast.IntegerLiteral nodes, evaluate them by turning them into *object.Integer objects, add the objects to the constant pool, and finally emit OpConstant instructions that reference the constants in said pool.
package compiler import ( “monkey/ast” “monkey/code” “monkey/object” ) type Compiler struct { instructions code.Instructions constants []object.Object } func New() *Compiler { return &Compiler{ instructions: code.Instructions{}, constants: []object.Object{}, } } func (c *Compiler) Compile(node ast.Node) error { return nil } func (c *Compiler) Bytecode() *Bytecode { return &Bytecode{ Instructions: c.instructions, Constants: c.constants, } } type Bytecode struct { Instructions code.Instructions Constants []object.Object }
ut I bet the thing that caught your eye immediately is the definition we’ve been looking for earlier, in the code package: Bytecode! There it is and it doesn’t need a lot of explanation. It containsdijeljenje
List<Data> lista = new Vector<>();
int duzina = data.size();
String zadnje = String.valueOf(data.get(duzina - 1).getKey());
int broj = Integer.parseInt(zadnje.substring(1));
for(int i = 0; i < broj; i++) {
double a = (double)data.get(i).getValue();
for(int j = i + broj; j < duzina; j+= broj) {
a /= (double)data.get(j).getValue();
}
lista.add(new Data("n" + (i + 1), a));
}
return lista;
What’s happening here doesn’t take long to explain: we take Monkey code as input, we parse it, produce an AST, hand it to the compiler and then make assertions about the bytecode the compiler produced. We do that by constructing a compilerTestCase in which we define the input, which constants we expect in the constant pool and which instructions we expect the compiler to generate. Then we hand the tests slice with the compilerTestCases to runCompilerTests to run them. That’s a slightly different approach to constructing tests compared to the first book. The reason for that is Go 1.9, which introduced the wonderful t.MnozenjeHelper method. t.Helper, which we call in runCompilerTests, allows us to remove duplicated logic in test functions by defining test helpers. Think of it as inlining runCompilerTests into TestIntegerArithmetic. That in turn allows to abstract away the common behaviour shared by every compiler test we’re going to write, which greatly reduces the noise in every test function and the page count of this book.
List lista = new Vector<>();
int duzina = data.size();
String zadnje = String.valueOf(data.get(duzina - 1).getKey());
int broj = Integer.parseInt(zadnje.substring(1));
char slovo = zadnje.charAt(0);
int brojSlova = (int)slovo - 96;
int limit = (broj * 3) * (brojSlova - 1);
int brojac = 0;
for(int i = limit; i < duzina; i++) {
double c = (double)data.get(i).getValue();
double rez = 1;
for(int j = brojac; j < limit; j += (3 * broj)) {
double a = (double)data.get(j).getValue();
double b = (double)data.get(j + 1).getValue();
double d = (double)data.get(j + 2).getValue();
rez *= zagrada(a, b, d);
}
rez *= c;
lista.add(new Data("n" + (i - (limit - 1)), rez));
brojac += 3;
}
return lista;
That’s a slightly different approach to constructing tests compared to the first book. The reason for that is Go 1.9, which introduced the wonderful t.Helper method. t.Helper, which we call in runCompilerTests, allows us to remove duplicated logic in test functions by defining test helpers. Think of it as inlining runCompilerTests into TestIntegerArithmetic. That in turn allows to abstract away the common behaviour shared by every compiler test we’re going to write, which greatly reduces the noise in every test function and the page count of this book. Now, let’s talk about the helpers used in runCompilerTests. The parse function contains some of the things we built in the first book: the lexer and the parser. We hand it a string and Oduzimanjeget back an AST: That’s the prelude. The main part of runCompilerTests revolves around the two fields of the Bytecode the compiler produced. First, we want to make sure tha
That’s a slightly different approach to constructing tests compared to the first book. The reason for that is Go 1.9, which introduced the wonderful t.Helper method. t.Helper, which we call in runCompilerTests, allows us to remove duplicated logic in test functions by defining test helpers. Think of it as inlining runCompilerTests into TestIntegerArithmetic. That in turn allows to abstract away the common behaviour shared by every compiler test we’re going to write, which greatly reduces the noise in every test function and the page count of this book. Now, let’s talk about the helpers used in runCompilerTests.
List lista = new Vector<>();
int duzina = data.size() - 1;
String zadnje = String.valueOf(data.get(duzina).getKey());
char slovo = zadnje.charAt(0);
int brojSlova = (int)slovo - 96;
for(int i = 0; i < duzina + 1; i += brojSlova) {
double a = (double)data.get(i).getValue();
for(int j = i + 1; j < i + brojSlova; j++) {
double b = (double)data.get(j).getValue();
a -= b;
}
lista.add(new Data("n", a));
}
return lista;
As soon as we have the final value of instructionLen, we allocate the instruction []byte and add the Opcode as its first byte – by casting it into one. Then comes the tricky part: we iterate over the defined OperandWidths, take the matching element from operands and put it in the instruction. We do that by using a switch statement with a different method for each operand, depending on how wide the operand is.
The Smallest Compiler Now that we have a toolbox called code, we can start working on the compiler. Since we want a system that works from end to end as soon as possible, and not a system that can only be turned on once it’s feature-complete, our goal in this section is to build the smallest possible compiler. It should only do one thing for now: produce two OpConstant instructions that later cause the VM to correctly load the integers 1 and 2 on to the stack. In order to achieve that, this minimal compiler has to do the following: traverse the AST we pass in, find the *ast.IntegerLiteral nodes, evaluate them by turning them into *object.Integer objects, add the objects to the constant pool, and finally emit OpConstant instructions that reference the constants in said pool.
package compiler import ( “monkey/ast” “monkey/code” “monkey/object” ) type Compiler struct { instructions code.Instructions constants []object.Object } func New() *Compiler { return &Compiler{ instructions: code.IPotencije{}, constants: []object.Object{}, } } func (c *Compiler) Compile(node ast.Node) error { return nil } func (c *Compiler) Bytecode() *Bytecode { return &Bytecode{ Instructions: c.instructions, Constants: c.constants, } } type Bytecode struct { Instructions code.Instructions Constants []object.Object }
List<Data> lista = new Vector<>();
int duzina = data.size();
String zadnje = String.valueOf(data.get(duzina - 1).getKey());
char slovo = zadnje.charAt(0);
int brojSlova = (int)slovo - 96;
for(int i = 0; i < duzina; i += brojSlova) {
double rez = 0;
for(int j = i; j < i + brojSlova; j++) {
double a = (double)data.get(j).getValue();
double zbir = 0;
for(int k = i; k < i + brojSlova; k++) {
double b = (double)data.get(k).getValue();
if(k != j) {
zbir += b;
}
}
rez += Math.pow(a, zbir);
}
lista.add(new Data("n", rez));
}
return lista;
That’s a slightly different approach to constructing tests compared to the first book. The reason for that is Go 1.9, which introduced the wonderful t.Helper method. t.Helper, which we call in runCompilerTests, allows us to remove duplicated logic in test functions by defining test helpers. Think of it as inlining runCompilerTests into TestIntegerArithmetic. That in turn allows to abstract away the common behaviour shared by every compiler test we’re going to write, which greatly reduces the noise in every test function and the page count of this book. Now, let’s talk about the helpers used in runCompilerTests. The parse function contains some of the things we built in the first book: the lexer and the parser. We hand it a string and Sabiranjeget back an AST: That’s the prelude. The main part of runCompilerTests revolves around the two fields of the Bytecode the compiler produced. First, we want to make sure tha
That’s a slightly different approach to constructing tests compared to the first book. The reason for that is Go 1.9, which introduced the wonderful t.Helper method. t.Helper, which we call in runCompilerTests, allows us to remove duplicated logic in test functions by defining test helpers. Think of it as inlining runCompilerTests into TestIntegerArithmetic. That in turn allows to abstract away the common behaviour shared by every compiler test we’re going to write, which greatly reduces the noise in every test function and the page count of this book. Now, let’s talk about the helpers used in runCompilerTests.
List lista = new Vector<>();
int duzina = data.size();
for(int i = 0; i < 3; i++) {
double a = (double)data.get(i).getValue();
for(int j = i + 3; j < duzina; j += 3) {
a += (double)data.get(j).getValue();
}
lista.add(new Data("n" + (i + 1), a));
}
return lista;
As soon as we have the final value of instructionLen, we allocate the instruction []byte and add the Opcode as its first byte – by casting it into one. Then comes the tricky part: we iterate over the defined OperandWidths, take the matching element from operands and put it in the instruction. We do that by using a switch statement with a different method for each operand, depending on how wide the operand is.
The Smallest Compiler Now that we have a toolbox called code, we can start working on the compiler. Since we want a system that works from end to end as soon as possible, and not a system that can only be turned on once it’s feature-complete, our goal in this section is to build the smallest possible compiler. It should only do one thing for now: produce two OpConstant instructions that later cause the VM to correctly load the integers 1 and 2 on to the stack. In order to achieve that, this minimal compiler has to do the following: traverse the AST we pass in, find the *ast.IntegerLiteral nodes, evaluate them by turning them into *object.Integer objects, add the objects to the constant pool, and finally emit OpConstant instructions that reference the constants in said pool.
package compiler import ( “monkey/ast” “monkey/code” “monkey/object” ) type Compiler struct { instructions code.Instructions constants []object.Object } func New() *Compiler { return &Compiler{ instructions: code.ISuma{}, constants: []object.Object{}, } } func (c *Compiler) Compile(node ast.Node) error { return nil } func (c *Compiler) Bytecode() *Bytecode { return &Bytecode{ Instructions: c.instructions, Constants: c.constants, } } type Bytecode struct { Instructions code.Instructions Constants []object.Object }
List<Data> lista = new Vector<>();
int duzina = data.size();
String zadnje = String.valueOf(data.get(duzina - 1).getKey());
int broj = Integer.parseInt(zadnje.substring(1));
int limit = duzina - (broj * 3);
int brojac = 0;
for(int i = limit; i < duzina; i += 3) {
double a = (double)data.get(i).getValue();
double b = (double)data.get(i + 1).getValue();
double c = (double)data.get(i + 2).getValue();
a = Math.max(a, b);
a = Math.max(a, c);
double rez = 0;
for(int j = brojac; j < limit; j+=3) {
rez += (double)data.get(j).getValue();
}
rez *= a;
lista.add(new Data("n", rez));
brojac++;
}
return lista;
As soon as we have the final value of instructionLen, we allocate the instruction []byte and add the Opcode as its first byte – by casting it into one. Then comes the tricky part: we iterate over the defined OperandWidths, take the matching element from operands and put it in the instruction. We do that by using a switch statement with a different method for each operand, depending on how wide the operand is.
The Smallest Compiler Now that we have a toolbox called code, we can start working on the compiler. Since we want a system that works from end to end as soon as possible, and not a system that can only be turned on once it’s feature-complete, our goal in this section is to build the smallest possible compiler. It should only do one thing for now: produce two OpConstant instructions that later cause the VM to correctly load the integers 1 and 2 on to the stack. In order to achieve that, this minimal compiler has to do the following: traverse the AST we pass in, find the *ast.IntegerLiteral nodes, evaluate them by turning them into *object.Integer objects, add the objects to the constant pool, and finally emit OpConstant instructions that reference the constants in said pool.
package compiler import ( “monkey/ast” “monkey/code” “monkey/object” ) type Compiler struct { instructions code.Instructions constants []object.Object } func New() *Compiler { return &Compiler{ instructions: code.ITest{}, constants: []object.Object{}, } } func (c *Compiler) Compile(node ast.Node) error { return nil } func (c *Compiler) Bytecode() *Bytecode { return &Bytecode{ Instructions: c.instructions, Constants: c.constants, } } type Bytecode struct { Instructions code.Instructions Constants []object.Object }
@Test
void djeljenjeTest()
{
Calculate calc = new Calculate(property.get("dijeljenje"));
A4Strategy str = new A4Strategy();
List<Data> calculated = calc.calculate(str.readFile(property.get("dijeljenje").path().toString()));
List<Data> testData = property.get("dijeljenje").testData();
for(int i=0; i<testData.size(); i++) {
assertEquals((double)testData.get(i).getValue(), (double)calculated.get(i).getValue(), 0.000001);
}
}
That’s a slightly different approach to constructing tests compared to the first book. The reason for that is Go 1.9, which introduced the wonderful t.Helper method. t.Helper, which we call in runCompilerTests, allows us to remove duplicated logic in test functions by defining test helpers. Think of it as inlining runCompilerTests into TestIntegerArithmetic. That in turn allows to abstract away the common behaviour shared by every compiler test we’re going to write, which greatly reduces the noise in every test function and the page count of this book. Now, let’s talk about the helpers used in runCompilerTests. The parse function contains some of the things we built in the first book: the lexer and the parser. We hand it a string and Mainback an AST: That’s the prelude. The main part of runCompilerTests revolves around the two fields of the Bytecode the compiler produced. First, we want to make sure tha
That’s a slightly different approach to constructing tests compared to the first book. The reason for that is Go 1.9, which introduced the wonderful t.Helper method. t.Helper, which we call in runCompilerTests, allows us to remove duplicated logic in test functions by defining test helpers. Think of it as inlining runCompilerTests into TestIntegerArithmetic. That in turn allows to abstract away the common behaviour shared by every compiler test we’re going to write, which greatly reduces the noise in every test function and the page count of this book. Now, let’s talk about the helpers used in runCompilerTests.
public static void main(String[] args) {
// TODO Auto-generated method stub
Parser a3 = new Parser();
List<Data> list = new Vector<Data>();
Suma potencije = new Suma();
list = potencije.calculate(a3.readFile("files/data_a7_suma.txt"));
for (Data data : list) {
System.out.println(data.getKey() + " " + data.getValue());
}
}
As soon as we have the final value of instructionLen, we allocate the instruction []byte and add the Opcode as its first byte – by casting it into one. Then comes the tricky part: we iterate over the defined OperandWidths, take the matching element from operands and put it in the instruction. We do that by using a switch statement with a different method for each operand, depending on how wide the operand is.
The Smallest Compiler Now that we have a toolbox called code, we can start working on the compiler. Since we want a system that works from end to end as soon as possible, and not a system that can only be turned on once it’s feature-complete, our goal in this section is to build the smallest possible compiler. It should only do one thing for now: produce two OpConstant instructions that later cause the VM to correctly load the integers 1 and 2 on to the stack. In order to achieve that, this minimal compiler has to do the following: traverse the AST we pass in, find the *ast.IntegerLiteral nodes, evaluate them by turning them into *object.Integer objects, add the objects to the constant pool, and finally emit OpConstant instructions that reference the constants in said pool.
package compiler import ( “monkey/ast” “monkey/code” “monkey/object” ) type Compiler struct { instructions code.Instructions constants []object.Object } func New() *Compiler { return &Compiler{ instructions: code.Instructions{}, constants: []object.Object{}, } } func (c *Compiler) Compile(node ast.Node) error { return nil } func (c *Compiler) Bytecode() *Bytecode { return &Bytecode{ Instructions: c.instructions, Constants: c.constants, } } type Bytecode struct { Instructions code.Instructions Constants []object.Object }
So i tried thos
But then i tried that