From d462c961ab057c0f222c9e503cae4da2a61fda19 Mon Sep 17 00:00:00 2001 From: Maxwell-Alexius Date: Fri, 20 Oct 2017 15:06:00 +0800 Subject: [PATCH 1/7] Complete float literal and token part --- compiler/ast/expressions.go | 13 +++ compiler/parser/data_type_parsing.go | 16 +++ compiler/parser/expression_parsing.go | 12 +++ compiler/parser/parser.go | 2 +- compiler/token/token.go | 1 + vm/float_test.go | 144 +++++++++++++------------- 6 files changed, 115 insertions(+), 73 deletions(-) diff --git a/compiler/ast/expressions.go b/compiler/ast/expressions.go index 03a9a8807..38d394f5f 100644 --- a/compiler/ast/expressions.go +++ b/compiler/ast/expressions.go @@ -19,6 +19,19 @@ func (il *IntegerLiteral) String() string { return il.Token.Literal } +type FloatLiteral struct { + *BaseNode + Value float64 +} + +func (il *FloatLiteral) expressionNode() {} +func (il *FloatLiteral) TokenLiteral() string { + return il.Token.Literal +} +func (il *FloatLiteral) String() string { + return il.Token.Literal +} + type StringLiteral struct { *BaseNode Value string diff --git a/compiler/parser/data_type_parsing.go b/compiler/parser/data_type_parsing.go index 33f4bd096..3a876523e 100644 --- a/compiler/parser/data_type_parsing.go +++ b/compiler/parser/data_type_parsing.go @@ -20,6 +20,22 @@ func (p *Parser) parseIntegerLiteral() ast.Expression { return lit } +func (p *Parser) parseFloatLiteral(receiver ast.Expression) ast.Expression { + p.nextToken() + floatLiteral := receiver.String() + "." + p.curToken.Literal + + floatTok := token.Token{Type: token.Float, Literal: floatLiteral, Line: p.curToken.Line} + lit := &ast.FloatLiteral{BaseNode: &ast.BaseNode{Token: floatTok}} + value, err := strconv.ParseFloat(lit.TokenLiteral(), 64) + if err != nil { + p.error = newTypeParsingError(lit.TokenLiteral(), "float", p.curToken.Line) + return nil + } + lit.Value = float64(value) + + return lit +} + func (p *Parser) parseStringLiteral() ast.Expression { lit := &ast.StringLiteral{BaseNode: &ast.BaseNode{Token: p.curToken}} lit.Value = p.curToken.Literal diff --git a/compiler/parser/expression_parsing.go b/compiler/parser/expression_parsing.go index d2eb80416..90a1d01b5 100644 --- a/compiler/parser/expression_parsing.go +++ b/compiler/parser/expression_parsing.go @@ -388,6 +388,18 @@ func (p *Parser) parseMultiVariables(left ast.Expression) ast.Expression { return result } +func (p *Parser) parseDotExpression(receiver ast.Expression) ast.Expression { + _, ok := receiver.(*ast.IntegerLiteral) + + if ok && p.peekTokenIs(token.Int) { + // When both receiver & caller are integer => Float + return p.parseFloatLiteral(receiver) + } else { + // Normal call method expression with receiver + return p.parseCallExpressionWithReceiver(receiver) + } +} + func (p *Parser) expandAssignmentValue(value ast.Expression) ast.Expression { switch p.curToken.Type { case token.Assign: diff --git a/compiler/parser/parser.go b/compiler/parser/parser.go index 88e11d727..baf3c7c48 100644 --- a/compiler/parser/parser.go +++ b/compiler/parser/parser.go @@ -171,7 +171,7 @@ func New(l *lexer.Lexer) *Parser { p.registerInfix(token.ResolutionOperator, p.parseInfixExpression) p.registerInfix(token.Assign, p.parseAssignExpression) p.registerInfix(token.Range, p.parseRangeExpression) - p.registerInfix(token.Dot, p.parseCallExpressionWithReceiver) + p.registerInfix(token.Dot, p.parseDotExpression) p.registerInfix(token.LParen, p.parseCallExpressionWithoutReceiver) p.registerInfix(token.LBracket, p.parseIndexExpression) p.registerInfix(token.Colon, p.parsePairExpression) diff --git a/compiler/token/token.go b/compiler/token/token.go index 19281bcf9..bbcce34f7 100644 --- a/compiler/token/token.go +++ b/compiler/token/token.go @@ -19,6 +19,7 @@ const ( Ident = "IDENT" InstanceVariable = "INSTANCE_VAR" Int = "INT" + Float = "FLOAT" String = "STRING" Comment = "COMMENT" diff --git a/vm/float_test.go b/vm/float_test.go index 73ec7f1ca..c9bd0ea80 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -27,12 +27,12 @@ func TestFloatArithmeticOperationWithFloat(t *testing.T) { input string expected interface{} }{ - {`'13.5'.to_f + '3.2'.to_f`, 16.7}, - {`'13.5'.to_f - '3.2'.to_f`, 10.3}, - {`'13.5'.to_f * '3.2'.to_f`, 43.2}, - {`'13.5'.to_f % '3.75'.to_f`, 2.25}, - {`'13.5'.to_f / '3.75'.to_f`, 3.6}, - {`'16.0'.to_f ** '3.5'.to_f`, 16384.0}, + {`13.5 + 3.2`, 16.7}, + {`13.5 - 3.2`, 10.3}, + {`13.5 * 3.2`, 43.2}, + {`13.5 % 3.75`, 2.25}, + {`13.5 / 3.75`, 3.6}, + {`16.0 ** 3.5`, 16384.0}, } for i, tt := range tests { @@ -49,12 +49,12 @@ func TestFloatArithmeticOperationWithInteger(t *testing.T) { input string expected interface{} }{ - {`'13.5'.to_f + 3`, 16.5}, - {`'13.5'.to_f - 3`, 10.5}, - {`'13.5'.to_f * 3`, 40.5}, - {`'13.5'.to_f % 3`, 1.5}, - {`'13.5'.to_f / 3`, 4.5}, - {`'13.5'.to_f ** 3`, 2460.375}, + {`13.5.to_f + 3`, 16.5}, + {`13.5.to_f - 3`, 10.5}, + {`13.5.to_f * 3`, 40.5}, + {`13.5.to_f % 3`, 1.5}, + {`13.5.to_f / 3`, 4.5}, + {`13.5.to_f ** 3`, 2460.375}, } for i, tt := range tests { @@ -68,10 +68,10 @@ func TestFloatArithmeticOperationWithInteger(t *testing.T) { func TestFloatArithmeticOperationFail(t *testing.T) { testsFail := []errorTestCase{ - {`'1'.to_f + "p"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, - {`'1'.to_f - "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, - {`'1'.to_f ** "p"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, - {`'1'.to_f / "t"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 + "p"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 - "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 ** "p"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 / "t"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, } for i, tt := range testsFail { @@ -88,21 +88,21 @@ func TestFloatComparisonWithFloat(t *testing.T) { input string expected interface{} }{ - {`'1.5'.to_f > '2.5'.to_f`, false}, - {`'2.5'.to_f > '1.5'.to_f`, true}, - {`'3.5'.to_f > '3.5'.to_f`, false}, - {`'1.5'.to_f < '2.5'.to_f`, true}, - {`'2.5'.to_f < '1.5'.to_f`, false}, - {`'3.5'.to_f < '3.5'.to_f`, false}, - {`'1.5'.to_f >= '2.5'.to_f`, false}, - {`'2.5'.to_f >= '1.5'.to_f`, true}, - {`'3.5'.to_f >= '3.5'.to_f`, true}, - {`'1.5'.to_f <= '2.5'.to_f`, true}, - {`'2.5'.to_f <= '1.5'.to_f`, false}, - {`'3.5'.to_f <= '3.5'.to_f`, true}, - {`'1.5'.to_f <=> '2.5'.to_f`, -1}, - {`'2.5'.to_f <=> '1.5'.to_f`, 1}, - {`'3.5'.to_f <=> '3.5'.to_f`, 0}, + {`1.5 > 2.5`, false}, + {`2.5 > 1.5`, true}, + {`3.5 > 3.5`, false}, + {`1.5 < 2.5`, true}, + {`2.5 < 1.5`, false}, + {`3.5 < 3.5`, false}, + {`1.5 >= 2.5`, false}, + {`2.5 >= 1.5`, true}, + {`3.5 >= 3.5`, true}, + {`1.5 <= 2.5`, true}, + {`2.5 <= 1.5`, false}, + {`3.5 <= 3.5`, true}, + {`1.5 <=> 2.5`, -1}, + {`2.5 <=> 1.5`, 1}, + {`3.5 <=> 3.5`, 0}, } for i, tt := range tests { @@ -119,21 +119,21 @@ func TestFloatComparisonWithInteger(t *testing.T) { input string expected interface{} }{ - {`'1'.to_f > 2`, false}, - {`'2'.to_f > 1`, true}, - {`'3'.to_f > 3`, false}, - {`'1'.to_f < 2`, true}, - {`'2'.to_f < 1`, false}, - {`'3'.to_f < 3`, false}, - {`'1'.to_f >= 2`, false}, - {`'2'.to_f >= 1`, true}, - {`'3'.to_f >= 3`, true}, - {`'1'.to_f <= 2`, true}, - {`'2'.to_f <= 1`, false}, - {`'3'.to_f <= 3`, true}, - {`'1'.to_f <=> 2`, -1}, - {`'2'.to_f <=> 1`, 1}, - {`'3'.to_f <=> 3`, 0}, + {`1 > 2`, false}, + {`2 > 1`, true}, + {`3 > 3`, false}, + {`1 < 2`, true}, + {`2 < 1`, false}, + {`3 < 3`, false}, + {`1 >= 2`, false}, + {`2 >= 1`, true}, + {`3 >= 3`, true}, + {`1 <= 2`, true}, + {`2 <= 1`, false}, + {`3 <= 3`, true}, + {`1 <=> 2`, -1}, + {`2 <=> 1`, 1}, + {`3 <=> 3`, 0}, } for i, tt := range tests { @@ -147,11 +147,11 @@ func TestFloatComparisonWithInteger(t *testing.T) { func TestFloatComparisonFail(t *testing.T) { testsFail := []errorTestCase{ - {`'1'.to_f > "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, - {`'1'.to_f >= "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, - {`'1'.to_f < "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, - {`'1'.to_f <= "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, - {`'1'.to_f <=> "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 > "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 >= "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 < "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 <= "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, + {`1 <=> "m"`, "TypeError: Expect argument to be Numeric. got: String", 1, 1}, } for i, tt := range testsFail { @@ -168,22 +168,22 @@ func TestFloatEquality(t *testing.T) { input string expected interface{} }{ - {`'123.5'.to_f == '123.5'.to_f`, true}, - {`'123'.to_f == 123`, true}, - {`'123.5'.to_f == '124'.to_f`, false}, - {`'123.5'.to_f == "123.5"`, false}, - {`'123.5'.to_f == (1..3)`, false}, - {`'123.5'.to_f == { a: 1 }`, false}, - {`'123.5'.to_f == [1]`, false}, - {`'123.5'.to_f == Float`, false}, - {`'123.5'.to_f != '123.5'.to_f`, false}, - {`'123.5'.to_f != 123`, true}, - {`'123.5'.to_f != '124'.to_f`, true}, - {`'123.5'.to_f != "123.5"`, true}, - {`'123.5'.to_f != (1..3)`, true}, - {`'123.5'.to_f != { a: 1 }`, true}, - {`'123.5'.to_f != [1]`, true}, - {`'123.5'.to_f != Float`, true}, + {`123.5 == 123.5`, true}, + {`123 == 123`, true}, + {`123.5 == 124`, false}, + {`123.5 == "123.5"`, false}, + {`123.5 == (1..3)`, false}, + {`123.5 == { a: 1 }`, false}, + {`123.5 == [1]`, false}, + {`123.5 == Float`, false}, + {`123.5 != 123.5`, false}, + {`123.5 != 123`, true}, + {`123.5 != 124`, true}, + {`123.5 != "123.5"`, true}, + {`123.5 != (1..3)`, true}, + {`123.5 != { a: 1 }`, true}, + {`123.5 != [1]`, true}, + {`123.5 != Float`, true}, } for i, tt := range tests { @@ -200,14 +200,14 @@ func TestFloatConversions(t *testing.T) { input string expected interface{} }{ - {`'100.3'.to_f.to_i`, 100}, - {`'100.3'.to_f.to_s`, "100.3"}, - {`'100.3'.to_f.to_d.to_s`, "100.3"}, + {`100.3.to_i`, 100}, + {`100.3.to_s`, "100.3"}, + {`100.3.to_d.to_s`, "100.3"}, {` - '3.14159265358979'.to_f.to_d.to_s`, + 3.14159265358979.to_d.to_s`, "3.14159265358979"}, {` - '-273.150000000'.to_f.to_d.to_s`, + -273.150000000.to_d.to_s`, "-273.15"}, } From 6cba31513a8da684c8ec6b534a0dbfaf7016e109 Mon Sep 17 00:00:00 2001 From: Maxwell-Alexius Date: Tue, 24 Oct 2017 17:00:10 +0800 Subject: [PATCH 2/7] Support PutFloat instruction --- compiler/bytecode/expression_generation.go | 2 ++ compiler/bytecode/instruction.go | 1 + compiler/parser/data_type_parsing.go | 1 - compiler/parser/expression_parsing.go | 8 ++++---- vm/float_test.go | 12 ++++++------ vm/instruction.go | 17 ++++++++++++++++- 6 files changed, 29 insertions(+), 12 deletions(-) diff --git a/compiler/bytecode/expression_generation.go b/compiler/bytecode/expression_generation.go index eec63f3f1..bfee13de0 100644 --- a/compiler/bytecode/expression_generation.go +++ b/compiler/bytecode/expression_generation.go @@ -14,6 +14,8 @@ func (g *Generator) compileExpression(is *InstructionSet, exp ast.Expression, sc is.define(GetInstanceVariable, sourceLine, exp.Value) case *ast.IntegerLiteral: is.define(PutObject, sourceLine, fmt.Sprint(exp.Value)) + case *ast.FloatLiteral: + is.define(PutFloat, sourceLine, fmt.Sprint(exp.Value)) case *ast.StringLiteral: is.define(PutString, sourceLine, exp.Value) case *ast.BooleanExpression: diff --git a/compiler/bytecode/instruction.go b/compiler/bytecode/instruction.go index cf6373e39..763bf5619 100644 --- a/compiler/bytecode/instruction.go +++ b/compiler/bytecode/instruction.go @@ -24,6 +24,7 @@ const ( SetInstanceVariable = "setinstancevariable" PutBoolean = "putboolean" PutString = "putstring" + PutFloat = "putfloat" PutSelf = "putself" PutObject = "putobject" PutNull = "putnil" diff --git a/compiler/parser/data_type_parsing.go b/compiler/parser/data_type_parsing.go index 3a876523e..d844e390f 100644 --- a/compiler/parser/data_type_parsing.go +++ b/compiler/parser/data_type_parsing.go @@ -32,7 +32,6 @@ func (p *Parser) parseFloatLiteral(receiver ast.Expression) ast.Expression { return nil } lit.Value = float64(value) - return lit } diff --git a/compiler/parser/expression_parsing.go b/compiler/parser/expression_parsing.go index 90a1d01b5..e8088b26d 100644 --- a/compiler/parser/expression_parsing.go +++ b/compiler/parser/expression_parsing.go @@ -391,13 +391,13 @@ func (p *Parser) parseMultiVariables(left ast.Expression) ast.Expression { func (p *Parser) parseDotExpression(receiver ast.Expression) ast.Expression { _, ok := receiver.(*ast.IntegerLiteral) + // When both receiver & caller are integer => Float if ok && p.peekTokenIs(token.Int) { - // When both receiver & caller are integer => Float return p.parseFloatLiteral(receiver) - } else { - // Normal call method expression with receiver - return p.parseCallExpressionWithReceiver(receiver) } + + // Normal call method expression with receiver + return p.parseCallExpressionWithReceiver(receiver) } func (p *Parser) expandAssignmentValue(value ast.Expression) ast.Expression { diff --git a/vm/float_test.go b/vm/float_test.go index c9bd0ea80..db3401f6d 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -49,12 +49,12 @@ func TestFloatArithmeticOperationWithInteger(t *testing.T) { input string expected interface{} }{ - {`13.5.to_f + 3`, 16.5}, - {`13.5.to_f - 3`, 10.5}, - {`13.5.to_f * 3`, 40.5}, - {`13.5.to_f % 3`, 1.5}, - {`13.5.to_f / 3`, 4.5}, - {`13.5.to_f ** 3`, 2460.375}, + {`13.5 + 3`, 16.5}, + {`13.5 - 3`, 10.5}, + {`13.5 * 3`, 40.5}, + {`13.5 % 3`, 1.5}, + {`13.5 / 3`, 4.5}, + {`13.5 ** 3`, 2460.375}, } for i, tt := range tests { diff --git a/vm/instruction.go b/vm/instruction.go index 16e42e987..182e9d947 100644 --- a/vm/instruction.go +++ b/vm/instruction.go @@ -6,6 +6,7 @@ import ( "github.com/goby-lang/goby/vm/classes" "github.com/goby-lang/goby/vm/errors" "strings" + "strconv" ) type operation func(t *thread, sourceLine int, cf *normalCallFrame, args ...interface{}) @@ -338,6 +339,20 @@ var builtinActions = map[operationType]*action{ t.stack.push(&Pointer{Target: object}) }, }, + bytecode.PutFloat: { + name: bytecode.PutFloat, + operation: func(t *thread, i *instruction, cf *normalCallFrame, args ...interface{}) { + var value float64; + switch argValue := args[0].(type) { + case string: + value, _ = strconv.ParseFloat(argValue,64) + case int: + value = float64(argValue) + } + object := t.vm.initFloatObject(value) + t.stack.push(&Pointer{Target: object}) + }, + }, bytecode.PutNull: { name: bytecode.PutNull, operation: func(t *thread, sourceLine int, cf *normalCallFrame, args ...interface{}) { @@ -555,7 +570,7 @@ func (vm *VM) initObjectFromGoType(value interface{}) Object { case int32: return vm.initIntegerObject(int(v)) case float64: - return vm.initIntegerObject(int(v)) + return vm.initFloatObject(v) case []uint8: bytes := []byte{} From e1cbef0d487dce40b1ef8430e1e3bb2bb7e304b5 Mon Sep 17 00:00:00 2001 From: Maxwell-Alexius Date: Tue, 24 Oct 2017 17:30:49 +0800 Subject: [PATCH 3/7] Rescue test case for JSON parse in float format --- vm/instruction.go | 6 +++--- vm/json.go | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/vm/instruction.go b/vm/instruction.go index 182e9d947..45602d71e 100644 --- a/vm/instruction.go +++ b/vm/instruction.go @@ -5,8 +5,8 @@ import ( "github.com/goby-lang/goby/compiler/bytecode" "github.com/goby-lang/goby/vm/classes" "github.com/goby-lang/goby/vm/errors" - "strings" "strconv" + "strings" ) type operation func(t *thread, sourceLine int, cf *normalCallFrame, args ...interface{}) @@ -342,10 +342,10 @@ var builtinActions = map[operationType]*action{ bytecode.PutFloat: { name: bytecode.PutFloat, operation: func(t *thread, i *instruction, cf *normalCallFrame, args ...interface{}) { - var value float64; + var value float64 switch argValue := args[0].(type) { case string: - value, _ = strconv.ParseFloat(argValue,64) + value, _ = strconv.ParseFloat(argValue, 64) case int: value = float64(argValue) } diff --git a/vm/json.go b/vm/json.go index f1d879d13..43f2c669f 100644 --- a/vm/json.go +++ b/vm/json.go @@ -140,6 +140,14 @@ func (v *VM) convertJSONToHashObj(j jsonObj) Object { // Single json object case map[string]interface{}: objectMap[key] = v.convertJSONToHashObj(jsonValue) + case float64: + // TODO: Find a better way to distinguish between Float & Integer because default GO JSON package + // TODO: support only for parsing float out regardless of integer or float type data of JSON value + if jsonValue == float64(int(jsonValue)) { + objectMap[key] = v.initIntegerObject(int(jsonValue)) + } else { + objectMap[key] = v.initFloatObject(jsonValue) + } default: objectMap[key] = v.initObjectFromGoType(jsonValue) } From a05cf1480076f1f6a651d1044baf2cbc4972048a Mon Sep 17 00:00:00 2001 From: Maxwell-Alexius Date: Fri, 27 Oct 2017 14:55:10 +0800 Subject: [PATCH 4/7] Rescue some case for float type data --- vm/float.go | 4 ++-- vm/float_test.go | 17 +++++++++++++++++ vm/instruction.go | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/vm/float.go b/vm/float.go index 15eeba353..e9b54d5da 100644 --- a/vm/float.go +++ b/vm/float.go @@ -2,10 +2,10 @@ package vm import ( "math" - "strconv" "github.com/goby-lang/goby/vm/classes" "github.com/goby-lang/goby/vm/errors" + "strconv" ) // FloatObject represents an inexact real number using the native architecture's double-precision floating point @@ -438,7 +438,7 @@ func (f *FloatObject) numericComparison(t *thread, rightObject Object, operation // toString returns the object's value as the string format, in non // exponential format (straight number, without exponent `E`). func (f *FloatObject) toString() string { - return strconv.FormatFloat(f.value, 'f', -1, 64) + return strconv.FormatFloat(f.value, 'f', -1, 64) // fmt.Sprintf("%f", f.value) } // toJSON just delegates to toString diff --git a/vm/float_test.go b/vm/float_test.go index db3401f6d..e9a251ee6 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -219,3 +219,20 @@ func TestFloatConversions(t *testing.T) { v.checkSP(t, i, 1) } } + +func TestFloatEdgeCases(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {`(0.1 + 0.2).to_s`, "0.30000000000000004"}, + } + + for i, tt := range tests { + v := initTestVM() + evaluated := v.testEval(t, tt.input, getFilename()) + checkExpected(t, i, evaluated, tt.expected) + v.checkCFP(t, i, 0) + v.checkSP(t, i, 1) + } +} diff --git a/vm/instruction.go b/vm/instruction.go index 45602d71e..5652a8b56 100644 --- a/vm/instruction.go +++ b/vm/instruction.go @@ -341,7 +341,7 @@ var builtinActions = map[operationType]*action{ }, bytecode.PutFloat: { name: bytecode.PutFloat, - operation: func(t *thread, i *instruction, cf *normalCallFrame, args ...interface{}) { + operation: func(t *thread, sourceLine int, cf *normalCallFrame, args ...interface{}) { var value float64 switch argValue := args[0].(type) { case string: From 10f8a0a69f1462a093dcee487dfa482ac3d7d917 Mon Sep 17 00:00:00 2001 From: Maxwell-Alexius Date: Fri, 27 Oct 2017 16:42:13 +0800 Subject: [PATCH 5/7] Fix errors and refactor the float expression parsing part --- compiler/ast/expressions.go | 35 ++++++++++++++++++++++++++++ compiler/parser/data_type_parsing.go | 11 ++++++--- vm/float_test.go | 2 ++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/compiler/ast/expressions.go b/compiler/ast/expressions.go index 38d394f5f..cd4ca9a85 100644 --- a/compiler/ast/expressions.go +++ b/compiler/ast/expressions.go @@ -6,28 +6,38 @@ import ( "strings" ) +// Define the integer literal which contains the node expression and its value type IntegerLiteral struct { *BaseNode Value int } func (il *IntegerLiteral) expressionNode() {} + +// Get the literal of the Integer type token func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal } + +// Get the string format of the Integer type token func (il *IntegerLiteral) String() string { return il.Token.Literal } +// Define the float literal which contains the node expression and its value type FloatLiteral struct { *BaseNode Value float64 } func (il *FloatLiteral) expressionNode() {} + +// Get the literal of the Float type token func (il *FloatLiteral) TokenLiteral() string { return il.Token.Literal } + +// Get the string format of the Float type token func (il *FloatLiteral) String() string { return il.Token.Literal } @@ -37,10 +47,15 @@ type StringLiteral struct { Value string } +// Define the string literal which contains the node expression and its value func (sl *StringLiteral) expressionNode() {} + +// Get the literal of the String type token func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal } + +// Get the string format of the String type token func (sl *StringLiteral) String() string { var out bytes.Buffer @@ -50,15 +65,20 @@ func (sl *StringLiteral) String() string { return out.String() } +// Define the array literal which contains the node expression and its value type ArrayExpression struct { *BaseNode Elements []Expression } func (ae *ArrayExpression) expressionNode() {} + +// Get the literal of the Array type token func (ae *ArrayExpression) TokenLiteral() string { return ae.Token.Literal } + +// Get the string format of the Array type token func (ae *ArrayExpression) String() string { var out bytes.Buffer @@ -103,15 +123,20 @@ func (pe *PairExpression) String() string { return fmt.Sprintf("%s: %s", pe.Key.String(), pe.Value.String()) } +// Define the hash expression literal which contains the node expression and its value type HashExpression struct { *BaseNode Data map[string]Expression } func (he *HashExpression) expressionNode() {} + +// Get the literal of the Hash type token func (he *HashExpression) TokenLiteral() string { return he.Token.Literal } + +// Get the string format of the Hash type token func (he *HashExpression) String() string { var out bytes.Buffer var pairs []string @@ -203,15 +228,20 @@ func (ae *AssignExpression) String() string { return out.String() } +// Define the boolean expression literal which contains the node expression and its value type BooleanExpression struct { *BaseNode Value bool } func (b *BooleanExpression) expressionNode() {} + +// Get the literal of the Boolean type token func (b *BooleanExpression) TokenLiteral() string { return b.Token.Literal } + +// Get the string format of the Boolean type token func (b *BooleanExpression) String() string { return b.Token.Literal } @@ -380,6 +410,7 @@ func (ye *YieldExpression) String() string { return out.String() } +// Define the range expression literal which contains the node expression and its start/end value type RangeExpression struct { *BaseNode Start Expression @@ -387,9 +418,13 @@ type RangeExpression struct { } func (re *RangeExpression) expressionNode() {} + +// Get the literal of the Range type token func (re *RangeExpression) TokenLiteral() string { return re.Token.Literal } + +// Get the string format of the Range type token func (re *RangeExpression) String() string { var out bytes.Buffer diff --git a/compiler/parser/data_type_parsing.go b/compiler/parser/data_type_parsing.go index d844e390f..35844e4cf 100644 --- a/compiler/parser/data_type_parsing.go +++ b/compiler/parser/data_type_parsing.go @@ -1,6 +1,7 @@ package parser import ( + "fmt" "github.com/goby-lang/goby/compiler/ast" "github.com/goby-lang/goby/compiler/token" "strconv" @@ -20,11 +21,15 @@ func (p *Parser) parseIntegerLiteral() ast.Expression { return lit } -func (p *Parser) parseFloatLiteral(receiver ast.Expression) ast.Expression { +func (p *Parser) parseFloatLiteral(integerPart ast.Expression) ast.Expression { + // Get the fractional part of the token p.nextToken() - floatLiteral := receiver.String() + "." + p.curToken.Literal - floatTok := token.Token{Type: token.Float, Literal: floatLiteral, Line: p.curToken.Line} + floatTok := token.Token{ + Type: token.Float, + Literal: fmt.Sprintf("%s.%s", integerPart.String(), p.curToken.Literal), + Line: p.curToken.Line, + } lit := &ast.FloatLiteral{BaseNode: &ast.BaseNode{Token: floatTok}} value, err := strconv.ParseFloat(lit.TokenLiteral(), 64) if err != nil { diff --git a/vm/float_test.go b/vm/float_test.go index e9a251ee6..5f12e3919 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -203,6 +203,8 @@ func TestFloatConversions(t *testing.T) { {`100.3.to_i`, 100}, {`100.3.to_s`, "100.3"}, {`100.3.to_d.to_s`, "100.3"}, + {`(100.3).to_i`, 100}, + {`(100.3).to_s`, "100.3"}, {` 3.14159265358979.to_d.to_s`, "3.14159265358979"}, From d151ecd567db942a01447cb14a061ea733ecfc3d Mon Sep 17 00:00:00 2001 From: Maxwell-Alexius Date: Tue, 31 Oct 2017 15:09:57 +0800 Subject: [PATCH 6/7] Fix the comment in expression.go --- compiler/ast/expressions.go | 41 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/compiler/ast/expressions.go b/compiler/ast/expressions.go index cd4ca9a85..76e98e855 100644 --- a/compiler/ast/expressions.go +++ b/compiler/ast/expressions.go @@ -6,7 +6,7 @@ import ( "strings" ) -// Define the integer literal which contains the node expression and its value +// IntegerLiteral contains the node expression and its value type IntegerLiteral struct { *BaseNode Value int @@ -14,17 +14,17 @@ type IntegerLiteral struct { func (il *IntegerLiteral) expressionNode() {} -// Get the literal of the Integer type token +// IntegerLiteral.TokenLiteral gets the Integer type token func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal } -// Get the string format of the Integer type token +// IntegerLiteral.String gets the string format of the Integer type token func (il *IntegerLiteral) String() string { return il.Token.Literal } -// Define the float literal which contains the node expression and its value +// FloatLiteral contains the node expression and its value type FloatLiteral struct { *BaseNode Value float64 @@ -32,16 +32,17 @@ type FloatLiteral struct { func (il *FloatLiteral) expressionNode() {} -// Get the literal of the Float type token +// FloatLiteral.TokenLiteral gets the literal of the Float type token func (il *FloatLiteral) TokenLiteral() string { return il.Token.Literal } -// Get the string format of the Float type token +// FloatLiteral.String gets the string format of the Float type token func (il *FloatLiteral) String() string { return il.Token.Literal } +// StringLiteral contains the node expression and its value type StringLiteral struct { *BaseNode Value string @@ -50,12 +51,12 @@ type StringLiteral struct { // Define the string literal which contains the node expression and its value func (sl *StringLiteral) expressionNode() {} -// Get the literal of the String type token +// StringLiteral.TokenLiteral gets the literal of the String type token func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal } -// Get the string format of the String type token +// StringLiteral.String gets the string format of the String type token func (sl *StringLiteral) String() string { var out bytes.Buffer @@ -65,7 +66,7 @@ func (sl *StringLiteral) String() string { return out.String() } -// Define the array literal which contains the node expression and its value +// ArrayExpression defines the array expression literal which contains the node expression and its value type ArrayExpression struct { *BaseNode Elements []Expression @@ -73,12 +74,12 @@ type ArrayExpression struct { func (ae *ArrayExpression) expressionNode() {} -// Get the literal of the Array type token +// ArrayExpression gets the literal of the Array type token func (ae *ArrayExpression) TokenLiteral() string { return ae.Token.Literal } -// Get the string format of the Array type token +// ArrayExpression.String gets the string format of the Array type token func (ae *ArrayExpression) String() string { var out bytes.Buffer @@ -123,7 +124,7 @@ func (pe *PairExpression) String() string { return fmt.Sprintf("%s: %s", pe.Key.String(), pe.Value.String()) } -// Define the hash expression literal which contains the node expression and its value +// HashExpression defines the hash expression literal which contains the node expression and its value type HashExpression struct { *BaseNode Data map[string]Expression @@ -131,12 +132,12 @@ type HashExpression struct { func (he *HashExpression) expressionNode() {} -// Get the literal of the Hash type token +// HashExpression.TokenLiteral gets the literal of the Hash type token func (he *HashExpression) TokenLiteral() string { return he.Token.Literal } -// Get the string format of the Hash type token +// HashExpression.String gets the string format of the Hash type token func (he *HashExpression) String() string { var out bytes.Buffer var pairs []string @@ -228,7 +229,7 @@ func (ae *AssignExpression) String() string { return out.String() } -// Define the boolean expression literal which contains the node expression and its value +// BooleanExpression defines the boolean expression literal which contains the node expression and its value type BooleanExpression struct { *BaseNode Value bool @@ -236,12 +237,12 @@ type BooleanExpression struct { func (b *BooleanExpression) expressionNode() {} -// Get the literal of the Boolean type token +// BooleanExpression.TokenLiteral gets the literal of the Boolean type token func (b *BooleanExpression) TokenLiteral() string { return b.Token.Literal } -// Get the string format of the Boolean type token +// BooleanExpression.String gets the string format of the Boolean type token func (b *BooleanExpression) String() string { return b.Token.Literal } @@ -410,7 +411,7 @@ func (ye *YieldExpression) String() string { return out.String() } -// Define the range expression literal which contains the node expression and its start/end value +// RangeExpression defines the range expression literal which contains the node expression and its start/end value type RangeExpression struct { *BaseNode Start Expression @@ -419,12 +420,12 @@ type RangeExpression struct { func (re *RangeExpression) expressionNode() {} -// Get the literal of the Range type token +// RangeExpression.TokenLiteral gets the literal of the Range type token func (re *RangeExpression) TokenLiteral() string { return re.Token.Literal } -// Get the string format of the Range type token +// RangeExpression.String gets the string format of the Range type token func (re *RangeExpression) String() string { var out bytes.Buffer From 19972aacbf7292fcfbb8b420dff080f40427ea85 Mon Sep 17 00:00:00 2001 From: Maxwell-Alexius Date: Sat, 18 Nov 2017 19:19:33 +0800 Subject: [PATCH 7/7] Rebase master --- vm/float_test.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/vm/float_test.go b/vm/float_test.go index 5f12e3919..a07490f7d 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -200,17 +200,25 @@ func TestFloatConversions(t *testing.T) { input string expected interface{} }{ - {`100.3.to_i`, 100}, - {`100.3.to_s`, "100.3"}, - {`100.3.to_d.to_s`, "100.3"}, {`(100.3).to_i`, 100}, {`(100.3).to_s`, "100.3"}, + {`(100.3).to_d.to_s`, "100.3"}, {` - 3.14159265358979.to_d.to_s`, + (3.14159265358979).to_d.to_s`, "3.14159265358979"}, {` - -273.150000000.to_d.to_s`, + (-273.150000000).to_d.to_s`, "-273.15"}, + {`100.3.to_i`, 100}, + {`100.3.to_s`, "100.3"}, + {`100.3.to_d.to_s`, "100.3"}, + {` + 3.14159265358979.to_d.to_s`, + "3.14159265358979"}, + // TODO: Able to parse negative float value and call method without parentheses + //{` + //-273.150000000.to_d.to_s`, + // "-273.15"}, } for i, tt := range tests {