diff --git a/pkg/corset/ast.go b/pkg/corset/ast.go index 3bc44b0..577efab 100644 --- a/pkg/corset/ast.go +++ b/pkg/corset/ast.go @@ -111,46 +111,50 @@ type Assignment interface { Resolve(*Environment) ([]ColumnAssignment, []SyntaxError) } -// ColumnName represents a name within some syntactic item. Essentially this wraps a +// Name represents a name within some syntactic item. Essentially this wraps a // string and provides a mechanism for it to be associated with source line // information. -type ColumnName struct { - name string +type Name struct { + // Name of symbol + name string + // Indicates whether represents function or something else. + function bool + // Binding constructed for symbol. binding Binding } // IsQualified determines whether this symbol is qualfied or not (i.e. has an // explicit module specifier). Column names are never qualified. -func (e *ColumnName) IsQualified() bool { +func (e *Name) IsQualified() bool { return false } // IsFunction indicates whether or not this symbol refers to a function (which // of course it never does). -func (e *ColumnName) IsFunction() bool { - return false +func (e *Name) IsFunction() bool { + return e.function } // IsResolved checks whether this symbol has been resolved already, or not. -func (e *ColumnName) IsResolved() bool { +func (e *Name) IsResolved() bool { return e.binding != nil } // Module returns the optional module qualification. This always panics because // column name's are never qualified. -func (e *ColumnName) Module() string { +func (e *Name) Module() string { panic("undefined") } // Name returns the (unqualified) name of the column to which this symbol // refers. -func (e *ColumnName) Name() string { +func (e *Name) Name() string { return e.name } // Binding gets binding associated with this interface. This will panic if this // symbol is not yet resolved. -func (e *ColumnName) Binding() Binding { +func (e *Name) Binding() Binding { if e.binding == nil { panic("name not yet resolved") } @@ -160,7 +164,7 @@ func (e *ColumnName) Binding() Binding { // Resolve this symbol by associating it with the binding associated with // the definition of the symbol to which this refers. -func (e *ColumnName) Resolve(binding Binding) { +func (e *Name) Resolve(binding Binding) { if e.binding != nil { panic("name already resolved") } @@ -170,7 +174,7 @@ func (e *ColumnName) Resolve(binding Binding) { // Lisp converts this node into its lisp representation. This is primarily used // for debugging purposes. -func (e *ColumnName) Lisp() sexp.SExp { +func (e *Name) Lisp() sexp.SExp { return sexp.NewSymbol(e.name) } @@ -181,6 +185,8 @@ func (e *ColumnName) Lisp() sexp.SExp { // DefAliases represents the declaration of one or more aliases. That is, // alternate names for existing symbols. type DefAliases struct { + // Distinguishes defalias from defunalias + functions bool // Aliases aliases []*DefAlias // Symbols being aliased @@ -210,9 +216,16 @@ func (p *DefAliases) Lisp() sexp.SExp { pairs.Append(p.symbols[i].Lisp()) } // + var name *sexp.Symbol + // + if p.functions { + name = sexp.NewSymbol("defunalias") + } else { + name = sexp.NewSymbol("defalias") + } + // return sexp.NewList([]sexp.SExp{ - sexp.NewSymbol("defalias"), - pairs, + name, pairs, }) } diff --git a/pkg/corset/parser.go b/pkg/corset/parser.go index 6e89c60..438a87b 100644 --- a/pkg/corset/parser.go +++ b/pkg/corset/parser.go @@ -234,13 +234,15 @@ func (p *Parser) parseDeclaration(module string, s *sexp.List) (Declaration, []S ) // if s.MatchSymbols(1, "defalias") { - decl, errors = p.parseDefAlias(s.Elements) + decl, errors = p.parseDefAlias(false, s.Elements) } else if s.MatchSymbols(1, "defcolumns") { decl, errors = p.parseDefColumns(module, s) } else if s.Len() > 1 && s.MatchSymbols(1, "defconst") { decl, errors = p.parseDefConst(s.Elements) } else if s.Len() == 4 && s.MatchSymbols(2, "defconstraint") { decl, errors = p.parseDefConstraint(s.Elements) + } else if s.MatchSymbols(1, "defunalias") { + decl, errors = p.parseDefAlias(true, s.Elements) } else if s.Len() == 3 && s.MatchSymbols(1, "defpurefun") { decl, errors = p.parseDefFun(true, s.Elements) } else if s.Len() == 3 && s.MatchSymbols(1, "defun") { @@ -271,7 +273,7 @@ func (p *Parser) parseDeclaration(module string, s *sexp.List) (Declaration, []S } // Parse an alias declaration -func (p *Parser) parseDefAlias(elements []sexp.SExp) (Declaration, []SyntaxError) { +func (p *Parser) parseDefAlias(functions bool, elements []sexp.SExp) (Declaration, []SyntaxError) { var ( errors []SyntaxError aliases []*DefAlias @@ -291,7 +293,7 @@ func (p *Parser) parseDefAlias(elements []sexp.SExp) (Declaration, []SyntaxError errors = append(errors, *p.translator.SyntaxError(elements[i+1], "invalid alias definition")) } else { alias := &DefAlias{elements[i].AsSymbol().Value} - name := &ColumnName{elements[i+1].AsSymbol().Value, nil} + name := &Name{elements[i+1].AsSymbol().Value, functions, nil} p.mapSourceNode(elements[i], alias) p.mapSourceNode(elements[i+1], name) // @@ -300,7 +302,7 @@ func (p *Parser) parseDefAlias(elements []sexp.SExp) (Declaration, []SyntaxError } } // Done - return &DefAliases{aliases, names}, errors + return &DefAliases{functions, aliases, names}, errors } // Parse a column declaration @@ -452,7 +454,7 @@ func (p *Parser) parseDefInterleaved(module string, elements []sexp.SExp) (Decla return nil, p.translator.SyntaxError(ith, "malformed source column") } // Extract column name - sources[i] = &ColumnName{ith.AsSymbol().Value, nil} + sources[i] = &Name{ith.AsSymbol().Value, false, nil} p.mapSourceNode(ith, sources[i]) } // @@ -536,10 +538,10 @@ func (p *Parser) parseDefPermutation(module string, elements []sexp.SExp) (Decla return &DefPermutation{targets, sources, signs}, nil } -func (p *Parser) parsePermutedColumnDeclaration(signRequired bool, e sexp.SExp) (*ColumnName, bool, *SyntaxError) { +func (p *Parser) parsePermutedColumnDeclaration(signRequired bool, e sexp.SExp) (*Name, bool, *SyntaxError) { var ( err *SyntaxError - name *ColumnName + name *Name sign bool ) // Check whether extended declaration or not. @@ -557,11 +559,11 @@ func (p *Parser) parsePermutedColumnDeclaration(signRequired bool, e sexp.SExp) return nil, false, err } // Parse column name - name = &ColumnName{l.Get(1).AsSymbol().Value, nil} + name = &Name{l.Get(1).AsSymbol().Value, false, nil} } else if signRequired { return nil, false, p.translator.SyntaxError(e, "missing sort direction") } else { - name = &ColumnName{e.String(false), nil} + name = &Name{e.String(false), false, nil} } // Update source mapping p.mapSourceNode(e, name) diff --git a/pkg/test/invalid_corset_test.go b/pkg/test/invalid_corset_test.go index 4880f9f..45d2255 100644 --- a/pkg/test/invalid_corset_test.go +++ b/pkg/test/invalid_corset_test.go @@ -171,6 +171,25 @@ func Test_Invalid_Alias_07(t *testing.T) { CheckInvalid(t, "alias_invalid_07") } +// =================================================================== +// Function Alias Tests +// =================================================================== +func Test_Invalid_FunAlias_01(t *testing.T) { + CheckInvalid(t, "funalias_invalid_01") +} + +func Test_Invalid_FunAlias_02(t *testing.T) { + CheckInvalid(t, "funalias_invalid_02") +} + +func Test_Invalid_FunAlias_03(t *testing.T) { + CheckInvalid(t, "funalias_invalid_03") +} + +func Test_Invalid_FunAlias_04(t *testing.T) { + CheckInvalid(t, "funalias_invalid_04") +} + // =================================================================== // Property Tests // =================================================================== diff --git a/pkg/test/valid_corset_test.go b/pkg/test/valid_corset_test.go index 99c663d..bcda248 100644 --- a/pkg/test/valid_corset_test.go +++ b/pkg/test/valid_corset_test.go @@ -119,6 +119,21 @@ func Test_Alias_06(t *testing.T) { Check(t, "alias_06") } +// =================================================================== +// Function Alias Tests +// =================================================================== +func Test_FunAlias_01(t *testing.T) { + Check(t, "funalias_01") +} + +func Test_FunAlias_02(t *testing.T) { + Check(t, "funalias_02") +} + +func Test_FunAlias_03(t *testing.T) { + Check(t, "funalias_03") +} + // =================================================================== // Domain Tests // =================================================================== diff --git a/testdata/funalias_01.accepts b/testdata/funalias_01.accepts new file mode 100644 index 0000000..945d190 --- /dev/null +++ b/testdata/funalias_01.accepts @@ -0,0 +1,6 @@ +{ "A": [0] } +{ "A": [0,0] } +{ "A": [0,0,0] } +{ "A": [0,0,0,0] } +{ "A": [0,0,0,0,0] } +{ "A": [0,0,0,0,0,0] } diff --git a/testdata/funalias_01.lisp b/testdata/funalias_01.lisp new file mode 100644 index 0000000..c01144a --- /dev/null +++ b/testdata/funalias_01.lisp @@ -0,0 +1,4 @@ +(defcolumns A) +(defpurefun (id x) x) +(defunalias ID id) +(defconstraint test () (ID A)) diff --git a/testdata/funalias_01.rejects b/testdata/funalias_01.rejects new file mode 100644 index 0000000..a5f8491 --- /dev/null +++ b/testdata/funalias_01.rejects @@ -0,0 +1,5 @@ +{ "A": [1] } +{ "A": [2] } +{ "A": [1,1] } +{ "A": [1,1] } +{ "A": [2,1] } diff --git a/testdata/funalias_02.accepts b/testdata/funalias_02.accepts new file mode 100644 index 0000000..5f89401 --- /dev/null +++ b/testdata/funalias_02.accepts @@ -0,0 +1,15 @@ +{ "A": [], "B": [] } +{ "A": [0], "B": [0] } +{ "A": [1], "B": [1] } +{ "A": [2], "B": [2] } +{ "A": [3], "B": [3] } +{ "A": [4], "B": [4] } +;; +{ "A": [0,0], "B": [0,0] } +{ "A": [1,0], "B": [1,0] } +{ "A": [0,1], "B": [0,1] } +{ "A": [1,1], "B": [1,1] } +;; +{ "A": [125,0], "B": [125,0] } +{ "A": [0,125], "B": [0,125] } +{ "A": [125,125], "B": [125,125] } diff --git a/testdata/funalias_02.lisp b/testdata/funalias_02.lisp new file mode 100644 index 0000000..7eec36d --- /dev/null +++ b/testdata/funalias_02.lisp @@ -0,0 +1,4 @@ +(defcolumns A B) +(defpurefun (eq x y) (- y x)) +(defunalias eq! eq) +(defconstraint test () (eq! A B)) diff --git a/testdata/funalias_02.rejects b/testdata/funalias_02.rejects new file mode 100644 index 0000000..7ec90d0 --- /dev/null +++ b/testdata/funalias_02.rejects @@ -0,0 +1,35 @@ +{ "A": [0], "B": [1] } +{ "A": [1], "B": [0] } +{ "A": [0], "B": [1] } +{ "A": [0], "B": [2] } +{ "A": [0], "B": [3] } +{ "A": [1], "B": [0] } +{ "A": [2], "B": [0] } +{ "A": [3], "B": [0] } +;; +{ "A": [0,0], "B": [0,1] } +{ "A": [1,0], "B": [0,0] } +{ "A": [0,0], "B": [1,0] } +{ "A": [0,1], "B": [0,0] } +{ "A": [0,0], "B": [1,1] } +{ "A": [1,1], "B": [0,0] } +{ "A": [1,0], "B": [0,1] } +{ "A": [0,1], "B": [1,0] } +;; +{ "A": [0,0], "B": [0,125] } +{ "A": [125,0], "B": [0,0] } +{ "A": [0,0], "B": [125,0] } +{ "A": [0,125], "B": [0,0] } +{ "A": [0,0], "B": [125,125] } +{ "A": [125,125], "B": [0,0] } +{ "A": [125,0], "B": [0,125] } +{ "A": [0,125], "B": [125,0] } +;; +{ "A": [65,65], "B": [65,65573234] } +{ "A": [65573234,65], "B": [65,65] } +{ "A": [65,65], "B": [65573234,65] } +{ "A": [65,65573234], "B": [65,65] } +{ "A": [65,65], "B": [65573234,65573234] } +{ "A": [65573234,65573234], "B": [65,65] } +{ "A": [65573234,65], "B": [65,65573234] } +{ "A": [65,65573234], "B": [65573234,65] } diff --git a/testdata/funalias_03.accepts b/testdata/funalias_03.accepts new file mode 100644 index 0000000..08fdcf8 --- /dev/null +++ b/testdata/funalias_03.accepts @@ -0,0 +1,25 @@ +{ "X": [], "Y": [] } +{ "X": [0], "Y": [0] } +{ "X": [1], "Y": [2] } +{ "X": [2], "Y": [4] } +{ "X": [3], "Y": [6] } +;; +{ "X": [0,0], "Y": [0,0] } +{ "X": [0,1], "Y": [0,2] } +{ "X": [0,2], "Y": [0,4] } +{ "X": [0,3], "Y": [0,6] } +;; +{ "X": [1,0], "Y": [2,0] } +{ "X": [1,1], "Y": [2,2] } +{ "X": [1,2], "Y": [2,4] } +{ "X": [1,3], "Y": [2,6] } +;; +{ "X": [2,0], "Y": [4,0] } +{ "X": [2,1], "Y": [4,2] } +{ "X": [2,2], "Y": [4,4] } +{ "X": [2,3], "Y": [4,6] } +;; +{ "X": [3,0], "Y": [6,0] } +{ "X": [3,1], "Y": [6,2] } +{ "X": [3,2], "Y": [6,4] } +{ "X": [3,3], "Y": [6,6] } diff --git a/testdata/funalias_03.lisp b/testdata/funalias_03.lisp new file mode 100644 index 0000000..2bdc67c --- /dev/null +++ b/testdata/funalias_03.lisp @@ -0,0 +1,6 @@ +(defcolumns X Y) +(defun (double x) (+ x x)) +(defpurefun (eq x y) (- x y)) +(defunalias times2 double) +;; Y == 2 * X +(defconstraint c1 () (eq Y (times2 X))) diff --git a/testdata/funalias_03.rejects b/testdata/funalias_03.rejects new file mode 100644 index 0000000..11c9332 --- /dev/null +++ b/testdata/funalias_03.rejects @@ -0,0 +1,9 @@ +{ "X": [1], "Y": [0] } +{ "X": [1], "Y": [3] } +{ "X": [2], "Y": [3] } +{ "X": [3], "Y": [5] } +;; +{ "X": [0,1], "Y": [0,0] } +{ "X": [0,0], "Y": [0,2] } +{ "X": [0,3], "Y": [0,4] } +{ "X": [0,2], "Y": [0,6] } diff --git a/testdata/funalias_invalid_01.lisp b/testdata/funalias_invalid_01.lisp new file mode 100644 index 0000000..7a576a5 --- /dev/null +++ b/testdata/funalias_invalid_01.lisp @@ -0,0 +1,2 @@ +(defcolumns X) +(defunalias INC X) diff --git a/testdata/funalias_invalid_02.lisp b/testdata/funalias_invalid_02.lisp new file mode 100644 index 0000000..7dacf52 --- /dev/null +++ b/testdata/funalias_invalid_02.lisp @@ -0,0 +1,2 @@ +(defconst X 1) +(defunalias INC X) diff --git a/testdata/funalias_invalid_03.lisp b/testdata/funalias_invalid_03.lisp new file mode 100644 index 0000000..ad4c8f8 --- /dev/null +++ b/testdata/funalias_invalid_03.lisp @@ -0,0 +1 @@ +(defunalias X Y) diff --git a/testdata/funalias_invalid_04.lisp b/testdata/funalias_invalid_04.lisp new file mode 100644 index 0000000..e7fb78e --- /dev/null +++ b/testdata/funalias_invalid_04.lisp @@ -0,0 +1 @@ +(defunalias X X)