Skip to content

Commit 724cbbc

Browse files
committed
{pyactr} Handle printing of numbers and variables
Addresses more of #32.
1 parent 080f1df commit 724cbbc

File tree

1 file changed

+94
-37
lines changed

1 file changed

+94
-37
lines changed

framework/pyactr/pyactr.go

+94-37
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ func (p *PyACTR) WriteModel(path, initialGoal string) (outputFileName string, er
116116

117117
p.outputAuthors()
118118

119+
if p.model.HasPrintStatement() {
120+
// We use csv to parse the print text we are generating.
121+
// This is just simpler than writing it ourselves (i.e. handling "foo, bar ", 66).
122+
p.Writeln("import csv")
123+
}
124+
119125
p.Writeln("import pyactr as actr")
120126

121127
if p.model.HasPrintStatement() {
@@ -137,20 +143,6 @@ func (p *PyACTR) WriteModel(path, initialGoal string) (outputFileName string, er
137143

138144
p.Write("%s = actr.ACTRModel(%s)\n\n", p.className, strings.Join(additionalInit, ", "))
139145

140-
if p.model.HasPrintStatement() {
141-
p.Writeln(`
142-
# Monkey patch Buffer to add a new method.
143-
# Currently we can only output simple strings.
144-
def print_text(*args):
145-
text = ''.join(args[1:])
146-
text = text.strip("'")
147-
print(text)
148-
149-
150-
Buffer.print_text = print_text
151-
`)
152-
}
153-
154146
// chunks
155147
for _, chunk := range p.model.Chunks {
156148
if chunk.IsInternal() {
@@ -218,13 +210,6 @@ Buffer.print_text = print_text
218210
if production.DoStatements != nil {
219211
for _, statement := range production.DoStatements {
220212
if statement.Print != nil {
221-
if !onlyStrings(statement.Print.Values) {
222-
warning := fmt.Sprintf("Warning: ('%s') pyactr currently cannot print variables from productions", production.Name)
223-
warnings = append(warnings, warning)
224-
225-
continue
226-
}
227-
228213
numPrintStatements++
229214
if numPrintStatements > 1 {
230215
warning := fmt.Sprintf("Warning: ('%s') pyactr currently only supports one print statement per production", production.Name)
@@ -233,7 +218,7 @@ Buffer.print_text = print_text
233218
}
234219
}
235220

236-
p.outputStatement(statement)
221+
p.outputStatement(production, statement)
237222
}
238223
}
239224

@@ -250,12 +235,80 @@ Buffer.print_text = print_text
250235

251236
p.Writeln("")
252237

238+
if p.model.HasPrintStatement() {
239+
// We add some python code to monkey patch the model and the buffer.
240+
// This lets us output strings, numbers, and the contents of buffer slots.
241+
242+
p.Writeln(`
243+
# Monkey patch ACTRModel to add a new method.
244+
def get_buffer(self, buffer_name: str) -> Buffer:
245+
if buffer_name == 'goal':
246+
return self._ACTRModel__buffers['goal']
247+
if buffer_name == 'retrieval':
248+
return self._ACTRModel__buffers["retrieval"]
249+
250+
print('ERROR: Buffer \'' + buffer_name + '\' not found')
251+
252+
253+
actr.ACTRModel.get_buffer = get_buffer
254+
255+
256+
# Monkey patch Buffer to add a new methods.
257+
def get_slot_contents(self, buffer_name: str, slot_name: str) -> str:
258+
if self._data:
259+
chunk = self._data.copy().pop()
260+
else:
261+
chunk = None
262+
try:
263+
return str(getattr(chunk, slot_name))
264+
except AttributeError:
265+
print('ERROR: no slot named \'' + slot_name +
266+
'\' in buffer \'' + buffer_name + '\'')
267+
268+
return ''
269+
270+
271+
def print_text(*args):
272+
text = ''.join(args[1:]).strip('"')
273+
output = '' # build up our output in this buffer
274+
275+
for itemlist in csv.reader([text]):
276+
for item in itemlist:
277+
item = item.strip(' ')
278+
279+
# Handle string
280+
if item[0] == '\'' or item[0] == '"':
281+
output += item[1:-1]
282+
else:
283+
# Handle number
284+
try:
285+
float(item)
286+
output += item
287+
except ValueError:
288+
# If we are here, we should have a buffer.slotname
289+
ids = item.split('.')
290+
if len(ids) != 2:
291+
print(
292+
'ERROR: expected <buffer>.<slot_name>, found \'' + item + '\'')
293+
else:
294+
buffer = %s.get_buffer(ids[0])
295+
output += buffer.get_slot_contents(ids[0], ids[1])
296+
297+
print(output)
298+
299+
300+
Buffer.get_slot_contents = get_slot_contents
301+
Buffer.print_text = print_text
302+
`, p.className)
303+
}
304+
253305
// ...add our code to run
306+
p.Writeln("# Main")
254307
p.Writeln("if __name__ == '__main__':")
255308
p.Writeln("\tsim = %s.simulation()", p.className)
256309
p.Writeln("\tsim.run()")
257310
// TODO: Add some intelligent output when logging level is info or detail
258-
p.Writeln("\tif goal.test_buffer('full') == True:")
311+
p.Writeln("\tif goal.test_buffer('full') is True:")
259312
p.Writeln("\t\tprint('final goal: ' + str(goal.pop()))")
260313

261314
return
@@ -335,7 +388,7 @@ func addPatternSlot(tabbedItems *framework.KeyValueList, slotName string, patter
335388
}
336389
}
337390

338-
func (p *PyACTR) outputStatement(s *actr.Statement) {
391+
func (p *PyACTR) outputStatement(production *actr.Production, s *actr.Statement) {
339392
if s.Set != nil {
340393
buffer := s.Set.Buffer
341394
bufferName := buffer.GetName()
@@ -367,27 +420,31 @@ func (p *PyACTR) outputStatement(s *actr.Statement) {
367420
p.Writeln("\t+retrieval>")
368421
p.outputPattern(s.Recall.Pattern, 2)
369422
} else if s.Print != nil {
423+
// Using "goal" here is arbitrary because of the way we monkey patch the python code.
424+
// Our "print_text" statement handles its own formatting and lookup.
370425
p.Writeln("\t!goal>")
371-
values := framework.PythonValuesToStrings(s.Print.Values, true)
372-
p.Writeln("\t\tprint_text %s", strings.Join(values, " "))
426+
427+
str := make([]string, len(*s.Print.Values))
428+
429+
for index, val := range *s.Print.Values {
430+
if val.Var != nil {
431+
varIndex := production.VarIndexMap[*val.Var]
432+
str[index] = fmt.Sprintf("%s.%s", varIndex.Buffer.GetName(), varIndex.SlotName)
433+
} else if val.Str != nil {
434+
str[index] = fmt.Sprintf("'%s'", *val.Str)
435+
} else if val.Number != nil {
436+
str[index] = *val.Number
437+
}
438+
}
439+
440+
p.Writeln("\t\tprint_text \"%s\"", strings.Join(str, ", "))
373441
} else if s.Clear != nil {
374442
for _, name := range s.Clear.BufferNames {
375443
p.Writeln("\t~%s>", name)
376444
}
377445
}
378446
}
379447

380-
func onlyStrings(values *[]*actr.Value) bool {
381-
for _, v := range *values {
382-
if v.Var != nil {
383-
return false
384-
}
385-
// v.ID should not be possible because of validation
386-
}
387-
388-
return true
389-
}
390-
391448
// removeWarning will remove the long warning whenever pyactr is run without tkinter.
392449
func removeWarning(text []byte) []byte {
393450
str := string(text)

0 commit comments

Comments
 (0)