From a0401762f45d4788a1e36d59c59b8f2e7a3919ce Mon Sep 17 00:00:00 2001 From: Alexander K Date: Tue, 25 Jun 2019 15:02:02 +0300 Subject: [PATCH 1/3] Fixed isDirty problem --- evaluator/smalltalkEvaluator.go | 48 +++++++++++++++++-------- evaluator/smalltalkEvaluator_test.go | 33 +++++++++++++++++ treeNodes/nodes.go | 53 ++++++++++++++++++++++++++++ treeNodes/nodesEvaluation.go | 10 ------ 4 files changed, 120 insertions(+), 24 deletions(-) diff --git a/evaluator/smalltalkEvaluator.go b/evaluator/smalltalkEvaluator.go index 2356fa6..c8e8b81 100644 --- a/evaluator/smalltalkEvaluator.go +++ b/evaluator/smalltalkEvaluator.go @@ -3,6 +3,7 @@ package evaluator import ( "github.com/SealNTibbers/GotalkInterpreter/parser" "github.com/SealNTibbers/GotalkInterpreter/treeNodes" + "sort" ) //testing stuff @@ -15,13 +16,13 @@ func NewTestEvaluator() *Evaluator { func TestEval(codeString string) treeNodes.SmalltalkObjectInterface { evaluator := NewTestEvaluator() programNode := parser.InitializeParserFor(codeString) - return evaluator.EvaluateProgramNode(programNode) + return evaluator.EvaluateProgram(&Program{needUpdate: true, programNode: programNode}) } func TestEvalWithScope(codeString string, scope *treeNodes.Scope) treeNodes.SmalltalkObjectInterface { evaluator := NewEvaluatorWithGlobalScope(scope) programNode := parser.InitializeParserFor(codeString) - return evaluator.EvaluateProgramNode(programNode) + return evaluator.EvaluateProgram(&Program{needUpdate: true, programNode: programNode}) } //real world API @@ -39,14 +40,19 @@ func NewSmalltalkWorkspace() *Evaluator { func NewEvaluatorWithGlobalScope(global *treeNodes.Scope) *Evaluator { evaluator := new(Evaluator) - evaluator.programCache = make(map[string]treeNodes.ProgramNodeInterface) + evaluator.programCache = make(map[string]*Program) evaluator.globalScope = global return evaluator } +type Program struct { + needUpdate bool + programNode treeNodes.ProgramNodeInterface +} + type Evaluator struct { globalScope *treeNodes.Scope - programCache map[string]treeNodes.ProgramNodeInterface + programCache map[string]*Program workspaceScope *treeNodes.Scope } @@ -60,15 +66,15 @@ func (e *Evaluator) GetGlobalScope() *treeNodes.Scope { } func (e *Evaluator) RunProgram(programString string) treeNodes.SmalltalkObjectInterface { - programNode, ok := e.programCache[programString] + _, ok := e.programCache[programString] if !ok { - programNode = parser.InitializeParserFor(programString) - e.programCache[programString] = programNode + e.programCache[programString] = &Program{needUpdate: true, programNode: parser.InitializeParserFor(programString)} } - return e.EvaluateProgramNode(programNode) + evaluatorProgram := e.programCache[programString] + return e.EvaluateProgram(evaluatorProgram) } -func (e *Evaluator) EvaluateProgramNode(programNode treeNodes.ProgramNodeInterface) treeNodes.SmalltalkObjectInterface { +func (e *Evaluator) EvaluateProgram(program *Program) treeNodes.SmalltalkObjectInterface { var result treeNodes.SmalltalkObjectInterface var localScope *treeNodes.Scope if e.workspaceScope != nil { @@ -78,12 +84,12 @@ func (e *Evaluator) EvaluateProgramNode(programNode treeNodes.ProgramNodeInterfa } localScope.OuterScope = e.globalScope - if e.globalScope.IsDirty() || programNode.GetLastValue() == nil { - result = programNode.Eval(localScope) - programNode.SetLastValue(result) - e.globalScope.Clean() + if program.needUpdate || program.programNode.GetLastValue() == nil { + result = program.programNode.Eval(localScope) + program.programNode.SetLastValue(result) + program.needUpdate = false } else { - result = programNode.GetLastValue() + result = program.programNode.GetLastValue() } return result @@ -124,23 +130,37 @@ func (e *Evaluator) EvaluateToInterface(programString string) interface{} { } } +func (e *Evaluator) updateCacheIfNeededForVariable(variableName string){ + for _, evaluatorProgram := range e.programCache { + program := evaluatorProgram.programNode + if sort.SearchStrings(program.GetVariables(), variableName) < len(program.GetVariables()) { + evaluatorProgram.needUpdate = true + } + } +} + //scope-related delegations func (e *Evaluator) SetVar(name string, value treeNodes.SmalltalkObjectInterface) treeNodes.SmalltalkObjectInterface { + e.updateCacheIfNeededForVariable(name) return e.globalScope.SetVar(name, value) } func (e *Evaluator) SetStringVar(name string, value string) treeNodes.SmalltalkObjectInterface { + e.updateCacheIfNeededForVariable(name) return e.globalScope.SetStringVar(name, value) } func (e *Evaluator) SetNumberVar(name string, value float64) treeNodes.SmalltalkObjectInterface { + e.updateCacheIfNeededForVariable(name) return e.globalScope.SetNumberVar(name, value) } func (e *Evaluator) SetBoolVar(name string, value bool) treeNodes.SmalltalkObjectInterface { + e.updateCacheIfNeededForVariable(name) return e.globalScope.SetBoolVar(name, value) } func (e *Evaluator) FindValueByName(name string) (treeNodes.SmalltalkObjectInterface, bool) { + e.updateCacheIfNeededForVariable(name) return e.globalScope.FindValueByName(name) } diff --git a/evaluator/smalltalkEvaluator_test.go b/evaluator/smalltalkEvaluator_test.go index f4aec43..2728027 100644 --- a/evaluator/smalltalkEvaluator_test.go +++ b/evaluator/smalltalkEvaluator_test.go @@ -71,6 +71,39 @@ func TestGlobalScopeEvaluation(t *testing.T) { testutils.ASSERT_FLOAT64_EQ(t, resultObject.(*treeNodes.SmalltalkNumber).GetValue(), 25) } +func TestGlobalScopeWithChangingEvaluation(t *testing.T) { + var inputString string + var resultObject treeNodes.SmalltalkObjectInterface + + globalScope := new(treeNodes.Scope).Initialize() + globalScope.SetVar("x", treeNodes.NewSmalltalkNumber(25)) + + evaluator := NewEvaluatorWithGlobalScope(globalScope) + + inputString = `x+5` + resultObject = evaluator.RunProgram(inputString) + testutils.ASSERT_TRUE(t, resultObject.TypeOf() == treeNodes.NUMBER_OBJ) + testutils.ASSERT_FLOAT64_EQ(t, resultObject.(*treeNodes.SmalltalkNumber).GetValue(), 30) + + inputString = `x+10` + resultObject = evaluator.RunProgram(inputString) + testutils.ASSERT_TRUE(t, resultObject.TypeOf() == treeNodes.NUMBER_OBJ) + testutils.ASSERT_FLOAT64_EQ(t, resultObject.(*treeNodes.SmalltalkNumber).GetValue(), 35) + + evaluator.SetVar("x", treeNodes.NewSmalltalkNumber(5)) + + inputString = `x+5` + resultObject = evaluator.RunProgram(inputString) + testutils.ASSERT_TRUE(t, resultObject.TypeOf() == treeNodes.NUMBER_OBJ) + testutils.ASSERT_FLOAT64_EQ(t, resultObject.(*treeNodes.SmalltalkNumber).GetValue(), 10) + + // This test should be fail, because we have logic problem with isDirty + inputString = `x+10` + resultObject = evaluator.RunProgram(inputString) + testutils.ASSERT_TRUE(t, resultObject.TypeOf() == treeNodes.NUMBER_OBJ) + testutils.ASSERT_FLOAT64_EQ(t, resultObject.(*treeNodes.SmalltalkNumber).GetValue(), 15) +} + func TestLocalScopeEvaluation(t *testing.T) { var inputString1, inputString2 string var result1, result2 int64 diff --git a/treeNodes/nodes.go b/treeNodes/nodes.go index f2a4031..6c41d68 100644 --- a/treeNodes/nodes.go +++ b/treeNodes/nodes.go @@ -2,6 +2,7 @@ package treeNodes import ( "github.com/SealNTibbers/GotalkInterpreter/scanner" + "sort" ) type ProgramNodeInterface interface { @@ -16,6 +17,7 @@ type ProgramNodeInterface interface { Eval(scope *Scope) SmalltalkObjectInterface GetLastValue() SmalltalkObjectInterface SetLastValue(SmalltalkObjectInterface) + GetVariables() []string } type Node struct { @@ -111,6 +113,15 @@ func (m *SequenceNode) SetRightBar(rightBar int64) { m.rightBar = rightBar } +func (m *SequenceNode) GetVariables() []string { + result := [] string {} + for _, statement := range m.statements { + result = append(result, statement.GetVariables()...) + } + sort.Strings(result) + return result +} + type ValueNodeInterface interface { ProgramNodeInterface AddParenthesis(interval Interval) @@ -159,6 +170,10 @@ func (a *AssignmentNode) IsAssignment() bool { return true } +func (a *AssignmentNode) GetVariables() []string { + return nil +} + type LiteralNodeInterface interface { ValueNodeInterface LiteralToken(token scanner.LiteralTokenInterface) LiteralNodeInterface @@ -227,6 +242,10 @@ func (l *LiteralArrayNode) GetValue() string { return value } +func (m *LiteralArrayNode) GetVariables() []string { + return nil +} + type LiteralValueNode struct { *LiteralNode token scanner.LiteralTokenInterface @@ -240,6 +259,10 @@ func (literalValue *LiteralValueNode) GetValue() string { return literalValue.token.ValueOfToken() } +func (l *LiteralValueNode) GetVariables() []string { + return nil +} + type VariableNode struct { *ValueNode Token scanner.ValueTokenInterface @@ -249,6 +272,11 @@ func (v *VariableNode) GetName() string { return v.Token.ValueOfToken() } +func (m *VariableNode) GetVariables() []string { + result := [] string {} + return append(result,m.Token.ValueOfToken()) +} + type NodeWithRreceiverInterface interface { GetReceiver() ValueNodeInterface } @@ -303,6 +331,15 @@ func (m *MessageNode) SetArguments(arguments []ValueNodeInterface) { } } +func (m *MessageNode) GetVariables() []string { + variables := m.receiver.GetVariables() + for _, arg := range m.arguments { + variables = append(variables, arg.GetVariables()...) + } + sort.Strings(variables) + return variables +} + type CascadeNode struct { *ValueNode semicolons []int64 @@ -324,6 +361,10 @@ func (m *CascadeNode) SetMessages(messages []*MessageNode) { } } +func (m *CascadeNode) GetVariables() []string { + return nil +} + type BlockNode struct { *ValueNode bar int64 @@ -365,6 +406,18 @@ func (m *BlockNode) SetRight(right int64) { m.right = right } +func (m *BlockNode) GetVariables() []string { + result := m.body.GetVariables() + sort.Strings(result) + for _, arg := range m.arguments { + localVariable := arg.GetVariables()[0] + index := sort.SearchStrings(result,localVariable) + result = append(result[:index], result[index+1:]...) + } + sort.Strings(result) + return result +} + type Interval struct { start int64 stop int64 diff --git a/treeNodes/nodesEvaluation.go b/treeNodes/nodesEvaluation.go index 8d16a1e..ff4f4b5 100644 --- a/treeNodes/nodesEvaluation.go +++ b/treeNodes/nodesEvaluation.go @@ -9,15 +9,6 @@ import ( type Scope struct { variables map[string]SmalltalkObjectInterface OuterScope *Scope - isDirty bool -} - -func (s *Scope) IsDirty() bool { - return s.isDirty -} - -func (s *Scope) Clean() { - s.isDirty = false } func (s *Scope) Initialize() *Scope { @@ -26,7 +17,6 @@ func (s *Scope) Initialize() *Scope { } func (s *Scope) SetVar(name string, value SmalltalkObjectInterface) SmalltalkObjectInterface { - s.isDirty = true s.variables[name] = value return value } From 844327d8769f731264d49bbc6f5ce356ac6105db Mon Sep 17 00:00:00 2001 From: Alexander K Date: Thu, 19 Sep 2019 15:47:17 +0300 Subject: [PATCH 2/3] Fixed naming --- evaluator/smalltalkEvaluator.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/evaluator/smalltalkEvaluator.go b/evaluator/smalltalkEvaluator.go index c8e8b81..e226db3 100644 --- a/evaluator/smalltalkEvaluator.go +++ b/evaluator/smalltalkEvaluator.go @@ -130,7 +130,7 @@ func (e *Evaluator) EvaluateToInterface(programString string) interface{} { } } -func (e *Evaluator) updateCacheIfNeededForVariable(variableName string){ +func (e *Evaluator) updateCache(variableName string){ for _, evaluatorProgram := range e.programCache { program := evaluatorProgram.programNode if sort.SearchStrings(program.GetVariables(), variableName) < len(program.GetVariables()) { @@ -141,26 +141,26 @@ func (e *Evaluator) updateCacheIfNeededForVariable(variableName string){ //scope-related delegations func (e *Evaluator) SetVar(name string, value treeNodes.SmalltalkObjectInterface) treeNodes.SmalltalkObjectInterface { - e.updateCacheIfNeededForVariable(name) + e.updateCache(name) return e.globalScope.SetVar(name, value) } func (e *Evaluator) SetStringVar(name string, value string) treeNodes.SmalltalkObjectInterface { - e.updateCacheIfNeededForVariable(name) + e.updateCache(name) return e.globalScope.SetStringVar(name, value) } func (e *Evaluator) SetNumberVar(name string, value float64) treeNodes.SmalltalkObjectInterface { - e.updateCacheIfNeededForVariable(name) + e.updateCache(name) return e.globalScope.SetNumberVar(name, value) } func (e *Evaluator) SetBoolVar(name string, value bool) treeNodes.SmalltalkObjectInterface { - e.updateCacheIfNeededForVariable(name) + e.updateCache(name) return e.globalScope.SetBoolVar(name, value) } func (e *Evaluator) FindValueByName(name string) (treeNodes.SmalltalkObjectInterface, bool) { - e.updateCacheIfNeededForVariable(name) + e.updateCache(name) return e.globalScope.FindValueByName(name) } From 88d2697c0f90a5c37331cb0d221b7244fd39eb81 Mon Sep 17 00:00:00 2001 From: micael Date: Fri, 18 Oct 2019 15:13:14 +0300 Subject: [PATCH 3/3] Removed unnecessary Program struct and simplified some of the code. Now we just just dropping cache to nil if it "needs update" so evaluator naturally recalculates result --- evaluator/smalltalkEvaluator.go | 40 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/evaluator/smalltalkEvaluator.go b/evaluator/smalltalkEvaluator.go index e226db3..10b9c1e 100644 --- a/evaluator/smalltalkEvaluator.go +++ b/evaluator/smalltalkEvaluator.go @@ -3,7 +3,6 @@ package evaluator import ( "github.com/SealNTibbers/GotalkInterpreter/parser" "github.com/SealNTibbers/GotalkInterpreter/treeNodes" - "sort" ) //testing stuff @@ -16,13 +15,13 @@ func NewTestEvaluator() *Evaluator { func TestEval(codeString string) treeNodes.SmalltalkObjectInterface { evaluator := NewTestEvaluator() programNode := parser.InitializeParserFor(codeString) - return evaluator.EvaluateProgram(&Program{needUpdate: true, programNode: programNode}) + return evaluator.EvaluateProgram(programNode) } func TestEvalWithScope(codeString string, scope *treeNodes.Scope) treeNodes.SmalltalkObjectInterface { evaluator := NewEvaluatorWithGlobalScope(scope) programNode := parser.InitializeParserFor(codeString) - return evaluator.EvaluateProgram(&Program{needUpdate: true, programNode: programNode}) + return evaluator.EvaluateProgram(programNode) } //real world API @@ -40,19 +39,14 @@ func NewSmalltalkWorkspace() *Evaluator { func NewEvaluatorWithGlobalScope(global *treeNodes.Scope) *Evaluator { evaluator := new(Evaluator) - evaluator.programCache = make(map[string]*Program) + evaluator.programCache = make(map[string]treeNodes.ProgramNodeInterface) evaluator.globalScope = global return evaluator } -type Program struct { - needUpdate bool - programNode treeNodes.ProgramNodeInterface -} - type Evaluator struct { globalScope *treeNodes.Scope - programCache map[string]*Program + programCache map[string]treeNodes.ProgramNodeInterface workspaceScope *treeNodes.Scope } @@ -68,13 +62,13 @@ func (e *Evaluator) GetGlobalScope() *treeNodes.Scope { func (e *Evaluator) RunProgram(programString string) treeNodes.SmalltalkObjectInterface { _, ok := e.programCache[programString] if !ok { - e.programCache[programString] = &Program{needUpdate: true, programNode: parser.InitializeParserFor(programString)} + e.programCache[programString] = parser.InitializeParserFor(programString) } evaluatorProgram := e.programCache[programString] return e.EvaluateProgram(evaluatorProgram) } -func (e *Evaluator) EvaluateProgram(program *Program) treeNodes.SmalltalkObjectInterface { +func (e *Evaluator) EvaluateProgram(program treeNodes.ProgramNodeInterface) treeNodes.SmalltalkObjectInterface { var result treeNodes.SmalltalkObjectInterface var localScope *treeNodes.Scope if e.workspaceScope != nil { @@ -84,12 +78,11 @@ func (e *Evaluator) EvaluateProgram(program *Program) treeNodes.SmalltalkObjectI } localScope.OuterScope = e.globalScope - if program.needUpdate || program.programNode.GetLastValue() == nil { - result = program.programNode.Eval(localScope) - program.programNode.SetLastValue(result) - program.needUpdate = false + if program.GetLastValue() == nil { + result = program.Eval(localScope) + program.SetLastValue(result) } else { - result = program.programNode.GetLastValue() + result = program.GetLastValue() } return result @@ -130,11 +123,16 @@ func (e *Evaluator) EvaluateToInterface(programString string) interface{} { } } -func (e *Evaluator) updateCache(variableName string){ +func (e *Evaluator) updateCache(variableName string) { for _, evaluatorProgram := range e.programCache { - program := evaluatorProgram.programNode - if sort.SearchStrings(program.GetVariables(), variableName) < len(program.GetVariables()) { - evaluatorProgram.needUpdate = true + needsUpdate := false + for _, variable := range evaluatorProgram.GetVariables() { + if variable == variableName { + needsUpdate = true + } + } + if needsUpdate { + evaluatorProgram.SetLastValue(nil) } } }