lang: Update map type definition to include a prefix

It turns out that some planned additions to the parser make it so that
the map type definition can be ambiguous. As a result, this patch
updates the definition so that the map definition is not confused with
an open curly bracket anywhere.

Thanks to pestle and stbenjamin for their help understanding yacc!
This commit is contained in:
James Shubin
2018-06-18 15:53:35 -04:00
parent 05f6ba7297
commit c4b97fadcc
13 changed files with 46 additions and 39 deletions

View File

@@ -60,7 +60,7 @@ func (obj *ExchangeFunc) Info() *interfaces.Info {
// TODO: do we want to allow this to be statically polymorphic, // TODO: do we want to allow this to be statically polymorphic,
// and have value be any type we might want? // and have value be any type we might want?
// output is map of: hostname => value // output is map of: hostname => value
Sig: types.NewType("func(namespace str, value str) {str: str}"), Sig: types.NewType("func(namespace str, value str) map{str: str}"),
Err: obj.Validate(), Err: obj.Validate(),
} }
} }

View File

@@ -58,7 +58,7 @@ func (obj *KVLookupFunc) Info() *interfaces.Info {
Pure: false, // definitely false Pure: false, // definitely false
Memo: false, Memo: false,
// output is map of: hostname => value // output is map of: hostname => value
Sig: types.NewType("func(namespace str) {str: str}"), Sig: types.NewType("func(namespace str) map{str: str}"),
Err: obj.Validate(), Err: obj.Validate(),
} }
} }

View File

@@ -38,7 +38,7 @@ func init() {
V: HasEnv, V: HasEnv,
}) })
Register("env", &types.FuncValue{ Register("env", &types.FuncValue{
T: types.NewType("func() {str: str}"), T: types.NewType("func() map{str: str}"),
V: Env, V: Env,
}) })
} }
@@ -78,7 +78,7 @@ func Env(input []types.Value) (types.Value, error) {
} }
} }
return &types.MapValue{ return &types.MapValue{
T: types.NewType("{str: str}"), T: types.NewType("map{str: str}"),
V: environ, V: environ,
}, nil }, nil
} }

View File

@@ -30,7 +30,7 @@ func init() {
V: Len, V: Len,
}, },
{ {
T: types.NewType("func({variant: variant}) int"), T: types.NewType("func(map{variant: variant}) int"),
V: Len, V: Len,
}, },
// TODO: should we add support for struct or func lengths? // TODO: should we add support for struct or func lengths?

View File

@@ -169,6 +169,11 @@
lval.str = yylex.Text() lval.str = yylex.Text()
return FLOAT_IDENTIFIER return FLOAT_IDENTIFIER
} }
/map/ {
yylex.pos(lval) // our pos
lval.str = yylex.Text()
return MAP_IDENTIFIER
}
/struct/ { /struct/ {
yylex.pos(lval) // our pos yylex.pos(lval) // our pos
lval.str = yylex.Text() lval.str = yylex.Text()

View File

@@ -242,11 +242,11 @@ func TestLexParse0(t *testing.T) {
values = append(values, test{ values = append(values, test{
name: "maps and lists", name: "maps and lists",
code: ` code: `
$strmap {str: int} = { $strmap map{str: int} = {
"key1" => 42, "key1" => 42,
"key2" => -13, "key2" => -13,
} }
$mapstrintlist {str: []int} = { $mapstrintlist map{str: []int} = {
"key1" => [42, 44,], "key1" => [42, 44,],
"key2" => [], "key2" => [],
"key3" => [-13,], "key3" => [-13,],

View File

@@ -83,7 +83,7 @@ func init() {
%token COMMA COLON SEMICOLON %token COMMA COLON SEMICOLON
%token ELVIS ROCKET ARROW DOT %token ELVIS ROCKET ARROW DOT
%token STR_IDENTIFIER BOOL_IDENTIFIER INT_IDENTIFIER FLOAT_IDENTIFIER %token STR_IDENTIFIER BOOL_IDENTIFIER INT_IDENTIFIER FLOAT_IDENTIFIER
%token STRUCT_IDENTIFIER VARIANT_IDENTIFIER VAR_IDENTIFIER IDENTIFIER %token MAP_IDENTIFIER STRUCT_IDENTIFIER VARIANT_IDENTIFIER VAR_IDENTIFIER IDENTIFIER
%token VAR_IDENTIFIER_HX CAPITALIZED_IDENTIFIER %token VAR_IDENTIFIER_HX CAPITALIZED_IDENTIFIER
%token CLASS_IDENTIFIER INCLUDE_IDENTIFIER %token CLASS_IDENTIFIER INCLUDE_IDENTIFIER
%token COMMENT ERROR %token COMMENT ERROR
@@ -910,11 +910,11 @@ type:
posLast(yylex, yyDollar) // our pos posLast(yylex, yyDollar) // our pos
$$.typ = types.NewType("[]" + $3.typ.String()) $$.typ = types.NewType("[]" + $3.typ.String())
} }
| OPEN_CURLY type COLON type CLOSE_CURLY | MAP_IDENTIFIER OPEN_CURLY type COLON type CLOSE_CURLY
// map: {str: int} or {str: []int} // map: map{str: int} or map{str: []int}
{ {
posLast(yylex, yyDollar) // our pos posLast(yylex, yyDollar) // our pos
$$.typ = types.NewType(fmt.Sprintf("{%s: %s}", $2.typ.String(), $4.typ.String())) $$.typ = types.NewType(fmt.Sprintf("map{%s: %s}", $3.typ.String(), $5.typ.String()))
} }
| STRUCT_IDENTIFIER OPEN_CURLY type_struct_fields CLOSE_CURLY | STRUCT_IDENTIFIER OPEN_CURLY type_struct_fields CLOSE_CURLY
// struct: struct{} or struct{a bool} or struct{a bool; bb int} // struct: struct{} or struct{a bool} or struct{a bool; bb int}

View File

@@ -236,8 +236,8 @@ func NewType(s string) *Type {
} }
// KindMap // KindMap
if strings.HasPrefix(s, "{") && strings.HasSuffix(s, "}") { if strings.HasPrefix(s, "map{") && strings.HasSuffix(s, "}") {
s := s[1 : len(s)-1] s := s[len("map{") : len(s)-1]
if s == "" { // it is empty if s == "" { // it is empty
return nil return nil
} }
@@ -502,7 +502,7 @@ func (obj *Type) String() string {
if obj.Key == nil || obj.Val == nil { if obj.Key == nil || obj.Val == nil {
panic("malformed map type") panic("malformed map type")
} }
return fmt.Sprintf("{%s: %s}", obj.Key.String(), obj.Val.String()) return fmt.Sprintf("map{%s: %s}", obj.Key.String(), obj.Val.String())
case KindStruct: // {a bool; b int} case KindStruct: // {a bool; b int}
if obj.Map == nil { if obj.Map == nil {

View File

@@ -122,8 +122,8 @@ func TestType1(t *testing.T) {
}, },
// maps // maps
"{}": nil, // invalid "map{}": nil, // invalid
"{str: str}": { "map{str: str}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindStr, Kind: KindStr,
@@ -132,7 +132,7 @@ func TestType1(t *testing.T) {
Kind: KindStr, Kind: KindStr,
}, },
}, },
"{str: int}": { "map{str: int}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindStr, Kind: KindStr,
@@ -141,7 +141,7 @@ func TestType1(t *testing.T) {
Kind: KindInt, Kind: KindInt,
}, },
}, },
"{str: variant}": { "map{str: variant}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindStr, Kind: KindStr,
@@ -150,7 +150,7 @@ func TestType1(t *testing.T) {
Kind: KindVariant, Kind: KindVariant,
}, },
}, },
"{variant: int}": { "map{variant: int}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindVariant, Kind: KindVariant,
@@ -159,7 +159,7 @@ func TestType1(t *testing.T) {
Kind: KindInt, Kind: KindInt,
}, },
}, },
"{variant: variant}": { "map{variant: variant}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindVariant, Kind: KindVariant,
@@ -170,7 +170,7 @@ func TestType1(t *testing.T) {
}, },
// nested maps // nested maps
"{str: {int: bool}}": { "map{str: map{int: bool}}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindStr, Kind: KindStr,
@@ -185,7 +185,7 @@ func TestType1(t *testing.T) {
}, },
}, },
}, },
"{{int: bool}: str}": { "map{map{int: bool}: str}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindMap, Kind: KindMap,
@@ -200,7 +200,7 @@ func TestType1(t *testing.T) {
Kind: KindStr, Kind: KindStr,
}, },
}, },
"{{str: int}: {int: bool}}": { "map{map{str: int}: map{int: bool}}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindMap, Kind: KindMap,
@@ -221,7 +221,7 @@ func TestType1(t *testing.T) {
}, },
}, },
}, },
"{str: {int: {int: bool}}}": { "map{str: map{int: map{int: bool}}}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindStr, Kind: KindStr,
@@ -394,7 +394,7 @@ func TestType1(t *testing.T) {
}, },
// mixed nesting // mixed nesting
"{str: []struct{a bool; int []bool}}": { "map{str: []struct{a bool; int []bool}}": {
Kind: KindMap, Kind: KindMap,
Key: &Type{ Key: &Type{
Kind: KindStr, Kind: KindStr,
@@ -421,7 +421,7 @@ func TestType1(t *testing.T) {
}, },
}, },
}, },
"struct{a {str: {struct{deeply int; nested bool}: {int: bool}}}; bb struct{z bool; yy int}; ccc str}": { "struct{a map{str: map{struct{deeply int; nested bool}: map{int: bool}}}; bb struct{z bool; yy int}; ccc str}": {
Kind: KindStruct, Kind: KindStruct,
Ord: []string{ Ord: []string{
"a", "a",
@@ -558,7 +558,7 @@ func TestType1(t *testing.T) {
Kind: KindBool, Kind: KindBool,
}, },
}, },
"func({str: int}) bool": { "func(map{str: int}) bool": {
Kind: KindFunc, Kind: KindFunc,
// key names are arbitrary... // key names are arbitrary...
Map: map[string]*Type{ Map: map[string]*Type{
@@ -579,7 +579,7 @@ func TestType1(t *testing.T) {
Kind: KindBool, Kind: KindBool,
}, },
}, },
"func(bool, {str: int}) bool": { "func(bool, map{str: int}) bool": {
Kind: KindFunc, Kind: KindFunc,
// key names are arbitrary... // key names are arbitrary...
Map: map[string]*Type{ Map: map[string]*Type{
@@ -1243,7 +1243,7 @@ func TestType3(t *testing.T) {
Kind: KindBool, Kind: KindBool,
}, },
}, },
"func(aaa {str: int}) bool": { "func(aaa map{str: int}) bool": {
Kind: KindFunc, Kind: KindFunc,
// key names are arbitrary... // key names are arbitrary...
Map: map[string]*Type{ Map: map[string]*Type{

View File

@@ -125,7 +125,7 @@ func ValueOf(v reflect.Value) (Value, error) {
} }
return &MapValue{ return &MapValue{
T: NewType(fmt.Sprintf("{%s: %s}", kt.String(), vt.String())), T: NewType(fmt.Sprintf("map{%s: %s}", kt.String(), vt.String())),
V: m, V: m,
}, nil }, nil

View File

@@ -81,14 +81,14 @@ func TestPrint1(t *testing.T) {
}}: `[["a", "bb", "ccc"], ["d", "ee", "fff"], ["g", "hh", "iii"]]`, }}: `[["a", "bb", "ccc"], ["d", "ee", "fff"], ["g", "hh", "iii"]]`,
} }
d0 := NewMap(NewType("{str: int}")) d0 := NewMap(NewType("map{str: int}"))
values[d0] = `{}` values[d0] = `{}`
d1 := NewMap(NewType("{str: int}")) d1 := NewMap(NewType("map{str: int}"))
d1.Add(&StrValue{V: "answer"}, &IntValue{V: 42}) d1.Add(&StrValue{V: "answer"}, &IntValue{V: 42})
values[d1] = `{"answer": 42}` values[d1] = `{"answer": 42}`
d2 := NewMap(NewType("{str: int}")) d2 := NewMap(NewType("map{str: int}"))
d2.Add(&StrValue{V: "answer"}, &IntValue{V: 42}) d2.Add(&StrValue{V: "answer"}, &IntValue{V: 42})
d2.Add(&StrValue{V: "hello"}, &IntValue{V: 13}) d2.Add(&StrValue{V: "hello"}, &IntValue{V: 13})
values[d2] = `{"answer": 42, "hello": 13}` values[d2] = `{"answer": 42, "hello": 13}`
@@ -196,10 +196,10 @@ func TestReflectValue1(t *testing.T) {
}: `[[a bb ccc] [d ee fff] [g hh iii]]`, }: `[[a bb ccc] [d ee fff] [g hh iii]]`,
} }
d0 := NewMap(NewType("{str: int}")) d0 := NewMap(NewType("map{str: int}"))
values[d0] = `map[]` values[d0] = `map[]`
d1 := NewMap(NewType("{str: int}")) d1 := NewMap(NewType("map{str: int}"))
d1.Add(&StrValue{V: "answer"}, &IntValue{V: 42}) d1.Add(&StrValue{V: "answer"}, &IntValue{V: 42})
values[d1] = `map[answer:42]` values[d1] = `map[answer:42]`
@@ -464,7 +464,7 @@ func TestSort1(t *testing.T) {
} }
func TestMapReflectValue1(t *testing.T) { func TestMapReflectValue1(t *testing.T) {
d := NewMap(NewType("{str: int}")) d := NewMap(NewType("map{str: int}"))
d.Add(&StrValue{V: "answer"}, &IntValue{V: 42}) d.Add(&StrValue{V: "answer"}, &IntValue{V: 42})
d.Add(&StrValue{V: "hello"}, &IntValue{V: 13}) d.Add(&StrValue{V: "hello"}, &IntValue{V: 13})
// both are valid, since map's aren't sorted // both are valid, since map's aren't sorted
@@ -480,7 +480,7 @@ func TestMapReflectValue1(t *testing.T) {
t.Errorf("did not match expected: `%s`", exp2) t.Errorf("did not match expected: `%s`", exp2)
} }
d2 := NewMap(NewType("{str: str}")) d2 := NewMap(NewType("map{str: str}"))
d2.Add(&StrValue{V: "answer"}, &StrValue{V: "42 hello:13"}) d2.Add(&StrValue{V: "answer"}, &StrValue{V: "42 hello:13"})
val2 := d2.Value() val2 := d2.Value()
@@ -512,7 +512,7 @@ func TestList1(t *testing.T) {
} }
func TestMapLookup1(t *testing.T) { func TestMapLookup1(t *testing.T) {
d := NewMap(NewType("{str: int}")) d := NewMap(NewType("map{str: int}"))
k := &StrValue{V: "answer"} k := &StrValue{V: "answer"}
v := &IntValue{V: 42} v := &IntValue{V: 42}
if err := d.Add(k, v); err != nil { if err := d.Add(k, v); err != nil {

View File

@@ -147,7 +147,7 @@ func TestUnification1(t *testing.T) {
v1: types.TypeFloat, v1: types.TypeFloat,
v2: types.TypeFloat, v2: types.TypeFloat,
v3: types.TypeFloat, v3: types.TypeFloat,
expr: types.NewType("{int: float}"), expr: types.NewType("map{int: float}"),
}, },
}) })
} }

View File

@@ -11,6 +11,8 @@ cd "${ROOT}"
failures='' failures=''
# TODO: test examples/lang/ directory to see if the .mcl files compile correctly
buildout='test-examples.out' buildout='test-examples.out'
# make symlink to outside of package # make symlink to outside of package
linkto="`pwd`/examples/lib/" linkto="`pwd`/examples/lib/"