Skip to content

Commit

Permalink
Add Generic type binding, unit tests to CI (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchpaulus authored Dec 30, 2024
1 parent a34ad05 commit 11aebc6
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ jobs:
realpath ./mshell >> "$GITHUB_PATH"
printf "MSHSTDLIB=%s\n" "$(realpath ./lib/std.msh)" >> "$GITHUB_ENV"
- name: Test mshell
- name: Run Go unit tests
run: cd mshell && go test -v

- name: Test mshell end-to-end
run: cd tests && ./test.sh

- name: Run awk example tests
Expand Down
88 changes: 88 additions & 0 deletions mshell/Parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,19 @@ type MShellType interface {
Equals(other MShellType) bool
String() string
ToMshell() string
Bind(otherType MShellType) ([]BoundType, error)
}

type TypeDefinition struct {
InputTypes []MShellType
OutputTypes []MShellType
}

type BoundType struct {
GenericName string
Type MShellType
}

func (def *TypeDefinition) ToMshell() string {
inputMshellCode := make([]string, len(def.InputTypes))
for i, t := range def.InputTypes {
Expand All @@ -273,6 +279,10 @@ type TypeGeneric struct {
Name string
}

func (generic TypeGeneric) Bind(otherType MShellType) ([]BoundType, error) {
return []BoundType{{GenericName: generic.Name, Type: otherType}}, nil
}

func (generic TypeGeneric) ToMshell() string {
return generic.Name
}
Expand All @@ -294,6 +304,10 @@ func (generic TypeGeneric) String() string {

type TypeInt struct { }

func (t TypeInt) Bind(otherType MShellType) ([]BoundType, error) {
return make([]BoundType, 0), nil
}

func (t TypeInt) ToMshell() string {
return "int"
}
Expand All @@ -313,6 +327,10 @@ func (t TypeInt) String() string {

type TypeFloat struct { }

func (t TypeFloat) Bind(otherType MShellType) ([]BoundType, error) {
return make([]BoundType, 0), nil
}

func (t TypeFloat) ToMshell() string {
return "float"
}
Expand All @@ -333,6 +351,10 @@ func (t TypeFloat) String() string {

type TypeString struct { }

func (t TypeString) Bind(otherType MShellType) ([]BoundType, error) {
return make([]BoundType, 0), nil
}

func (t TypeString) ToMshell() string {
return "str"
}
Expand All @@ -352,6 +374,10 @@ func (t TypeString) String() string {

type TypeBool struct { }

func (t TypeBool) Bind(otherType MShellType) ([]BoundType, error) {
return make([]BoundType, 0), nil
}

func (t TypeBool) ToMshell() string {
return "bool"
}
Expand All @@ -374,6 +400,16 @@ type TypeList struct {
Count int // This is < 0 if the Count is not known
}

func (list *TypeList) Bind(otherType MShellType) ([]BoundType, error) {
asListType, ok := otherType.(*TypeList)
if !ok {
return []BoundType{}, errors.New("Cannot bind a list to a non-list type")
}
// Recursively select all the bound types in the list.
return list.ListType.Bind(asListType.ListType)
}


func (list *TypeList) ToMshell() string {
return fmt.Sprintf("[%s]", list.ListType.ToMshell())
}
Expand Down Expand Up @@ -409,6 +445,28 @@ type TypeTuple struct {
Types []MShellType
}

func (tuple *TypeTuple) Bind(otherType MShellType) ([]BoundType, error) {
asTuple, ok := otherType.(*TypeTuple)
if !ok {
return []BoundType{}, errors.New("Cannot bind a tuple to a non-tuple type")
}

if len(tuple.Types) != len(asTuple.Types) {
return []BoundType{}, errors.New("Cannot bind tuples of different lengths")
}

boundTypes := make([]BoundType, 0)
for i, t := range tuple.Types {
bound, err := t.Bind(asTuple.Types[i])
if err != nil {
return boundTypes, err
}
boundTypes = append(boundTypes, bound...)
}
return boundTypes, nil
}


func (tuple *TypeTuple) ToMshell() string {
builder := strings.Builder{}
builder.WriteString("[")
Expand Down Expand Up @@ -472,6 +530,36 @@ type TypeQuote struct {
OutputTypes []MShellType
}

func (quote *TypeQuote) Bind(otherType MShellType) ([]BoundType, error) {
asQuote, ok := otherType.(*TypeQuote)
if !ok {
return []BoundType{}, errors.New("Cannot bind a quote to a non-quote type")
}

if len(quote.InputTypes) != len(asQuote.InputTypes) || len(quote.OutputTypes) != len(asQuote.OutputTypes) {
return []BoundType{}, errors.New("Cannot bind quotes of different lengths")
}

boundTypes := make([]BoundType, 0)
for i, t := range quote.InputTypes {
bound, err := t.Bind(asQuote.InputTypes[i])
if err != nil {
return boundTypes, err
}
boundTypes = append(boundTypes, bound...)
}

for i, t := range quote.OutputTypes {
bound, err := t.Bind(asQuote.OutputTypes[i])
if err != nil {
return boundTypes, err
}
boundTypes = append(boundTypes, bound...)
}

return boundTypes, nil
}

func (quote *TypeQuote) ToMshell() string {
builder := strings.Builder{}
builder.WriteString("(")
Expand Down
56 changes: 56 additions & 0 deletions mshell/TypeChecking_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
// "fmt"
// "strings"
"testing"
)

func TestSimpleBinding(t *testing.T) {
generic := TypeGeneric{ "a" }
concrete := TypeInt{}

binding, err := generic.Bind(concrete)
if err != nil {
t.Error("Binding failed")
}

if len(binding) != 1 {
t.Error("Binding failed")
}

if !binding[0].Type.Equals(concrete) {
t.Error("Binding failed")
}
}

func TestRecursiveBinding(t *testing.T) {
generic := &TypeList{
ListType: &TypeList {
ListType: TypeGeneric{ "a" },
Count: -1,
},
Count: -1,
}

concrete := &TypeList{
ListType: &TypeList{
ListType: TypeInt{},
Count: -1,
},
Count: -1,
}

binding, err := generic.Bind(concrete)
if err != nil {
t.Error("Binding failed")
}

if len(binding) != 1 {
t.Error("Binding failed")
}

if !binding[0].Type.Equals(TypeInt{}) {
t.Error("Binding failed")
}
}

0 comments on commit 11aebc6

Please sign in to comment.