Skip to content

Commit

Permalink
[cw|mr] add execution engine
Browse files Browse the repository at this point in the history
* implemented ExecutionEngine
* refactor test suite to spin up/down ExecutionEngine

Git issue references:

Gamelan music currently playing: Golek Lambangsari
  • Loading branch information
connorwalsh committed Mar 22, 2018
1 parent 6b4b61d commit d3ae0a2
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 78 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
sudo: required

services:
- docker

language: go
go:
- "1.9"
11 changes: 11 additions & 0 deletions compterpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ type Compterpreter struct {
}

func NewCompterpreter(c *Config) *Compterpreter {
// whenever we create a new compterpreter, we will also
// create a new execution engine which is set in the global
// scope.
err := NewExecutionEngine()
if err != nil {
panic(err)
}

return &Compterpreter{
Config: c,
Symbols: PopulateSymbols(),
Expand All @@ -35,6 +43,9 @@ func (c *Compterpreter) Compterpret() error {
err error
)

// always shutdown the docker execution engine
defer ShutdownExecutionEngine()

// initialize a scanner to read through source code character
// by character
err = c.LoadSourceCode()
Expand Down
22 changes: 13 additions & 9 deletions compterpreter_test.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
package dockerlang

import (
"testing"
"github.com/stretchr/testify/suite"
)

func TestLoadSourceCode_NoSuchFile(t *testing.T) {
type CompterpreterSuite struct {
suite.Suite
}

func (s *CompterpreterSuite) AfterTest(suiteName, testName string) {
ShutdownExecutionEngine()
}

func (s *CompterpreterSuite) TestLoadSourceCode_NoSuchFile() {
conf := &Config{SrcFileName: "nonexistent_test_src.doc"}
compt := NewCompterpreter(conf)

err := compt.LoadSourceCode()
if err == nil {
t.Error("failed to fail to find file")
}
s.Error(err)
}

func TestLoadSourceCode(t *testing.T) {
func (s *CompterpreterSuite) TestLoadSourceCode() {
conf := &Config{SrcFileName: "test/test.doc"}
compt := NewCompterpreter(conf)

err := compt.LoadSourceCode()
if err != nil {
t.Error(err)
}
s.NoError(err)
}
74 changes: 74 additions & 0 deletions execution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package dockerlang

import (
"context"
"fmt"
"os"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
uuid "github.com/satori/go.uuid"
)

var (
executer *ExecutionEngine
)

type ExecutionEngine struct {
Docker *client.Client
Guillotine string
Network string
}

// constructs an ExecutionEngine and binds to the globally scoped executer.
func NewExecutionEngine() error {
// set the API version to use in an environment variable
// TODO it would be nice to configure based on the docker version
// a user currently has.... not enough time right now so skipping that.
err := os.Setenv("DOCKER_API_VERSION", "1.35")
if err != nil {
return err
}

dockerClient, err := client.NewEnvClient()
if err != nil {
// this is probably because the person who is using dockerlang
// hasn't installed or started docker on their system -____-
// unclear why anyone would *not* have docker in their life.
return err
}

// define unique network name
networkName := fmt.Sprintf("dockerlang.%s", uuid.NewV4().String())

// bind new ExecutionEngine to globally scoped variable
executer = &ExecutionEngine{
Docker: dockerClient,
Guillotine: "robespierre",
Network: networkName,
}

// setup container bridge network if one doesn't already exist.
response, err := executer.Docker.NetworkCreate(
context.TODO(),
networkName,
types.NetworkCreate{},
)
if err != nil {
return err
}

fmt.Println(response)

return nil
}

func ShutdownExecutionEngine() error {
err := executer.Docker.NetworkRemove(context.TODO(), executer.Network)
if err != nil && !client.IsErrNotFound(err) {
// something is very wrong here
panic(err)
}

return nil
}
35 changes: 35 additions & 0 deletions execution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package dockerlang

import (
"github.com/stretchr/testify/suite"
)

type ExecutionSuite struct {
suite.Suite
}

func (s *ExecutionSuite) AfterTest(suiteName, testName string) {
ShutdownExecutionEngine()
}

// NOTE: for these tests to run, we need to ensure that docker is running on the
// host machine!
func (s *ExecutionSuite) TestNewExecutionEngine() {
err := NewExecutionEngine()
s.NoError(err)
}

func (s *ExecutionSuite) TestShutdownExecutionEngine() {
err := NewExecutionEngine()
s.NoError(err)

err = ShutdownExecutionEngine()
s.NoError(err)
}

func (s *ExecutionSuite) TestShutdownExecutionEngine_NonExistent() {
executer.Network = "non-existent network"

err := ShutdownExecutionEngine()
s.NoError(err)
}
3 changes: 2 additions & 1 deletion forest.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ func (b *BaseAST) Execute() error {
// expressions) will have their own containers. Literals will not. The variable
// declaration operator will spin up an new unbound variable container while
// the variable assignment operator will set the value within that container.
//

// we will be using the docker golang api since docker is written
// in go and therefore we can just talk directly to docker through
// this code.

// before execution of anything, create a docker network
// when we run a docker container, we must give it the network name
// and the computation type (some data structure we haven't decided on yet)
Expand Down
3 changes: 3 additions & 0 deletions lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ func (c *Compterpreter) Lex() error {
switch err {
case io.EOF:
return nil
// case nil:
// c.Tokens = append(c.Tokens, token)
// continue
default:
return err
}
Expand Down
91 changes: 34 additions & 57 deletions lexer_test.go
Original file line number Diff line number Diff line change
@@ -1,99 +1,84 @@
package dockerlang

import (
"testing"
"github.com/stretchr/testify/suite"
)

func TestTokenizeNumber(t *testing.T) {
type LexerSuite struct {
suite.Suite
}

func (s *LexerSuite) AfterTest(suiteName, testName string) {
ShutdownExecutionEngine()
}

func (s *LexerSuite) TestTokenizeNumber() {
conf := &Config{SrcFileName: "test/test.doc"}
compt := NewCompterpreter(conf)

err := compt.LoadSourceCode()
if err != nil {
t.Error(err)
}
s.NoError(err)

// advance ptr to first character
compt.Advance()

compt.TokenizeNumber(compt.CurrentChar)

if compt.CurrentToken.Value != "1234" {
t.Error("incorrect token!")
}
s.EqualValues(compt.CurrentToken.Value, "1234")
}

func TestGetNextToken(t *testing.T) {
func (s *LexerSuite) TestGetNextToken() {
conf := &Config{SrcFileName: "test/test.doc"}
compt := NewCompterpreter(conf)

err := compt.LoadSourceCode()
if err != nil {
t.Error(err)
}
s.NoError(err)

// advance ptr to first character
compt.Advance()

s, err := compt.GetNextToken()
if err != nil {
t.Error(err)
}
if s.Value != "1234" {
t.Errorf("incorrect first token! Expected '1234' got '%s'", s)
}
t, err := compt.GetNextToken()
s.NoError(err)

s, err = compt.GetNextToken()
if err != nil {
t.Error(err)
}
if s.Value != "5678" {
t.Errorf("incorrect second token! Expected '5678' got '%s'", s)
}
s.EqualValues(t.Value, "1234")

t, err = compt.GetNextToken()
s.NoError(err)
s.EqualValues(t.Value, "5678")
}

func TestIsOperator(t *testing.T) {
func (s *LexerSuite) TestIsOperator() {
conf := &Config{SrcFileName: "test/test.doc"}
compt := NewCompterpreter(conf)
for _, operator := range []rune{'+', '†', '*', '‡', '%'} {
ok := compt.IsOperator(operator)
if !ok {
t.Error("not an operator! but it should be!")
}
s.True(ok)
}
for _, operator := range []rune{'q', '!', '❧', '0', ' '} {
ok := compt.IsOperator(operator)
if ok {
t.Error("that was an operator! but it shouldn't be!")
}
s.False(ok)
}
}

func TestIsPunctuation(t *testing.T) {
func (s *LexerSuite) TestIsPunctuation() {
conf := &Config{SrcFileName: "test/test.doc"}
compt := NewCompterpreter(conf)
for _, operator := range []rune{'(', ')', '(', ')'} {
ok := compt.IsPunctuation(operator)
if !ok {
t.Error("not punctuation! but it should be!")
}
s.True(ok)
}
for _, operator := range []rune{'q', '!', '❧', '0', ' '} {
ok := compt.IsPunctuation(operator)
if ok {
t.Error("that was puncuation! but it shouldn't be!")
}
s.False(ok)
}
}

func TestTokenizeOperator(t *testing.T) {
func (s *LexerSuite) TestTokenizeOperator() {
conf := &Config{SrcFileName: "test/test-operators.doc"}
compt := NewCompterpreter(conf)

err := compt.LoadSourceCode()
if err != nil {
t.Error(err)
}
s.NoError(err)

compt.Advance()
// advance ptr to first character
Expand All @@ -103,32 +88,24 @@ func TestTokenizeOperator(t *testing.T) {
if string(compt.CurrentChar) == "EOF" {
break
}
if compt.CurrentToken.Value != op {
t.Error("incorrect token")
}
s.EqualValues(compt.CurrentToken.Value, op)
}
}

func TestLex(t *testing.T) {
func (s *LexerSuite) TestLex() {
conf := &Config{SrcFileName: "test/test_tokenize.doc"}
compt := NewCompterpreter(conf)

err := compt.LoadSourceCode()
if err != nil {
t.Error(err)
}
s.NoError(err)

err = compt.Lex()
if err != nil {
t.Error(err)
}
s.NoError(err)

expectedTokens := []string{
"\n", "123", "†", "3", "*", "2", "‡", "45787894357893", "\n", "0", "+", "00", "+", "1", "\n",
}
for idx, token := range compt.Tokens {
if token.Value != expectedTokens[idx] {
t.Error("Incorrect token! Try harder")
}
s.EqualValues(token.Value, expectedTokens[idx])
}
}
Loading

0 comments on commit d3ae0a2

Please sign in to comment.