Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement defunalias #428

Merged
merged 1 commit into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions pkg/corset/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand All @@ -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")
}
Expand All @@ -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)
}

Expand All @@ -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
Expand Down Expand Up @@ -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,
})
}

Expand Down
20 changes: 11 additions & 9 deletions pkg/corset/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down Expand Up @@ -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
Expand All @@ -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)
//
Expand All @@ -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
Expand Down Expand Up @@ -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])
}
//
Expand Down Expand Up @@ -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.
Expand All @@ -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)
Expand Down
19 changes: 19 additions & 0 deletions pkg/test/invalid_corset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
// ===================================================================
Expand Down
15 changes: 15 additions & 0 deletions pkg/test/valid_corset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
// ===================================================================
Expand Down
6 changes: 6 additions & 0 deletions testdata/funalias_01.accepts
Original file line number Diff line number Diff line change
@@ -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] }
4 changes: 4 additions & 0 deletions testdata/funalias_01.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(defcolumns A)
(defpurefun (id x) x)
(defunalias ID id)
(defconstraint test () (ID A))
5 changes: 5 additions & 0 deletions testdata/funalias_01.rejects
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{ "A": [1] }
{ "A": [2] }
{ "A": [1,1] }
{ "A": [1,1] }
{ "A": [2,1] }
15 changes: 15 additions & 0 deletions testdata/funalias_02.accepts
Original file line number Diff line number Diff line change
@@ -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] }
4 changes: 4 additions & 0 deletions testdata/funalias_02.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(defcolumns A B)
(defpurefun (eq x y) (- y x))
(defunalias eq! eq)
(defconstraint test () (eq! A B))
35 changes: 35 additions & 0 deletions testdata/funalias_02.rejects
Original file line number Diff line number Diff line change
@@ -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] }
25 changes: 25 additions & 0 deletions testdata/funalias_03.accepts
Original file line number Diff line number Diff line change
@@ -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] }
6 changes: 6 additions & 0 deletions testdata/funalias_03.lisp
Original file line number Diff line number Diff line change
@@ -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)))
9 changes: 9 additions & 0 deletions testdata/funalias_03.rejects
Original file line number Diff line number Diff line change
@@ -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] }
2 changes: 2 additions & 0 deletions testdata/funalias_invalid_01.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(defcolumns X)
(defunalias INC X)
2 changes: 2 additions & 0 deletions testdata/funalias_invalid_02.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(defconst X 1)
(defunalias INC X)
1 change: 1 addition & 0 deletions testdata/funalias_invalid_03.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(defunalias X Y)
1 change: 1 addition & 0 deletions testdata/funalias_invalid_04.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(defunalias X X)
Loading