mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-04 10:57:50 +08:00
parse: Check all fields of AST.
This commit is contained in:
parent
935bfd24e7
commit
618d927605
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// AST checking utilities. Used in test cases.
|
||||
|
@ -18,14 +20,16 @@ import (
|
|||
// fields part specified the children of the Pipeline instead of the Chunk
|
||||
// (which has no additional interesting fields anyway). Multi-level coalescence
|
||||
// like "Chunk/Pipeline/Form" is also allowed.
|
||||
//
|
||||
// The dynamic type of the Node being checked is assumed to be a pointer to a
|
||||
// struct that embeds the "node" struct.
|
||||
type ast struct {
|
||||
name string
|
||||
fields fs
|
||||
}
|
||||
|
||||
// fs specifies fields of a Node to check. For each key/value pair in fs, the
|
||||
// value ("wanted value") checks a field in the Node ("found value") using the
|
||||
// following algorithm:
|
||||
// fs specifies fields of a Node to check. For the value of field $f in the
|
||||
// Node ("found value"), fs[$f] ("wanted value") is used to check against it.
|
||||
//
|
||||
// If the key is "text", the SourceText of the Node is checked. It doesn't
|
||||
// involve a found value.
|
||||
|
@ -62,25 +66,30 @@ func checkAST(n Node, want ast) error {
|
|||
n = fields[0]
|
||||
}
|
||||
|
||||
if want.fields == nil && len(n.Children()) != 0 {
|
||||
return fmt.Errorf("want leaf, got inner node (%s)", summary(n))
|
||||
}
|
||||
nv := reflect.ValueOf(n).Elem()
|
||||
ntype := reflect.TypeOf(n).Elem()
|
||||
nvalue := reflect.ValueOf(n).Elem()
|
||||
|
||||
// TODO: Check fields present in n but not in want
|
||||
for fieldname, wantfield := range want.fields {
|
||||
if fieldname == "text" {
|
||||
if n.SourceText() != wantfield.(string) {
|
||||
return fmt.Errorf("want %q, got %q (%s)", wantfield, n.SourceText())
|
||||
}
|
||||
} else {
|
||||
fv := nv.FieldByName(fieldname)
|
||||
err := checkField(fv.Interface(), wantfield, summary(n))
|
||||
for i := 0; i < ntype.NumField(); i++ {
|
||||
fieldname := ntype.Field(i).Name
|
||||
if !exported(fieldname) {
|
||||
// Unexported field
|
||||
continue
|
||||
}
|
||||
got := nvalue.Field(i).Interface()
|
||||
want, ok := want.fields[fieldname]
|
||||
if ok {
|
||||
err := checkField(got, want, "field "+fieldname+" of: "+summary(n))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Not specified. Check if got is a zero value of its type.
|
||||
if !reflect.DeepEqual(got, reflect.Zero(reflect.TypeOf(got)).Interface()) {
|
||||
return fmt.Errorf("want zero, got %v (field %s of: %s)", got, fieldname, summary(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -138,3 +147,8 @@ func checkNodeInField(got Node, want interface{}) error {
|
|||
panic(fmt.Sprintf("bad want type %T (%s)", want, summary(got)))
|
||||
}
|
||||
}
|
||||
|
||||
func exported(name string) bool {
|
||||
r, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(r)
|
||||
}
|
||||
|
|
|
@ -48,9 +48,9 @@ var goodCases = []struct {
|
|||
"Head": "a",
|
||||
"Redirs": []ast{
|
||||
ast{"Redir", fs{"Mode": Append, "Source": "b"}},
|
||||
ast{"Redir", fs{"Mode": Write, "Source": "b"}},
|
||||
ast{"Redir", fs{"Mode": Write, "SourceIsFd": true, "Source": "-"}},
|
||||
ast{"Redir", fs{"Mode": Write, "SourceIsFd": true, "Source": "1"}},
|
||||
ast{"Redir", fs{"Dest": "2", "Mode": Write, "Source": "b"}},
|
||||
ast{"Redir", fs{"Dest": "3", "Mode": Write, "SourceIsFd": true, "Source": "-"}},
|
||||
ast{"Redir", fs{"Dest": "4", "Mode": Write, "SourceIsFd": true, "Source": "1"}},
|
||||
},
|
||||
}}},
|
||||
// Exitus redirection
|
||||
|
@ -76,16 +76,20 @@ var goodCases = []struct {
|
|||
//
|
||||
// Single quote
|
||||
{"a 'b'", a(ast{"Compound/Indexing/Primary", fs{
|
||||
"text": "'b'", "Type": SingleQuoted,
|
||||
"Type": SingleQuoted, "Value": "b",
|
||||
}})},
|
||||
// Double quote
|
||||
{`a "b"`, a(ast{"Compound/Indexing/Primary", fs{
|
||||
"text": `"b"`, "Type": DoubleQuoted,
|
||||
"Type": DoubleQuoted, "Value": "b",
|
||||
}})},
|
||||
// List
|
||||
{"a [] [ ] [1] [ 2] [3 ] [ 4 5 6 7 ]", a(
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": List}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": List}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": List,
|
||||
"List": ""}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": List,
|
||||
"List": ""}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": List,
|
||||
"List": ast{"Array", fs{"Compounds": []string{"1"}}}}},
|
||||
|
@ -124,36 +128,33 @@ var goodCases = []struct {
|
|||
)},
|
||||
// Empty map
|
||||
{"a [&] [ &] [& ] [ & ]", a(
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "text": "[&]"}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "text": "[ &]"}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "text": "[& ]"}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "text": "[ & ]"}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "MapPairs": nil}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "MapPairs": nil}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "MapPairs": nil}},
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": Map, "MapPairs": nil}},
|
||||
)},
|
||||
// Lambda
|
||||
{"a []{} [ ]{ } []{ echo 233 } [ $x $y ]{puts $x $y} { put $1}", a(
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": Lambda,
|
||||
"Type": Lambda, "List": "", "Chunk": "",
|
||||
}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": Lambda,
|
||||
"Type": Lambda, "List": "", "Chunk": " ",
|
||||
}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": Lambda,
|
||||
"Chunk": " echo 233 ",
|
||||
"Type": Lambda, "List": "", "Chunk": " echo 233 ",
|
||||
}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": Lambda,
|
||||
"List": "$x $y ",
|
||||
"Chunk": "puts $x $y",
|
||||
"Type": Lambda, "List": "$x $y ", "Chunk": "puts $x $y",
|
||||
}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": Lambda,
|
||||
"Chunk": " put $1",
|
||||
"Type": Lambda, "List": nil, "Chunk": " put $1",
|
||||
}},
|
||||
)},
|
||||
// Output capture
|
||||
{"a () (b;c)", a(
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": OutputCapture}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": OutputCapture, "Chunk": ""}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": OutputCapture, "Chunk": "b;c",
|
||||
}})},
|
||||
|
@ -163,7 +164,8 @@ var goodCases = []struct {
|
|||
{"a `a (b `c`)` `d [`e`]`", a("`a (b `c`)`", "`d [`e`]`")},
|
||||
// Exitus capture
|
||||
{"a ?() ?(b;c)", a(
|
||||
ast{"Compound/Indexing/Primary", fs{"Type": ErrorCapture}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": ErrorCapture, "Chunk": ""}},
|
||||
ast{"Compound/Indexing/Primary", fs{
|
||||
"Type": ErrorCapture, "Chunk": "b;c",
|
||||
}})},
|
||||
|
@ -177,7 +179,7 @@ var goodCases = []struct {
|
|||
{"a ~xiaq/go", a(
|
||||
ast{"Compound", fs{
|
||||
"Indexings": []ast{
|
||||
{"Indexing/Primary", fs{"Type": Tilde}},
|
||||
{"Indexing/Primary", fs{"Type": Tilde, "Value": "~"}},
|
||||
{"Indexing/Primary", fs{"Type": Bareword, "Value": "xiaq/go"}},
|
||||
},
|
||||
}},
|
||||
|
|
Loading…
Reference in New Issue
Block a user