Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added 'printf' and 'sprintf' primitives #65

Merged
merged 2 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ The interpreter in _this_ repository has been significantly extended from the st
* Added support for [ternary expressions](#261-ternary-expressions).
* Added support for creating arrays of consecutive integers via the range operator (`1..10`).
* Added the ability to iterate over the contents of arrays, hashes, and strings via the `foreach` statement.

* Added `printf` and `sprintf` primitives, which work as you would expect.
* `printf( "%d %s", 3, "Steve" );`


## 1. Installation
Expand Down Expand Up @@ -267,9 +268,13 @@ The core primitives are:
* `push`
* push an elements into the array.
* `puts`
* print literal value of objects.
* Write literal value of objects to STDOUT.
* `printf`
* Write values to STDOUT, via a format-string.
* `set`
* insert key value pair into the map.
* `sprintf`
* Create strings, via a format-string.
* `string`
* convert the given item to a string.
* `type`
Expand Down
57 changes: 57 additions & 0 deletions evaluator/stdlib_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,55 @@ func putsFun(args ...object.Object) object.Object {
return NULL
}

// printfFun is the implementation of our `printf` function.
func printfFun(args ...object.Object) object.Object {

// Convert to the formatted version, via our `sprintf`
// function.
out := sprintfFun(args...)

// If that returned a string then we can print it
if out.Type() == object.STRING_OBJ {
fmt.Print(out.(*object.String).Value)

}

return NULL
}

// sprintfFun is the implementation of our `sprintf` function.
func sprintfFun(args ...object.Object) object.Object {

// We expect 1+ arguments
if len(args) < 1 {
return &object.Null{}
}

// Type-check
if args[0].Type() != object.STRING_OBJ {
return &object.Null{}
}

// Get the format-string.
fs := args[0].(*object.String).Value

// Convert the arguments to something go's sprintf
// code will understand.
argLen := len(args)
fmtArgs := make([]interface{}, argLen-1)

// Here we convert and assign.
for i, v := range args[1:] {
fmtArgs[i] = v.ToInterface()
}

// Call the helper
out := fmt.Sprintf(fs, fmtArgs...)

// And now return the value.
return &object.String{Value: out}
}

// Get file info.
func statFun(args ...object.Object) object.Object {

Expand Down Expand Up @@ -603,10 +652,18 @@ func init() {
func(env *object.Environment, args ...object.Object) object.Object {
return (putsFun(args...))
})
RegisterBuiltin("printf",
func(env *object.Environment, args ...object.Object) object.Object {
return (printfFun(args...))
})
RegisterBuiltin("set",
func(env *object.Environment, args ...object.Object) object.Object {
return (setFun(args...))
})
RegisterBuiltin("sprintf",
func(env *object.Environment, args ...object.Object) object.Object {
return (sprintfFun(args...))
})
RegisterBuiltin("stat",
func(env *object.Environment, args ...object.Object) object.Object {
return (statFun(args...))
Expand Down
2 changes: 1 addition & 1 deletion lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ func (l *Lexer) NextToken() token.Token {
default:

if isDigit(l.ch) {
tok := l.readDecimal()
tok = l.readDecimal()
l.prevToken = tok
return tok

Expand Down
5 changes: 5 additions & 0 deletions object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ type Object interface {
// InvokeMethod invokes a method against the object.
// (Built-in methods only.)
InvokeMethod(method string, env Environment, args ...Object) Object

// ToInterface converts the given object to a "native" golang value,
// which is required to ensure that we can use the object in our
// `sprintf` or `printf` primitives.
ToInterface() interface{}
}

// Hashable type can be hashed
Expand Down
8 changes: 8 additions & 0 deletions object/object_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,11 @@ func (ao *Array) Next() (Object, Object, bool) {

return nil, &Integer{Value: 0}, false
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (ao *Array) ToInterface() interface{} {
return "ARRAY"
}
8 changes: 8 additions & 0 deletions object/object_bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,11 @@ func (b *Boolean) InvokeMethod(method string, env Environment, args ...Object) O
}
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (b *Boolean) ToInterface() interface{} {
return b.Value
}
8 changes: 8 additions & 0 deletions object/object_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,11 @@ func (b *Builtin) InvokeMethod(method string, env Environment, args ...Object) O
}
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (b *Builtin) ToInterface() interface{} {
return "<BUILTIN>"
}
8 changes: 8 additions & 0 deletions object/object_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,11 @@ func (e *Error) InvokeMethod(method string, env Environment, args ...Object) Obj
//
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (e *Error) ToInterface() interface{} {
return "<ERROR>"
}
8 changes: 8 additions & 0 deletions object/object_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,11 @@ func (f *File) InvokeMethod(method string, env Environment, args ...Object) Obje
}
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (f *File) ToInterface() interface{} {
return "<FILE>"
}
8 changes: 8 additions & 0 deletions object/object_float.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ func (f *Float) InvokeMethod(method string, env Environment, args ...Object) Obj
}
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (f *Float) ToInterface() interface{} {
return f.Value
}
8 changes: 8 additions & 0 deletions object/object_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,11 @@ func (f *Function) InvokeMethod(method string, env Environment, args ...Object)
}
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (f *Function) ToInterface() interface{} {
return "<FUNCTION>"
}
8 changes: 8 additions & 0 deletions object/object_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,11 @@ func (h *Hash) Next() (Object, Object, bool) {

return nil, &Integer{Value: 0}, false
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (h *Hash) ToInterface() interface{} {
return "<HASH>"
}
8 changes: 8 additions & 0 deletions object/object_int.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ func (i *Integer) InvokeMethod(method string, env Environment, args ...Object) O
}
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (i *Integer) ToInterface() interface{} {
return i.Value
}
8 changes: 8 additions & 0 deletions object/object_null.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ func (n *Null) Inspect() string {
func (n *Null) InvokeMethod(method string, env Environment, args ...Object) Object {
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (n *Null) ToInterface() interface{} {
return "<NULL>"
}
8 changes: 8 additions & 0 deletions object/object_regexp.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@ func (r *Regexp) Inspect() string {
func (r *Regexp) InvokeMethod(method string, env Environment, args ...Object) Object {
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (r *Regexp) ToInterface() interface{} {
return "<REGEXP>"
}
8 changes: 8 additions & 0 deletions object/object_return.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,11 @@ func (rv *ReturnValue) InvokeMethod(method string, env Environment, args ...Obje
//
return nil
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (rv *ReturnValue) ToInterface() interface{} {
return "<RETURN_VALUE>"
}
8 changes: 8 additions & 0 deletions object/object_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,11 @@ func (s *String) Next() (Object, Object, bool) {

return nil, &Integer{Value: 0}, false
}

// ToInterface converts this object to a go-interface, which will allow
// it to be used naturally in our sprintf/printf primitives.
//
// It might also be helpful for embedded users.
func (s *String) ToInterface() interface{} {
return s.Value
}