Repository: skx/monkey
Pull request #50 (2019-03-24 06:34:05)

 go modules & linting
This pull-request updates the README.md file to mention go modules, and improves the testing which is applied when pull-requests are created/updated.


Patch:
diff --git a/.github/build b/.github/build
index 5f9606f..ea779ca 100755
--- a/.github/build
+++ b/.github/build
@@ -3,9 +3,6 @@
 # The basename of our binary
 BASE="monkey"
 
-# Get the dependencies
-go mod init
-
 #
 # We build on multiple platforms/archs
 #
diff --git a/README.md b/README.md
index 79b3382..98e18fa 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,37 @@
 [![license](https://img.shields.io/github/license/skx/monkey.svg)](https://github.com/skx/monkey/blob/master/LICENSE)
 [![Release](https://img.shields.io/github/release/skx/monkey.svg)](https://github.com/skx/monkey/releases/latest)
 
+
+
+* [Monkey](#monkey)
+  * [My changes](#my-changes)
+* [1. Installation](#1-installation)
+  * [Source Installation go &lt;=  1.11](#source-installation-go---111)
+  * [Source installation go &gt;= 1.12](#source-installation-go--112)
+  * [Binary Releases](#binary-releases)
+* [1.1 Usage](#11-usage)
+* [2 Syntax](#2-syntax)
+  * [2.1 Definitions](#21-definitions)
+  * [2.2 Arithmetic operations](#22-arithmetic-operations)
+  * [2.3 Builtin containers](#23-builtin-containers)
+    * [2.3.1 Arrays](#231-arrays)
+    * [2.3.2 Hashes](#232-hashes)
+  * [2.4 Builtin functions](#24-builtin-functions)
+    * [2.4.1 The Standard Library](#241-the-standard-library)
+  * [2.5 Functions](#25-functions)
+  * [2.6 If-else statements](#26-if-else-statements)
+  * [2.7 For-loop statements](#27-for-loop-statements)
+  * [2.8 Comments](#28-comments)
+  * [2.9 Postfix Operators](#29-postfix-operators)
+  * [2.10 Command Execution](#210-command-execution)
+  * [2.11 Regular Expressions](#211-regular-expressions)
+  * [2.12 File I/O](#212-file-io)
+  * [2.13 File Operations](#213-file-operations)
+  * [3. Object Methods](#3-object-methods)
+      * [3.1 Defininig New Object Methods](#31-defininig-new-object-methods)
+* [Github Setup](#github-setup)
+
+
 # Monkey
 
 This repository contains an interpreter for the "Monkey" programming language, as described in [Write an Interpreter in Go](https://interpreterbook.com).
@@ -47,9 +78,24 @@ The interpreter in _this_ repository has been significantly extended from the st
 
 ## 1. Installation
 
-If you have a working [golang](https://golang.org/) setup you can install the interpreter via:
+There are two ways to install `monkey` from source, depending upon the version of go you're using:
+
+### Source Installation go <=  1.11
+
+If you're using `go` before 1.11 then the following command should fetch/update `monkey`, and install it upon your system:
+
+     $ go get -u github.com/skx/monkey
+
+### Source installation go >= 1.12
+
+If you're using a more recent version of `go` (which is _highly_ recommended), you need to clone to a directory which is not present upon your `GOPATH`:
+
+    git clone https://github.com/skx/monkey
+    cd monkey
+    go install
+
 
-    $ go get -u  github.com/skx/monkey
+### Binary Releases
 
 Alternatively you could install a binary-release, from the [release page](https://github.com/skx/monkey/releases).
 
diff --git a/evaluator/stdlib_core.go b/evaluator/stdlib_core.go
index be0f9d8..f407fa5 100644
--- a/evaluator/stdlib_core.go
+++ b/evaluator/stdlib_core.go
@@ -361,29 +361,29 @@ func statFun(args ...object.Object) object.Object {
 	//
 
 	// size -> int
-	size_data := &object.Integer{Value: info.Size()}
-	size_key := &object.String{Value: "size"}
-	size_hash := object.HashPair{Key: size_key, Value: size_data}
-	res[size_key.HashKey()] = size_hash
+	sizeData := &object.Integer{Value: info.Size()}
+	sizeKey := &object.String{Value: "size"}
+	sizeHash := object.HashPair{Key: sizeKey, Value: sizeData}
+	res[sizeKey.HashKey()] = sizeHash
 
 	// mod-time -> int
-	mtime_data := &object.Integer{Value: info.ModTime().Unix()}
-	mtime_key := &object.String{Value: "mtime"}
-	mtime_hash := object.HashPair{Key: mtime_key, Value: mtime_data}
-	res[mtime_key.HashKey()] = mtime_hash
+	mtimeData := &object.Integer{Value: info.ModTime().Unix()}
+	mtimeKey := &object.String{Value: "mtime"}
+	mtimeHash := object.HashPair{Key: mtimeKey, Value: mtimeData}
+	res[mtimeKey.HashKey()] = mtimeHash
 
 	// Perm -> string
-	perm_data := &object.String{Value: info.Mode().String()}
-	perm_key := &object.String{Value: "perm"}
-	perm_hash := object.HashPair{Key: perm_key, Value: perm_data}
-	res[perm_key.HashKey()] = perm_hash
+	permData := &object.String{Value: info.Mode().String()}
+	permKey := &object.String{Value: "perm"}
+	permHash := object.HashPair{Key: permKey, Value: permData}
+	res[permKey.HashKey()] = permHash
 
 	// Mode -> string  (because we want to emphasise the octal nature)
 	m := fmt.Sprintf("%04o", info.Mode().Perm())
-	mode_data := &object.String{Value: m}
-	mode_key := &object.String{Value: "mode"}
-	mode_hash := object.HashPair{Key: mode_key, Value: mode_data}
-	res[mode_key.HashKey()] = mode_hash
+	modeData := &object.String{Value: m}
+	modeKey := &object.String{Value: "mode"}
+	modeHash := object.HashPair{Key: modeKey, Value: modeData}
+	res[modeKey.HashKey()] = modeHash
 
 	typeStr := "unknown"
 	if info.Mode().IsDir() {
@@ -394,10 +394,10 @@ func statFun(args ...object.Object) object.Object {
 	}
 
 	// type: string
-	type_data := &object.String{Value: typeStr}
-	type_key := &object.String{Value: "type"}
-	type_hash := object.HashPair{Key: type_key, Value: type_data}
-	res[type_key.HashKey()] = type_hash
+	typeData := &object.String{Value: typeStr}
+	typeKey := &object.String{Value: "type"}
+	typeHash := object.HashPair{Key: typeKey, Value: typeData}
+	res[typeKey.HashKey()] = typeHash
 
 	return &object.Hash{Pairs: res}
 
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..2ee7803
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/skx/monkey
+
+go 1.12
diff --git a/lexer/lexer.go b/lexer/lexer.go
index bcd9bf1..99afb81 100644
--- a/lexer/lexer.go
+++ b/lexer/lexer.go
@@ -206,7 +206,7 @@ func (l *Lexer) NextToken() token.Token {
 }
 
 // return new token
-func newToken(tokenType token.TokenType, ch rune) token.Token {
+func newToken(tokenType token.Type, ch rune) token.Token {
 	return token.Token{Type: tokenType, Literal: string(ch)}
 }
 
diff --git a/object/object.go b/object/object.go
index 48a1e91..f2b8da7 100644
--- a/object/object.go
+++ b/object/object.go
@@ -1,10 +1,10 @@
 // Package object contains our core-definitions for objects.
 package object
 
-// ObjectType describes the type of an object.
-type ObjectType string
+// Type describes the type of an object.
+type Type string
 
-// pre-defined constant ObjectType
+// pre-defined constant Type
 const (
 	INTEGER_OBJ      = "INTEGER"
 	FLOAT_OBJ        = "FLOAT"
@@ -24,7 +24,7 @@ const (
 type Object interface {
 
 	// Type returns the type of this object.
-	Type() ObjectType
+	Type() Type
 
 	// Inspect returns a string-representation of the given object.
 	Inspect() string
diff --git a/object/object_array.go b/object/object_array.go
index a3e7dcd..1787082 100644
--- a/object/object_array.go
+++ b/object/object_array.go
@@ -13,7 +13,7 @@ type Array struct {
 }
 
 // Type returns the type of this object.
-func (ao *Array) Type() ObjectType {
+func (ao *Array) Type() Type {
 	return ARRAY_OBJ
 }
 
diff --git a/object/object_bool.go b/object/object_bool.go
index 954d540..5822c5e 100644
--- a/object/object_bool.go
+++ b/object/object_bool.go
@@ -13,7 +13,7 @@ type Boolean struct {
 }
 
 // Type returns the type of this object.
-func (b *Boolean) Type() ObjectType {
+func (b *Boolean) Type() Type {
 	return BOOLEAN_OBJ
 }
 
diff --git a/object/object_builtin.go b/object/object_builtin.go
index 8e23c66..28184b7 100644
--- a/object/object_builtin.go
+++ b/object/object_builtin.go
@@ -10,7 +10,7 @@ type Builtin struct {
 }
 
 // Type returns the type of this object.
-func (b *Builtin) Type() ObjectType {
+func (b *Builtin) Type() Type {
 	return BUILTIN_OBJ
 }
 
diff --git a/object/object_error.go b/object/object_error.go
index 517c388..719da86 100644
--- a/object/object_error.go
+++ b/object/object_error.go
@@ -7,7 +7,7 @@ type Error struct {
 }
 
 // Type returns the type of this object.
-func (e *Error) Type() ObjectType {
+func (e *Error) Type() Type {
 	return ERROR_OBJ
 }
 
diff --git a/object/object_file.go b/object/object_file.go
index 2abaed9..472c39b 100644
--- a/object/object_file.go
+++ b/object/object_file.go
@@ -24,7 +24,7 @@ type File struct {
 }
 
 // Type returns the type of this object.
-func (f *File) Type() ObjectType {
+func (f *File) Type() Type {
 	return FILE_OBJ
 }
 
diff --git a/object/object_float.go b/object/object_float.go
index cabd955..0817689 100644
--- a/object/object_float.go
+++ b/object/object_float.go
@@ -19,7 +19,7 @@ func (f *Float) Inspect() string {
 }
 
 // Type returns the type of this object.
-func (f *Float) Type() ObjectType {
+func (f *Float) Type() Type {
 	return FLOAT_OBJ
 }
 
diff --git a/object/object_function.go b/object/object_function.go
index 3a941c5..681e81d 100644
--- a/object/object_function.go
+++ b/object/object_function.go
@@ -17,7 +17,7 @@ type Function struct {
 }
 
 // Type returns the type of this object.
-func (f *Function) Type() ObjectType {
+func (f *Function) Type() Type {
 	return FUNCTION_OBJ
 }
 
diff --git a/object/object_hash.go b/object/object_hash.go
index 55520a6..bbdd91e 100644
--- a/object/object_hash.go
+++ b/object/object_hash.go
@@ -10,7 +10,7 @@ import (
 // HashKey is the structure used for hash-keys
 type HashKey struct {
 	// Type holds the type of the object.
-	Type ObjectType
+	Type Type
 
 	// Value holds the actual hash-key value.
 	Value uint64
@@ -32,7 +32,7 @@ type Hash struct {
 }
 
 // Type returns the type of this object.
-func (h *Hash) Type() ObjectType {
+func (h *Hash) Type() Type {
 	return HASH_OBJ
 }
 
diff --git a/object/object_int.go b/object/object_int.go
index cf023c9..fb7ce9a 100644
--- a/object/object_int.go
+++ b/object/object_int.go
@@ -18,7 +18,7 @@ func (i *Integer) Inspect() string {
 }
 
 // Type returns the type of this object.
-func (i *Integer) Type() ObjectType {
+func (i *Integer) Type() Type {
 	return INTEGER_OBJ
 }
 
diff --git a/object/object_null.go b/object/object_null.go
index df48329..6853531 100644
--- a/object/object_null.go
+++ b/object/object_null.go
@@ -4,7 +4,7 @@ package object
 type Null struct{}
 
 // Type returns the type of this object.
-func (n *Null) Type() ObjectType {
+func (n *Null) Type() Type {
 	return NULL_OBJ
 }
 
diff --git a/object/object_return.go b/object/object_return.go
index c236512..032015c 100644
--- a/object/object_return.go
+++ b/object/object_return.go
@@ -7,7 +7,7 @@ type ReturnValue struct {
 }
 
 // Type returns the type of this object.
-func (rv *ReturnValue) Type() ObjectType {
+func (rv *ReturnValue) Type() Type {
 	return RETURN_VALUE_OBJ
 }
 
diff --git a/object/object_string.go b/object/object_string.go
index 58c6bc6..68df621 100644
--- a/object/object_string.go
+++ b/object/object_string.go
@@ -17,7 +17,7 @@ type String struct {
 }
 
 // Type returns the type of this object.
-func (s *String) Type() ObjectType {
+func (s *String) Type() Type {
 	return STRING_OBJ
 }
 
diff --git a/parser/parser.go b/parser/parser.go
index ea584af..599c2fd 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -37,7 +37,7 @@ const (
 )
 
 // each token precedence
-var precedences = map[token.TokenType]int{
+var precedences = map[token.Type]int{
 	token.ASSIGN:          ASSIGN,
 	token.EQ:              EQUALS,
 	token.NOT_EQ:          EQUALS,
@@ -82,15 +82,15 @@ type Parser struct {
 
 	// prefixParseFns holds a map of parsing methods for
 	// prefix-based syntax.
-	prefixParseFns map[token.TokenType]prefixParseFn
+	prefixParseFns map[token.Type]prefixParseFn
 
 	// infixParseFns holds a map of parsing methods for
 	// infix-based syntax.
-	infixParseFns map[token.TokenType]infixParseFn
+	infixParseFns map[token.Type]infixParseFn
 
 	// postfixParseFns holds a map of parsing methods for
 	// postfix-based syntax.
-	postfixParseFns map[token.TokenType]postfixParseFn
+	postfixParseFns map[token.Type]postfixParseFn
 }
 
 // New returns our new parser-object.
@@ -99,7 +99,7 @@ func New(l *lexer.Lexer) *Parser {
 	p.nextToken()
 	p.nextToken()
 
-	p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
+	p.prefixParseFns = make(map[token.Type]prefixParseFn)
 	p.registerPrefix(token.IDENT, p.parseIdentifier)
 	p.registerPrefix(token.INT, p.parseIntegerLiteral)
 	p.registerPrefix(token.FLOAT, p.parseFloatLiteral)
@@ -117,7 +117,7 @@ func New(l *lexer.Lexer) *Parser {
 	p.registerPrefix(token.LBRACKET, p.parseArrayLiteral)
 	p.registerPrefix(token.LBRACE, p.parseHashLiteral)
 
-	p.infixParseFns = make(map[token.TokenType]infixParseFn)
+	p.infixParseFns = make(map[token.Type]infixParseFn)
 	p.registerInfix(token.ASSIGN, p.parseAssignExpression)
 	p.registerInfix(token.PLUS, p.parseInfixExpression)
 	p.registerInfix(token.MOD, p.parseInfixExpression)
@@ -141,24 +141,24 @@ func New(l *lexer.Lexer) *Parser {
 	p.registerInfix(token.ASTERISK_EQUALS, p.parseAssignExpression)
 	p.registerInfix(token.SLASH_EQUALS, p.parseAssignExpression)
 
-	p.postfixParseFns = make(map[token.TokenType]postfixParseFn)
+	p.postfixParseFns = make(map[token.Type]postfixParseFn)
 	p.registerPostfix(token.PLUS_PLUS, p.parsePostfixExpression)
 	p.registerPostfix(token.MINUS_MINUS, p.parsePostfixExpression)
 	return p
 }
 
 // registerPrefix registers a function for handling a prefix-based statement
-func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
+func (p *Parser) registerPrefix(tokenType token.Type, fn prefixParseFn) {
 	p.prefixParseFns[tokenType] = fn
 }
 
 // registerInfix registers a function for handling a infix-based statement
-func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
+func (p *Parser) registerInfix(tokenType token.Type, fn infixParseFn) {
 	p.infixParseFns[tokenType] = fn
 }
 
 // registerPostfix registers a function for handling a postfix-based statement
-func (p *Parser) registerPostfix(tokenType token.TokenType, fn postfixParseFn) {
+func (p *Parser) registerPostfix(tokenType token.Type, fn postfixParseFn) {
 	p.postfixParseFns[tokenType] = fn
 }
 
@@ -168,7 +168,7 @@ func (p *Parser) Errors() []string {
 }
 
 // peekError raises an error if the next token is not the expected type.
-func (p *Parser) peekError(t token.TokenType) {
+func (p *Parser) peekError(t token.Type) {
 	msg := fmt.Sprintf("expected next token to be %s, got %s instead around line %d", t, p.curToken.Type, p.l.GetLine())
 	p.errors = append(p.errors, msg)
 }
@@ -256,7 +256,7 @@ func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
 }
 
 // no prefix parse function error
-func (p *Parser) noPrefixParseFnError(t token.TokenType) {
+func (p *Parser) noPrefixParseFnError(t token.Type) {
 	msg := fmt.Sprintf("no prefix parse function for %s found around line %d", t, p.l.GetLine())
 	p.errors = append(p.errors, msg)
 }
@@ -532,7 +532,7 @@ func (p *Parser) parseArrayLiteral() ast.Expression {
 }
 
 // parsearray elements literal
-func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression {
+func (p *Parser) parseExpressionList(end token.Type) []ast.Expression {
 	list := make([]ast.Expression, 0)
 	if p.peekTokenIs(end) {
 		p.nextToken()
@@ -643,18 +643,18 @@ func (p *Parser) parseMethodCallExpression(obj ast.Expression) ast.Expression {
 }
 
 // curTokenIs tests if the current token has the given type.
-func (p *Parser) curTokenIs(t token.TokenType) bool {
+func (p *Parser) curTokenIs(t token.Type) bool {
 	return p.curToken.Type == t
 }
 
 // peekTokenIs tests if the next token has the given type.
-func (p *Parser) peekTokenIs(t token.TokenType) bool {
+func (p *Parser) peekTokenIs(t token.Type) bool {
 	return p.peekToken.Type == t
 }
 
 // expectPeek validates the next token is of the given type,
 // and advances if so.  If it is not an error is stored.
-func (p *Parser) expectPeek(t token.TokenType) bool {
+func (p *Parser) expectPeek(t token.Type) bool {
 	if p.peekTokenIs(t) {
 		p.nextToken()
 		return true
diff --git a/token/token.go b/token/token.go
index 8d2928a..21180f2 100644
--- a/token/token.go
+++ b/token/token.go
@@ -1,15 +1,15 @@
 package token
 
-// TokenType is a string
-type TokenType string
+// Type is a string
+type Type string
 
 // Token struct represent the lexer token
 type Token struct {
-	Type    TokenType
+	Type    Type
 	Literal string
 }
 
-// pre-defined TokenType
+// pre-defined Type
 const (
 	EOF             = "EOF"
 	IDENT           = "IDENT"
@@ -62,7 +62,7 @@ const (
 )
 
 // reversed keywords
-var keywords = map[string]TokenType{
+var keywords = map[string]Type{
 	"const":    CONST,
 	"else":     ELSE,
 	"false":    FALSE,
@@ -76,7 +76,7 @@ var keywords = map[string]TokenType{
 }
 
 // LookupIdentifier used to determinate whether identifier is keyword nor not
-func LookupIdentifier(identifier string) TokenType {
+func LookupIdentifier(identifier string) Type {
 	if tok, ok := keywords[identifier]; ok {
 		return tok
 	}


Test patch:
diff --git a/.github/run-tests.sh b/.github/run-tests.sh
index c9434c7..e4b656c 100755
--- a/.github/run-tests.sh
+++ b/.github/run-tests.sh
@@ -1,11 +1,42 @@
 #!/bin/sh
 
-# init modules
-go mod init
+# Install the lint-tool, and the shadow-tool
+go get -u golang.org/x/lint/golint
+go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
 
-# Run golang tests
-go test ./...
+# At this point failures cause aborts
+set -e
+
+# Run the linter
+echo "Launching linter .."
+
+#
+# We have a bunch of errors which we need to mask
+#
+#  opcode/opcode.go:8:2: don't use ALL_CAPS in Go names; use CamelCase
+#  opcode/opcode.go:11:2: don't use ALL_CAPS in Go names; use CamelCase
+#  opcode/opcode.go:14:2: don't use ALL_CAPS in Go names; use CamelCase
+#  opcode/opcode.go:17:2: don't use ALL_CAPS in Go names; use CamelCase
+#  opcode/opcode.go:20:2: don't use ALL_CAPS in Go names; use CamelCase
+#
+( golint  ./...  | grep -v ALL_CAPS > lint.out ) || true
+if [ -s lint.out ]; then
+    echo "Linter errors: "
+    cat lint.out
+    exit 0
+else
+    rm lint.out
+fi
+
+echo "Completed linter .."
+
+# Run the shadow-checker
+echo "Launching shadowed-variable check .."
+go vet -vettool=$(which shadow) ./...
+echo "Completed shadowed-variable check .."
 
 # Build a binary and run it.
+echo "Launching monkey-test .."
 go build -ldflags "-X main.version=test-code" -o "monkey"
 ./monkey -eval 'puts("ok - stdlib\n"); exit(0);'
+echo "Completed monkey-test .."
diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go
index 27915a2..cfb6a26 100644
--- a/lexer/lexer_test.go
+++ b/lexer/lexer_test.go
@@ -10,7 +10,7 @@ func TestNextToken1(t *testing.T) {
 	input := `=+(){},;`
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.ASSIGN, "="},
@@ -63,7 +63,7 @@ if(5<10){
 for
 `
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.LET, "let"},
@@ -191,7 +191,7 @@ let a = 1;
 // comment on two-lines`
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.ASSIGN, "="},
@@ -228,7 +228,7 @@ let a = 1;
  */`
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.ASSIGN, "="},
@@ -256,7 +256,7 @@ func TestIntegers(t *testing.T) {
 	input := `10 0x10 0xF0 0xFE 0b0101 0xFF 0b101 0xFF;`
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.INT, "10"},
@@ -288,7 +288,7 @@ func TestShebang(t *testing.T) {
 10;`
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.INT, "10"},
@@ -377,7 +377,7 @@ moi.kissa
 `
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.IDENT, "os.getenv"},
@@ -420,7 +420,7 @@ a?.b?();
 `
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.IDENT, "foo"},
@@ -464,7 +464,7 @@ func TestIntDotMethod(t *testing.T) {
 `
 
 	tests := []struct {
-		expectedType    token.TokenType
+		expectedType    token.Type
 		expectedLiteral string
 	}{
 		{token.INT, "3"},