Skip to content

Commit

Permalink
Split states into buffer states and module states (#337)
Browse files Browse the repository at this point in the history
Instead of:

	match { retrieval [_status: empty] }

buffer checks are now done like this:

	match { retrieval is empty }

Module state checks are still done using _status chunks and will be changed in a future PR.

Part of #222
  • Loading branch information
asmaloney authored Jul 30, 2023
1 parent 156ab71 commit 6c7bada
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 139 deletions.
34 changes: 10 additions & 24 deletions actr/buffer/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,17 @@
package buffer

import (
"fmt"
"sort"
"strings"

"golang.org/x/exp/slices"
)

// validBufferStates is a list of the valid buffer states to use with the _status chunk
// validStates is a list of valid buffer states to use when matching
// TODO: needs review and correction
// See: https://github.com/asmaloney/gactar/discussions/221
var validBufferStates = map[string]bool{
// buffer
"empty": true,
"full": true,

// state
"busy": true,
"error": true,
var validStates = []string{
"empty",
"full",
}

type Buffer struct {
Expand Down Expand Up @@ -98,19 +91,12 @@ func (b List) Lookup(name string) Interface {
return nil
}

// IsValidBufferState checks if 'state' is a valid buffer state.
func IsValidBufferState(state string) bool {
v, ok := validBufferStates[state]
return v && ok
// IsValidState checks if 'state' is a valid buffer state.
func IsValidState(state string) bool {
return slices.Contains(validStates, state)
}

// ValidBufferStatesStr returns a list of (sorted) valid buffer states. Used for error output.
func ValidBufferStatesStr() string {
keys := make([]string, 0, len(validBufferStates))
for k := range validBufferStates {
keys = append(keys, fmt.Sprintf("'%s'", k))
}

sort.Strings(keys)
return strings.Join(keys, ", ")
// ValidStatesStr returns a list of (sorted) valid buffer states. Used for error output.
func ValidStatesStr() string {
return strings.Join(validStates, ", ")
}
17 changes: 17 additions & 0 deletions actr/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package modules

import (
"strings"

"golang.org/x/exp/maps"
"golang.org/x/exp/slices"

Expand All @@ -11,6 +13,11 @@ import (

const BuiltIn = "built-in"

var validStates = []string{
"busy",
"error",
}

// Module is an ACT-R module
type Module struct {
Name string
Expand Down Expand Up @@ -153,3 +160,13 @@ func FindModule(name string) ModuleInterface {

return nil
}

// IsValidState checks if 'state' is a valid module state.
func IsValidState(state string) bool {
return slices.Contains(validStates, state)
}

// ValidStatesStr returns a list of valid module states. Used for error output.
func ValidStatesStr() string {
return strings.Join(validStates, ", ")
}
7 changes: 5 additions & 2 deletions actr/production.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,11 @@ func (c Constraint) String() string {
}

type Match struct {
Buffer buffer.Interface
Pattern *Pattern
Buffer buffer.Interface

// matches either a pattern or a buffer status
Pattern *Pattern
BufferStatus *string
}

type Statement struct {
Expand Down
20 changes: 16 additions & 4 deletions amod/amod.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,24 @@ func addProductions(model *actr.Model, log *issueLog, productions *productionSec
}

for _, match := range production.Match.Items {
pattern, err := createChunkPattern(model, log, match.Pattern)
// check for buffer status match first
if match.BufferStatus != nil {
name := match.BufferStatus.Name
actrMatch := actr.Match{
Buffer: model.LookupBuffer(name),
BufferStatus: &match.BufferStatus.Status,
}

prod.Matches = append(prod.Matches, &actrMatch)
continue
}

pattern, err := createChunkPattern(model, log, match.Chunk.Pattern)
if err != nil {
continue
}

name := match.Name
name := match.Chunk.Name
actrMatch := actr.Match{
Buffer: model.LookupBuffer(name),
Pattern: pattern,
Expand All @@ -454,8 +466,8 @@ func addProductions(model *actr.Model, log *issueLog, productions *productionSec
}
}

if match.When != nil {
for _, expr := range *match.When.Expressions {
if match.Chunk.When != nil {
for _, expr := range *match.Chunk.When.Expressions {
comparison := actr.Equal

if expr.Comparison.NotEqual != nil {
Expand Down
75 changes: 53 additions & 22 deletions amod/amod_productions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ func Example_productionPrintStatement3() {
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: error] }
match { goal is empty }
do { print }
}`)

Expand All @@ -678,7 +678,7 @@ func Example_productionPrintStatementInvalidID() {
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: error] }
match { retrieval is empty }
do { print fooID }
}`)

Expand All @@ -694,7 +694,7 @@ func Example_productionPrintStatementInvalidNil() {
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: error] }
match { goal is empty }
do { print nil }
}`)

Expand All @@ -710,7 +710,7 @@ func Example_productionPrintStatementInvalidVar() {
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: error] }
match { retrieval is empty }
do { print ?fooVar }
}`)

Expand All @@ -726,105 +726,136 @@ func Example_productionPrintStatementWildcard() {
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: error] }
match { goal is empty }
do { print * }
}`)

// Output:
// ERROR: unexpected token "*" (expected "}") (line 9, col 13)
}

func Example_productionMatchInternal() {
func Example_productionMatchBufferStatus() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: error] }
match { retrieval is empty }
do { print 42 }
}`)

// Output:
}

func Example_productionMatchInternalSlots() {
func Example_productionMatchBufferStatusInvalidBuffer() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: busy error] }
match { foo is empty }
do { print 42 }
}`)

// Output:
// ERROR: invalid chunk - '_status' expects 1 slot (line 8, col 20)
// ERROR: buffer 'foo' not found in production 'start' (line 8, col 10)
}

func Example_productionMatchInternalInvalidStatus1() {
func Example_productionMatchBufferStatusInvalidStatus() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { goal [_status: something] }
match { retrieval is foo }
do { print 42 }
}`)

// Output:
// ERROR: invalid _status 'something' for 'goal' in production 'start' (should be 'busy', 'empty', 'error', 'full') (line 8, col 25)
// ERROR: invalid status 'foo' for buffer 'retrieval' in production 'start' (should be one of: empty, full) (line 8, col 10)
}

func Example_productionMatchInternalInvalidStatus2() {
func Example_productionMatchBufferStatusInvalidString() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: something] }
match { goal is 'empty' }
do { print 42 }
}`)

// Output:
// ERROR: unexpected token "empty" (expected <ident>) (line 8, col 18)
}

func Example_productionMatchBufferStatusInvalidNumber() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { goal is 42 }
do { print 42 }
}`)

// Output:
// ERROR: unexpected token "42" (expected <ident>) (line 8, col 18)
}

func Example_productionMatchModuleStatus() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { retrieval [_status: error] }
do { print 42 }
}`)

// Output:
// ERROR: invalid _status 'something' for 'retrieval' in production 'start' (should be 'busy', 'empty', 'error', 'full') (line 8, col 30)
}

func Example_productionMatchInternalInvalidStatusString() {
func Example_productionMatchModuleStatusInvalidStatus1() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { goal [_status: 'something'] }
match { retrieval [_status: 'foo'] }
do { print 42 }
}`)

// Output:
// ERROR: invalid _status for 'goal' in production 'start' (should be 'busy', 'empty', 'error', 'full') (line 8, col 25)
// ERROR: invalid _status for 'retrieval' in production 'start' (should be one of: busy, error) (line 8, col 30)
}

func Example_productionMatchInternalInvalidStatusNumber() {
func Example_productionMatchModuleStatusInvalidStatus2() {
generateToStdout(`
~~ model ~~
name: Test
~~ config ~~
~~ init ~~
~~ productions ~~
start {
match { goal [_status: 42] }
match { retrieval [ _status: bar ] }
do { print 42 }
}`)

// Output:
// ERROR: invalid _status for 'goal' in production 'start' (should be 'busy', 'empty', 'error', 'full') (line 8, col 25)
// ERROR: invalid _status 'bar' for 'retrieval' in production 'start' (should be one of: busy, error) (line 8, col 31)
}
1 change: 1 addition & 0 deletions amod/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ var keywords []string = []string{
"do",
"examples",
"gactar",
"is",
"match",
"modules",
"name",
Expand Down
16 changes: 15 additions & 1 deletion amod/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,28 @@ type whenClause struct {
Tokens []lexer.Token
}

type matchItem struct {
type matchChunkItem struct {
Name string `parser:"@Ident"`
Pattern *pattern `parser:"@@"`
When *whenClause `parser:"@@?"`

Tokens []lexer.Token
}

type matchBufferStatusItem struct {
Name string `parser:"@Ident"`
Status string `parser:"'is' @Ident"`

Tokens []lexer.Token
}

type matchItem struct {
Chunk *matchChunkItem `parser:"( @@"`
BufferStatus *matchBufferStatusItem `parser:"| @@)"`

Tokens []lexer.Token
}

type match struct {
Items []*matchItem `parser:"'match' '{' @@+ '}'"`

Expand Down
Loading

0 comments on commit 6c7bada

Please sign in to comment.