From 37016f4e17118c3dc40d90bb38fe0ca80d03ae7a Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Thu, 24 Jan 2019 16:38:04 -0800 Subject: [PATCH 1/2] add 'printf' and 'sprintf' builtin functions --- docs/builtins.md | 23 ++++++++++++++++++ objects/builtin_print.go | 50 ++++++++++++++++++++++++++++++++++++++ objects/builtins.go | 8 ++++++ runtime/vm_builtin_test.go | 9 +++++++ 4 files changed, 90 insertions(+) diff --git a/docs/builtins.md b/docs/builtins.md index b190c80..266249a 100644 --- a/docs/builtins.md +++ b/docs/builtins.md @@ -7,6 +7,29 @@ Prints a string representation of the given variable to the standard output. ```golang v := [1, 2, 3] print(v) // "[1, 2, 3]" + +print(1, 2, 3) +// "1" +// "2" +// "3" +``` + +## printf + +Prints a formatted string to the standard output. It does not append the newline character at the end. The first argument must a String object. It's same as Go's `fmt.Printf`. + +```golang +a := [1, 2, 3] +printf("foo %v", a) // "foo [1, 2, 3]" +``` + +## sprintf + +Returns a formatted string. The first argument must be a String object. It's the same as Go's `fmt.Sprintf`. + +```golang +a := [1, 2, 3] +b := sprintp("foo %v", a) // b == "foo [1, 2, 3]" ``` ## len diff --git a/objects/builtin_print.go b/objects/builtin_print.go index 46be159..22be787 100644 --- a/objects/builtin_print.go +++ b/objects/builtin_print.go @@ -4,6 +4,7 @@ import ( "fmt" ) +// print(args...) func builtinPrint(args ...Object) (Object, error) { for _, arg := range args { if str, ok := arg.(*String); ok { @@ -15,3 +16,52 @@ func builtinPrint(args ...Object) (Object, error) { return nil, nil } + +// printf("format", args...) +func builtinPrintf(args ...Object) (Object, error) { + numArgs := len(args) + if numArgs == 0 { + return nil, ErrWrongNumArguments + } + + format, ok := args[0].(*String) + if !ok { + return nil, ErrInvalidTypeConversion + } + if numArgs == 1 { + fmt.Print(format) + return nil, nil + } + + formatArgs := make([]interface{}, numArgs-1, numArgs-1) + for idx, arg := range args[1:] { + formatArgs[idx] = objectToInterface(arg) + } + + fmt.Printf(format.Value, formatArgs...) + + return nil, nil +} + +// sprintf("format", args...) +func builtinSprintf(args ...Object) (Object, error) { + numArgs := len(args) + if numArgs == 0 { + return nil, ErrWrongNumArguments + } + + format, ok := args[0].(*String) + if !ok { + return nil, ErrInvalidTypeConversion + } + if numArgs == 1 { + return format, nil // okay to return 'format' directly as String is immutable + } + + formatArgs := make([]interface{}, numArgs-1, numArgs-1) + for idx, arg := range args[1:] { + formatArgs[idx] = objectToInterface(arg) + } + + return &String{Value: fmt.Sprintf(format.Value, formatArgs...)}, nil +} diff --git a/objects/builtins.go b/objects/builtins.go index a55d41d..428cc09 100644 --- a/objects/builtins.go +++ b/objects/builtins.go @@ -12,6 +12,14 @@ var Builtins = []NamedBuiltinFunc{ Name: "print", Func: builtinPrint, }, + { + Name: "printf", + Func: builtinPrintf, + }, + { + Name: "sprintf", + Func: builtinSprintf, + }, { Name: "len", Func: builtinLen, diff --git a/runtime/vm_builtin_test.go b/runtime/vm_builtin_test.go index f6481a3..dcefff8 100644 --- a/runtime/vm_builtin_test.go +++ b/runtime/vm_builtin_test.go @@ -140,4 +140,13 @@ func TestBuiltinFunction(t *testing.T) { expect(t, `out = from_json("5")`, 5.0) expect(t, `out = from_json("[\"bar\",1,1.8,56,true]")`, ARR{"bar", 1.0, 1.8, 56.0, true}) + + // sprintf + expect(t, `out = sprintf("")`, "") + expect(t, `out = sprintf("foo")`, "foo") + expect(t, `out = sprintf("foo %d %v %s", 1, 2, "bar")`, "foo 1 2 bar") + expect(t, `out = sprintf("foo %v", [1, "bar", true])`, "foo [1 bar true]") + expect(t, `out = sprintf("foo %v %d", [1, "bar", true], 19)`, "foo [1 bar true] 19") + expectError(t, `sprintf(1)`) // format has to be String + expectError(t, `sprintf('c')`) // format has to be String } From 011833b95b7b17346af9b1cb597cd4c4040b367a Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Thu, 24 Jan 2019 17:11:51 -0800 Subject: [PATCH 2/2] fix test fail --- compiler/compiler_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 5dfefe5..f05aedf 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -697,7 +697,7 @@ func TestCompiler_Compile(t *testing.T) { expect(t, `len([]);`, bytecode( concat( - compiler.MakeInstruction(compiler.OpGetBuiltin, 1), + compiler.MakeInstruction(compiler.OpGetBuiltin, 3), compiler.MakeInstruction(compiler.OpArray, 0), compiler.MakeInstruction(compiler.OpCall, 1), compiler.MakeInstruction(compiler.OpPop)), @@ -710,7 +710,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpPop)), objectsArray( compiledFunction(0, 0, - compiler.MakeInstruction(compiler.OpGetBuiltin, 1), + compiler.MakeInstruction(compiler.OpGetBuiltin, 3), compiler.MakeInstruction(compiler.OpArray, 0), compiler.MakeInstruction(compiler.OpCall, 1), compiler.MakeInstruction(compiler.OpReturnValue, 1)))))