Skip to content

Commit

Permalink
Fix semantics of print statement & implement output in vanilla ACT-R
Browse files Browse the repository at this point in the history
The print statement was using plain ids but we weren't determining if they were vars or not. It makes more sense and reads better to require vars with "?".

This also addresses #32 (output) for vanilla ACT-R.
  • Loading branch information
asmaloney committed Aug 20, 2021
1 parent b57315c commit c726ac8
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 59 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ increment {
retrieval `count( ?x ?next )`
}
do {
print x
print ?x
recall `count( ?next ? )`
set start of goal to next
}
Expand All @@ -323,7 +323,7 @@ stop {
goal `countFrom( ?x ?x counting )`
}
do {
print x
print ?x
clear goal
}
}
Expand Down Expand Up @@ -357,11 +357,11 @@ The _do_ section in the productions uses a small language which currently unders
| command | example |
| --------------------------------------------------------------------- | ------------------------------- |
| clear _(buffer name)+_ | clear goal, retrieval |
| print _(string or ident or number)+_ | print foo, 'text', 42 |
| print _(string or var or number)+_ | print 'text', ?var, 42 |
| recall _(pattern)_ | recall \`car( ?colour )\` |
| set _(slot name)_ of _(buffer name)_ to _(string or ident or number)_ | set sum of goal to 6 |
| set _(buffer name)_ to _(pattern)_ | set goal to \`start( 6 None )\` |
| write _(string or ident or number)+_ to _(text output name)_ | write 'foo' to text |
| write _(string or var or number)+_ to _(text output name)_ | write 'text', ?var to text |
## Processing
Expand Down
12 changes: 10 additions & 2 deletions actr/production.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,17 @@ type ClearStatement struct {
BufferNames []string
}

// Value holds something that may be printed.
type Value struct {
Var *string
ID *string
Str *string
Number *float64
}

// PrintStatement outputs the string, id, or number to stdout.
type PrintStatement struct {
Args []string // the strings, identifiers, or numbers to print
Values *[]*Value
}

// RecallStatement is used to pull information from a memory.
Expand All @@ -42,7 +50,7 @@ type RecallStatement struct {

// WriteStatement will send the list of strings, ids, and numbers to the text output.
type WriteStatement struct {
Args []string // the strings, identifiers, or numbers to write
Values *[]*Value
TextOutputName string
}

Expand Down
30 changes: 27 additions & 3 deletions amod/amod.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ func addACTR(model *actr.Model, list *fieldList, errs *errorListWithContext) {
case "log":
if field.Value.Number != nil {
model.Logging = (*field.Value.Number != 0)
} else {
} else if field.Value.ID != nil {
model.Logging = (strings.ToLower(*field.Value.ID) == "true")
} else if field.Value.Str != nil {
model.Logging = (strings.ToLower(*field.Value.Str) == "true")
}
default:
Expand Down Expand Up @@ -476,7 +478,7 @@ func addPrintStatement(model *actr.Model, print *printStatement, production *act

p := actr.PrintStatement{}
if print.Args != nil {
p.Args = print.Args.Strings()
p.Values = convertArgs(print.Args.Values)
}

s := actr.Statement{Print: &p}
Expand All @@ -492,9 +494,31 @@ func addWriteStatement(model *actr.Model, write *writeStatement, production *act

s := actr.Statement{
Write: &actr.WriteStatement{
Args: write.Args.Strings(),
Values: convertArgs(write.Args.Values),
TextOutputName: write.TextOutputName},
}

return &s, nil
}

func convertArgs(values []*value) *[]*actr.Value {
actrValues := []*actr.Value{}

for _, v := range values {
newValue := actr.Value{}

if v.Var != nil {
newValue.Var = v.Var
} else if v.ID != nil {
newValue.ID = v.ID
} else if v.Str != nil {
newValue.Str = v.Str
} else if v.Number != nil {
newValue.Number = v.Number
}

actrValues = append(actrValues, &newValue)
}

return &actrValues
}
33 changes: 31 additions & 2 deletions amod/amod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,11 @@ func TestProductionPrintStatement(t *testing.T) {
==model==
name: Test
==config==
chunks { foo( thing1 thing2 ) }
==productions==
start {
match { memory ` + "`_status( error )`" + ` }
do { print 42 }
match { goal ` + "`foo( ?next ?other )`" + ` }
do { print 42, ?other, "blat" }
}`

_, err := GenerateModel(src)
Expand All @@ -333,6 +334,34 @@ func TestProductionPrintStatement(t *testing.T) {
if err != nil {
t.Errorf("Unexpected error: %s", err)
}

src = `
==model==
name: Test
==config==
==productions==
start {
match { memory ` + "`_status( error )`" + ` }
do { print fooID }
}`

_, err = GenerateModel(src)
expected := "cannot use ID 'fooID' in print statement (line 8)"
checkExpectedError(err, expected, t)

src = `
==model==
name: Test
==config==
==productions==
start {
match { memory ` + "`_status( error )`" + ` }
do { print ?fooVar }
}`

_, err = GenerateModel(src)
expected = "print statement variable '?fooVar' not found in matches for production 'start' (line 8)"
checkExpectedError(err, expected, t)
}

func TestProductionMatchInternal(t *testing.T) {
Expand Down
8 changes: 3 additions & 5 deletions amod/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,7 @@ func lexStart(l *lexer_amod) stateFn {
l.emit(lexemePatternDelim)

case r == '?':
if l.inPattern {
return lexIdentifier
} else {
l.emit(lexemeChar)
}
return lexIdentifier

case r <= unicode.MaxASCII && unicode.IsPrint(r):
l.emit(lexemeChar)
Expand Down Expand Up @@ -450,6 +446,8 @@ func lexIdentifier(l *lexer_amod) stateFn {
isKeyword := l.lookupKeyword(l.input[l.start:l.pos])
if isKeyword {
l.emit(lexemeKeyword)
} else if l.input[l.start] == '?' {
l.emit(lexemePatternVar)
} else {
l.emit(lexemeIdentifier)
}
Expand Down
29 changes: 5 additions & 24 deletions amod/parse.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package amod

import (
"fmt"
"io"
"os"

Expand Down Expand Up @@ -42,38 +41,20 @@ type stringList struct {
}

type value struct {
Str *string `parser:" (@String|@Ident)"`
Number *float64 `parser:"| @Number"`
Var *string `parser:"( @PatternVar"`
ID *string `parser:"| @Ident"`
Str *string `parser:"| @String"`
Number *float64 `parser:"| @Number)"`

Pos lexer.Position
}

func (v *value) String() string {
if v.Str != nil {
return *v.Str
} else if v.Number != nil {
return fmt.Sprintf("%f", *v.Number)
}

return ""
}

type argList struct {
Args []*value `parser:"( @@ ','? )+"`
Values []*value `parser:"( @@ ','? )+"`

Pos lexer.Position
}

// Strings converts an arg list into a string slice
func (a *argList) Strings() []string {
str := make([]string, len(a.Args))
for i, arg := range a.Args {
str[i] = arg.String()
}

return str
}

type field struct {
Key string `parser:"@Ident ':'"`
Value value `parser:"@@ (',')?"`
Expand Down
26 changes: 26 additions & 0 deletions amod/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,19 @@ func validateClearStatement(clear *clearStatement, model *actr.Model, production
func validatePrintStatement(print *printStatement, model *actr.Model, production *actr.Production) (err error) {
errs := errorListWithContext{}

if print.Args != nil {
for _, v := range print.Args.Values {
if v.ID != nil {
errs.Addc(&print.Pos, "cannot use ID '%s' in print statement", *v.ID)
} else if v.Var != nil {
match := production.LookupMatchByVariable(*v.Var)
if match == nil {
errs.Addc(&print.Pos, "print statement variable '%s' not found in matches for production '%s'", *v.Var, production.Name)
}
}
}
}

return errs.ErrorOrNil()
}

Expand All @@ -210,5 +223,18 @@ func validateWriteStatement(write *writeStatement, model *actr.Model, production
errs.Addc(&write.Pos, "text output '%s' not found in production '%s'", name, production.Name)
}

if write.Args != nil {
for _, v := range write.Args.Values {
if v.ID != nil {
errs.Addc(&write.Pos, "cannot use ID '%s' in write statement", *v.ID)
} else if v.Var != nil {
match := production.LookupMatchByVariable(*v.Var)
if match == nil {
errs.Addc(&write.Pos, "write statement variable '%s' not found in matches for production '%s'", *v.Var, production.Name)
}
}
}
}

return errs.ErrorOrNil()
}
2 changes: 1 addition & 1 deletion examples/addition.amod
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ terminateAddition {
goal `add( ?num1 ?num2 ?num2 ?sum )`
}
do {
print sum
print ?sum
clear goal
}
}
Expand Down
18 changes: 9 additions & 9 deletions examples/addition2.amod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ actr { log: true }

// Declare chunks and their layouts
chunks {
add( slot_1 slot_2 slot_3 slot_4 slot_5 slot_6 slot_7 )
add( slot_1 slot_2 slot_3 slot_4 slot_5 answer remainder )
addFact( first second sum )
}

Expand All @@ -42,7 +42,7 @@ startPair {
}
// Steps to execute
do {
set slot_6 of goal to 'busy'
set answer of goal to 'busy'
recall `addFact( ?one1 ?one2 ? )`
}
}
Expand All @@ -53,8 +53,8 @@ addOnes {
retrieval `addFact( ? ? ?sum )`
}
do {
set slot_6 of goal to sum
set slot_7 of goal to 'busy'
set answer of goal to sum
set remainder of goal to 'busy'
recall `addFact( 10 ? ?sum )`
}
}
Expand All @@ -66,8 +66,8 @@ processCarry {
}
do {
set slot_5 of goal to 'busy'
set slot_6 of goal to rem
set slot_7 of goal to 1
set answer of goal to rem
set remainder of goal to 1
recall `addFact( ?ten1 ?ten2 ? )`
}
}
Expand All @@ -79,7 +79,7 @@ noCarry {
}
do {
set slot_4 of goal to 'busy'
set slot_6 of goal to 0
set answer of goal to 0
recall `addFact( ?ten1 ?ten2 ? )`
}
}
Expand All @@ -90,7 +90,7 @@ addTensDone {
retrieval `addFact( ? ? ?sum )`
}
do {
print sum, oneAns
print ?sum, ?oneAns
set slot_5 of goal to sum
}
}
Expand All @@ -101,7 +101,7 @@ addTensCarry {
retrieval `addFact( ? ? ?sum )`
}
do {
set slot_7 of goal to 0
set remainder of goal to 0
recall `addFact( 1 ?sum ? )`
}
}
4 changes: 2 additions & 2 deletions examples/count.amod
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ increment {
retrieval `count( ?x ?next )`
}
do {
print x
print ?x
recall `count( ?next ? )`
set start of goal to next
}
Expand All @@ -71,7 +71,7 @@ stop {
goal `countFrom( ?x ?x counting )`
}
do {
print x
print ?x
clear goal
}
}
Loading

0 comments on commit c726ac8

Please sign in to comment.