mirror of
https://github.com/dragonheim/gagent.git
synced 2025-04-25 20:28:58 -07:00
feat: added some more test harness.
This commit is contained in:
parent
7f9a5777bd
commit
bdeaf2ec92
8 changed files with 513 additions and 59 deletions
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -18,9 +17,9 @@ func incorrectArgCountError(i *Interpreter, name string, argv []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* needleInHaystack returns true if the string is in a slice
|
* NeedleInHaystack returns true if the string is in a slice
|
||||||
*/
|
*/
|
||||||
func needleInHaystack(needle string, haystack []string) bool {
|
func NeedleInHaystack(needle string, haystack []string) bool {
|
||||||
for _, haystackMember := range haystack {
|
for _, haystackMember := range haystack {
|
||||||
if haystackMember == needle {
|
if haystackMember == needle {
|
||||||
return true
|
return true
|
||||||
|
@ -29,27 +28,6 @@ func needleInHaystack(needle string, haystack []string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Test_needleInHaystack tests the return value of needleInHaystack
|
|
||||||
*/
|
|
||||||
func Test_needleInHaystack(t *testing.T) {
|
|
||||||
var haystack = []string{"a", "b", "c"}
|
|
||||||
var needle = "a"
|
|
||||||
if !needleInHaystack(needle, haystack) {
|
|
||||||
t.Errorf("%s not in %s", needle, haystack)
|
|
||||||
}
|
|
||||||
|
|
||||||
needle = "j"
|
|
||||||
if needleInHaystack(needle, haystack) {
|
|
||||||
t.Errorf("%s in %s", needle, haystack)
|
|
||||||
}
|
|
||||||
|
|
||||||
needle = "ab"
|
|
||||||
if needleInHaystack(needle, haystack) {
|
|
||||||
t.Errorf("%s in %s", needle, haystack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CommandMath is the math command for TCL
|
* CommandMath is the math command for TCL
|
||||||
*/
|
*/
|
||||||
|
@ -274,7 +252,7 @@ func CommandPuts(i *Interpreter, argv []string, pd interface{}) (string, error)
|
||||||
/*
|
/*
|
||||||
* RegisterCoreCommands is a callable to register TCL commands.
|
* RegisterCoreCommands is a callable to register TCL commands.
|
||||||
*/
|
*/
|
||||||
func (i *Interpreter) RegisterCoreCommands() {
|
func (i *Interpreter) RegisterCoreCommands() error {
|
||||||
name := [...]string{"+", "-", "*", "/", ">", ">=", "<", "<=", "==", "!="}
|
name := [...]string{"+", "-", "*", "/", ">", ">=", "<", "<=", "==", "!="}
|
||||||
for _, n := range name {
|
for _, n := range name {
|
||||||
_ = i.RegisterCommand(n, CommandMath, nil)
|
_ = i.RegisterCommand(n, CommandMath, nil)
|
||||||
|
@ -289,4 +267,6 @@ func (i *Interpreter) RegisterCoreCommands() {
|
||||||
_ = i.RegisterCommand("return", CommandReturn, nil)
|
_ = i.RegisterCommand("return", CommandReturn, nil)
|
||||||
_ = i.RegisterCommand("error", CommandError, nil)
|
_ = i.RegisterCommand("error", CommandError, nil)
|
||||||
_ = i.RegisterCommand("puts", CommandPuts, nil)
|
_ = i.RegisterCommand("puts", CommandPuts, nil)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
300
pkg/picol/commands_test.go
Normal file
300
pkg/picol/commands_test.go
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
package picol_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dragonheim/gagent/pkg/picol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_NeedleInHaystack(t *testing.T) {
|
||||||
|
var haystack = []string{"a", "b", "c"}
|
||||||
|
var needle = "a"
|
||||||
|
if !picol.NeedleInHaystack(needle, haystack) {
|
||||||
|
t.Errorf("%s not in %s", needle, haystack)
|
||||||
|
}
|
||||||
|
|
||||||
|
needle = "j"
|
||||||
|
if picol.NeedleInHaystack(needle, haystack) {
|
||||||
|
t.Errorf("%s in %s", needle, haystack)
|
||||||
|
}
|
||||||
|
|
||||||
|
needle = "ab"
|
||||||
|
if picol.NeedleInHaystack(needle, haystack) {
|
||||||
|
t.Errorf("%s in %s", needle, haystack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandMath(t *testing.T) {
|
||||||
|
// You can add more test cases for various operations and edge cases
|
||||||
|
testCases := []struct {
|
||||||
|
argv []string
|
||||||
|
result string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{[]string{"+", "2", "3"}, "5", nil},
|
||||||
|
{[]string{"-", "8", "3"}, "5", nil},
|
||||||
|
{[]string{"*", "2", "3"}, "6", nil},
|
||||||
|
{[]string{"/", "6", "3"}, "2", nil},
|
||||||
|
{[]string{">", "4", "2"}, "1", nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
for _, tc := range testCases {
|
||||||
|
result, err := picol.CommandMath(i, tc.argv, nil)
|
||||||
|
if result != tc.result || (err != nil && tc.err != nil && err.Error() != tc.err.Error()) {
|
||||||
|
t.Errorf("CommandMath(%v) = (%v, %v); expected (%v, %v)", tc.argv, result, err, tc.result, tc.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandSet(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
argv []string
|
||||||
|
result string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{[]string{"set", "x", "42"}, "42", nil},
|
||||||
|
{[]string{"set", "y", "abc"}, "abc", nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
for _, tc := range testCases {
|
||||||
|
result, err := picol.CommandSet(i, tc.argv, nil)
|
||||||
|
if result != tc.result || (err != nil && tc.err != nil && err.Error() != tc.err.Error()) {
|
||||||
|
t.Errorf("CommandSet(%v) = (%v, %v); expected (%v, %v)", tc.argv, result, err, tc.result, tc.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandUnset(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
argv []string
|
||||||
|
result string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{[]string{"unset", "x"}, "", nil},
|
||||||
|
{[]string{"unset", "y"}, "", nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
i.SetVariable("x", "42")
|
||||||
|
i.SetVariable("y", "abc")
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
result, err := picol.CommandUnset(i, tc.argv, nil)
|
||||||
|
if result != tc.result || (err != nil && tc.err != nil && err.Error() != tc.err.Error()) {
|
||||||
|
t.Errorf("CommandUnset(%v) = (%v, %v); expected (%v, %v)", tc.argv, result, err, tc.result, tc.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandIf(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
argv []string
|
||||||
|
result string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{[]string{"unset", "x"}, "", nil},
|
||||||
|
{[]string{"unset", "y"}, "", nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
i.SetVariable("x", "42")
|
||||||
|
i.SetVariable("y", "abc")
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
result, err := picol.CommandUnset(i, tc.argv, nil)
|
||||||
|
if result != tc.result || (err != nil && tc.err != nil && err.Error() != tc.err.Error()) {
|
||||||
|
t.Errorf("CommandUnset(%v) = (%v, %v); expected (%v, %v)", tc.argv, result, err, tc.result, tc.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandWhile(t *testing.T) {
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
|
||||||
|
// Test simple while loop
|
||||||
|
err := i.RegisterCoreCommands()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
script := `
|
||||||
|
set i 0
|
||||||
|
set result 0
|
||||||
|
while { < $i 5 } {
|
||||||
|
set result [+ $result $i]
|
||||||
|
set i [+ $i 1]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
res, err := i.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during while loop evaluation: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedResult := "10"
|
||||||
|
if res != expectedResult {
|
||||||
|
t.Errorf("Expected %s, got %s", expectedResult, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test nested while loops
|
||||||
|
script = `
|
||||||
|
set i 0
|
||||||
|
set j 0
|
||||||
|
set result 0
|
||||||
|
while { < $i 3 } {
|
||||||
|
set j 0
|
||||||
|
while { < $j 3 } {
|
||||||
|
set result [+ $result $j]
|
||||||
|
set j [+ $j 1]
|
||||||
|
}
|
||||||
|
set i [+ $i 1]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
res, err = i.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during nested while loop evaluation: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedResult = "9"
|
||||||
|
if res != expectedResult {
|
||||||
|
t.Errorf("Expected %s, got %s", expectedResult, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can also add test functions for other commands like CommandProc, CommandReturn, etc.
|
||||||
|
func Test_CommandRetCodes(t *testing.T) {
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
|
||||||
|
err := i.RegisterCoreCommands()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test break
|
||||||
|
script := `
|
||||||
|
while { 1 } {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
`
|
||||||
|
_, err = i.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during break evaluation: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test continue
|
||||||
|
script = `
|
||||||
|
set i 0
|
||||||
|
set result 0
|
||||||
|
while { < $i 5 } {
|
||||||
|
if { == $i 2 } {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
set result [+ $result $i]
|
||||||
|
set i [+ $i 1]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
expectedResult := "7"
|
||||||
|
res, err := i.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during continue evaluation: %s", err)
|
||||||
|
}
|
||||||
|
if res != expectedResult {
|
||||||
|
t.Errorf("Expected %s, got %s", expectedResult, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandProc(t *testing.T) {
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
|
||||||
|
err := i.RegisterCoreCommands()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
script := `
|
||||||
|
proc sum {a b} {
|
||||||
|
return [+ $a $b]
|
||||||
|
}
|
||||||
|
set res [sum 3 4]
|
||||||
|
`
|
||||||
|
expectedResult := "7"
|
||||||
|
res, err := i.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during proc evaluation: %s", err)
|
||||||
|
}
|
||||||
|
if res != expectedResult {
|
||||||
|
t.Errorf("Expected %s, got %s", expectedResult, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandReturn(t *testing.T) {
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
|
||||||
|
err := i.RegisterCoreCommands()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
script := `
|
||||||
|
proc testReturn {val} {
|
||||||
|
return $val
|
||||||
|
}
|
||||||
|
set res [testReturn 42]
|
||||||
|
`
|
||||||
|
expectedResult := "42"
|
||||||
|
res, err := i.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during return evaluation: %s", err)
|
||||||
|
}
|
||||||
|
if res != expectedResult {
|
||||||
|
t.Errorf("Expected %s, got %s", expectedResult, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandError(t *testing.T) {
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
|
||||||
|
err := i.RegisterCoreCommands()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
script := `
|
||||||
|
error "An error occurred"
|
||||||
|
`
|
||||||
|
_, err = i.Eval(script)
|
||||||
|
if err == nil || err.Error() != "An error occurred" {
|
||||||
|
t.Fatalf("Error not raised or incorrect error message: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CommandPuts(t *testing.T) {
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
|
||||||
|
err := i.RegisterCoreCommands()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following test checks if the "puts" command runs without any error.
|
||||||
|
// However, it doesn't check the printed output since it's not straightforward to capture stdout in tests.
|
||||||
|
script := `
|
||||||
|
puts "Hello, world!"
|
||||||
|
`
|
||||||
|
_, err = i.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during puts evaluation: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RegisterCoreCommands(t *testing.T) {
|
||||||
|
i := picol.NewInterpreter()
|
||||||
|
|
||||||
|
err := i.RegisterCoreCommands()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error during core command registration: %s", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,13 +7,13 @@ import (
|
||||||
|
|
||||||
// Define parser token types
|
// Define parser token types
|
||||||
const (
|
const (
|
||||||
ptESC = iota
|
ParserTokenESC = iota
|
||||||
ptSTR
|
ParserTokenSTR
|
||||||
ptCMD
|
ParserTokenCMD
|
||||||
ptVAR
|
ParserTokenVAR
|
||||||
ptSEP
|
ParserTokenSEP
|
||||||
ptEOL
|
ParserTokenEOL
|
||||||
ptEOF
|
ParserTokenEOF
|
||||||
)
|
)
|
||||||
|
|
||||||
// parserStruct represents the parser state
|
// parserStruct represents the parser state
|
||||||
|
@ -26,7 +26,7 @@ type parserStruct struct {
|
||||||
|
|
||||||
// initParser initializes a new parserStruct instance
|
// initParser initializes a new parserStruct instance
|
||||||
func initParser(text string) *parserStruct {
|
func initParser(text string) *parserStruct {
|
||||||
return &parserStruct{text: text, ln: len(text), Type: ptEOL}
|
return &parserStruct{text: text, ln: len(text), Type: ParserTokenEOL}
|
||||||
}
|
}
|
||||||
|
|
||||||
// next advances the parser position by one rune
|
// next advances the parser position by one rune
|
||||||
|
@ -57,7 +57,7 @@ func (p *parserStruct) parseSep() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.end = p.p
|
p.end = p.p
|
||||||
p.Type = ptSEP
|
p.Type = ParserTokenSEP
|
||||||
return p.token()
|
return p.token()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ func (p *parserStruct) parseEol() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
p.end = p.p
|
p.end = p.p
|
||||||
p.Type = ptEOL
|
p.Type = ParserTokenEOL
|
||||||
return p.token()
|
return p.token()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ Loop:
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
p.end = p.p
|
p.end = p.p
|
||||||
p.Type = ptCMD
|
p.Type = ParserTokenCMD
|
||||||
if p.p < len(p.text) && p.current() == ']' {
|
if p.p < len(p.text) && p.current() == ']' {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ func (p *parserStruct) parseVar() string {
|
||||||
p.start = p.p
|
p.start = p.p
|
||||||
|
|
||||||
if p.current() == '{' {
|
if p.current() == '{' {
|
||||||
p.Type = ptVAR
|
p.Type = ParserTokenVAR
|
||||||
return p.parseBrace()
|
return p.parseBrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,10 +134,10 @@ func (p *parserStruct) parseVar() string {
|
||||||
if p.start == p.p { // It's just a single char string "$"
|
if p.start == p.p { // It's just a single char string "$"
|
||||||
p.start = p.p - 1
|
p.start = p.p - 1
|
||||||
p.end = p.p
|
p.end = p.p
|
||||||
p.Type = ptSTR
|
p.Type = ParserTokenSTR
|
||||||
} else {
|
} else {
|
||||||
p.end = p.p
|
p.end = p.p
|
||||||
p.Type = ptVAR
|
p.Type = ParserTokenVAR
|
||||||
}
|
}
|
||||||
return p.token()
|
return p.token()
|
||||||
}
|
}
|
||||||
|
@ -173,10 +173,10 @@ Loop:
|
||||||
|
|
||||||
// parseString parses a string with or without quotes
|
// parseString parses a string with or without quotes
|
||||||
func (p *parserStruct) parseString() string {
|
func (p *parserStruct) parseString() string {
|
||||||
newword := p.Type == ptSEP || p.Type == ptEOL || p.Type == ptSTR
|
newword := p.Type == ParserTokenSEP || p.Type == ParserTokenEOL || p.Type == ParserTokenSTR
|
||||||
|
|
||||||
if c := p.current(); newword && c == '{' {
|
if c := p.current(); newword && c == '{' {
|
||||||
p.Type = ptSTR
|
p.Type = ParserTokenSTR
|
||||||
return p.parseBrace()
|
return p.parseBrace()
|
||||||
} else if newword && c == '"' {
|
} else if newword && c == '"' {
|
||||||
p.insidequote = 1
|
p.insidequote = 1
|
||||||
|
@ -197,7 +197,7 @@ Loop:
|
||||||
case '"':
|
case '"':
|
||||||
if p.insidequote != 0 {
|
if p.insidequote != 0 {
|
||||||
p.end = p.p
|
p.end = p.p
|
||||||
p.Type = ptESC
|
p.Type = ParserTokenESC
|
||||||
p.next()
|
p.next()
|
||||||
p.insidequote = 0
|
p.insidequote = 0
|
||||||
return p.token()
|
return p.token()
|
||||||
|
@ -211,7 +211,7 @@ Loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
p.end = p.p
|
p.end = p.p
|
||||||
p.Type = ptESC
|
p.Type = ParserTokenESC
|
||||||
return p.token()
|
return p.token()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,10 +227,10 @@ func (p *parserStruct) parseComment() string {
|
||||||
func (p *parserStruct) GetToken() string {
|
func (p *parserStruct) GetToken() string {
|
||||||
for {
|
for {
|
||||||
if p.ln == 0 {
|
if p.ln == 0 {
|
||||||
if p.Type != ptEOL && p.Type != ptEOF {
|
if p.Type != ParserTokenEOL && p.Type != ParserTokenEOF {
|
||||||
p.Type = ptEOL
|
p.Type = ParserTokenEOL
|
||||||
} else {
|
} else {
|
||||||
p.Type = ptEOF
|
p.Type = ParserTokenEOF
|
||||||
}
|
}
|
||||||
return p.token()
|
return p.token()
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ func (p *parserStruct) GetToken() string {
|
||||||
case '$':
|
case '$':
|
||||||
return p.parseVar()
|
return p.parseVar()
|
||||||
case '#':
|
case '#':
|
||||||
if p.Type == ptEOL {
|
if p.Type == ParserTokenEOL {
|
||||||
p.parseComment()
|
p.parseComment()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
79
pkg/picol/parser_test.go
Normal file
79
pkg/picol/parser_test.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package picol_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dragonheim/gagent/pkg/picol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParser(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Simple test",
|
||||||
|
"set x 10\nincr x",
|
||||||
|
[]int{
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenEOL,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenEOL,
|
||||||
|
picol.ParserTokenEOF,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Variable and command test",
|
||||||
|
"set x $y\nputs [expr $x * 2]",
|
||||||
|
[]int{
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenVAR,
|
||||||
|
picol.ParserTokenEOL,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenCMD,
|
||||||
|
picol.ParserTokenEOL,
|
||||||
|
picol.ParserTokenEOF,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Braces and quotes test",
|
||||||
|
`set x {"Hello World"}`,
|
||||||
|
[]int{
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenSEP,
|
||||||
|
picol.ParserTokenSTR,
|
||||||
|
picol.ParserTokenEOL,
|
||||||
|
picol.ParserTokenEOF,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
parser := picol.InitParser(tc.input)
|
||||||
|
|
||||||
|
for _, expectedType := range tc.expected {
|
||||||
|
token := parser.GetToken()
|
||||||
|
if parser.Type != expectedType {
|
||||||
|
t.Errorf("Expected token type %d, got %d", expectedType, parser.Type)
|
||||||
|
}
|
||||||
|
if parser.Type == picol.ParserTokenEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -125,33 +125,33 @@ func (interp *Interpreter) Eval(script string) (string, error) {
|
||||||
for {
|
for {
|
||||||
prevType := parser.Type
|
prevType := parser.Type
|
||||||
token := parser.GetToken()
|
token := parser.GetToken()
|
||||||
if parser.Type == ptEOF {
|
if parser.Type == ParserTokenEOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
switch parser.Type {
|
switch parser.Type {
|
||||||
case ptVAR:
|
case ParserTokenVAR:
|
||||||
v, ok := interp.Variable(token)
|
v, ok := interp.Variable(token)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("no such variable '%s'", token)
|
return "", fmt.Errorf("no such variable '%s'", token)
|
||||||
}
|
}
|
||||||
token = string(v)
|
token = string(v)
|
||||||
case ptCMD:
|
case ParserTokenCMD:
|
||||||
result, err = interp.Eval(token)
|
result, err = interp.Eval(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
token = result
|
token = result
|
||||||
case ptESC:
|
case ParserTokenESC:
|
||||||
/*
|
/*
|
||||||
* TODO: escape handling missing!
|
* TODO: escape handling missing!
|
||||||
*/
|
*/
|
||||||
case ptSEP:
|
case ParserTokenSEP:
|
||||||
// prevType = parser.Type
|
// prevType = parser.Type
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if parser.Type == ptEOL {
|
if parser.Type == ParserTokenEOL {
|
||||||
// prevType = parser.Type
|
// prevType = parser.Type
|
||||||
if len(argv) != 0 {
|
if len(argv) != 0 {
|
||||||
cmd := interp.Command(argv[0])
|
cmd := interp.Command(argv[0])
|
||||||
|
@ -173,7 +173,7 @@ func (interp *Interpreter) Eval(script string) (string, error) {
|
||||||
/*
|
/*
|
||||||
* We have a new token, append to the previous or as new arg?
|
* We have a new token, append to the previous or as new arg?
|
||||||
*/
|
*/
|
||||||
if prevType == ptSEP || prevType == ptEOL {
|
if prevType == ParserTokenSEP || prevType == ParserTokenEOL {
|
||||||
argv = append(argv, token)
|
argv = append(argv, token)
|
||||||
} else { // Interpolation
|
} else { // Interpolation
|
||||||
argv[len(argv)-1] = strings.Join([]string{argv[len(argv)-1], token}, "")
|
argv[len(argv)-1] = strings.Join([]string{argv[len(argv)-1], token}, "")
|
||||||
|
|
56
pkg/picol/picol_test.go
Normal file
56
pkg/picol/picol_test.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package picol_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dragonheim/gagent/pkg/picol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInterpreter(t *testing.T) {
|
||||||
|
interp := picol.NewInterpreter()
|
||||||
|
|
||||||
|
// Register a command
|
||||||
|
err := interp.RegisterCommand("test", testCommand, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error registering test command: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test command execution
|
||||||
|
script := "test hello world"
|
||||||
|
result, err := interp.Eval(script)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error executing script: %v", err)
|
||||||
|
}
|
||||||
|
expected := "hello world"
|
||||||
|
if result != expected {
|
||||||
|
t.Errorf("Expected result '%s', got '%s'", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test variable setting
|
||||||
|
interp.SetVariable("x", "42")
|
||||||
|
|
||||||
|
// Test variable retrieval
|
||||||
|
val, ok := interp.Variable("x")
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Variable 'x' not found")
|
||||||
|
}
|
||||||
|
expectedVar := "42"
|
||||||
|
if val != picol.Variable(expectedVar) {
|
||||||
|
t.Errorf("Expected variable value '%s', got '%s'", expectedVar, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test variable unsetting
|
||||||
|
interp.UnsetVariable("x")
|
||||||
|
_, ok = interp.Variable("x")
|
||||||
|
if ok {
|
||||||
|
t.Fatalf("Variable 'x' should have been unset")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testCommand is a simple custom command for testing
|
||||||
|
func testCommand(interp *picol.Interpreter, argv []string, privdata interface{}) (string, error) {
|
||||||
|
if len(argv) != 3 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return argv[1] + " " + argv[2], nil
|
||||||
|
}
|
|
@ -12,16 +12,15 @@ import (
|
||||||
|
|
||||||
var fname = flag.String("f", "", "file name")
|
var fname = flag.String("f", "", "file name")
|
||||||
|
|
||||||
func main() {
|
func RunPicol(fname string) error {
|
||||||
flag.Parse()
|
|
||||||
interp := picol.NewInterpreter()
|
interp := picol.NewInterpreter()
|
||||||
interp.RegisterCoreCommands()
|
interp.RegisterCoreCommands()
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(*fname)
|
buf, err := ioutil.ReadFile(fname)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
result, err := interp.Eval(string(buf))
|
result, err := interp.Eval(string(buf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("ERRROR", result, err)
|
return fmt.Errorf("Error: %s, Result: %s", err, result)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for {
|
for {
|
||||||
|
@ -30,8 +29,17 @@ func main() {
|
||||||
clibuf, _ := scanner.ReadString('\n')
|
clibuf, _ := scanner.ReadString('\n')
|
||||||
result, err := interp.Eval(clibuf[:len(clibuf)-1])
|
result, err := interp.Eval(clibuf[:len(clibuf)-1])
|
||||||
if len(result) != 0 {
|
if len(result) != 0 {
|
||||||
fmt.Println("ERRROR", result, err)
|
return fmt.Errorf("Error: %s, Result: %s", err, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
err := RunPicol(*fname)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
31
pkg/picol/picol_unused/main_test.go_unused
Normal file
31
pkg/picol/picol_unused/main_test.go_unused
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package main_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
picol "github.com/dragonheim/gagent/pkg/picol/picol_unused"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_RunPicol(t *testing.T) {
|
||||||
|
// Create a temporary test file
|
||||||
|
content := []byte("set a 5\nset b 7\n+ $a $b\n")
|
||||||
|
tmpfile, err := ioutil.TempFile("", "picol_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temporary test file: %v", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tmpfile.Name()) // clean up
|
||||||
|
|
||||||
|
if _, err := tmpfile.Write(content); err != nil {
|
||||||
|
t.Fatalf("Error writing content to temporary test file: %v", err)
|
||||||
|
}
|
||||||
|
if err := tmpfile.Close(); err != nil {
|
||||||
|
t.Fatalf("Error closing temporary test file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = picol.RunPicol(tmpfile.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error during RunPicol: %v", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue