Skip to content

Commit 71ead2d

Browse files
committed
fix: use global handle checking
Previously, the algorithm for checking that two constraints do not have the same handle (in the same module) only worked when the constraints were defined within the same module block. However, if they were defined in different blocks (e.g. in different files) the duplicate handle was not reported as an error. To resolve this, a global map of all handles is now maintained which is shared across all source files and module blocks.
1 parent 7d2741c commit 71ead2d

File tree

3 files changed

+66
-61
lines changed

3 files changed

+66
-61
lines changed

pkg/corset/compiler/parser.go

Lines changed: 64 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,22 @@ import (
4242
// Thus, you should never expect to see duplicate module names in the returned
4343
// array.
4444
func ParseSourceFiles(files []source.File, enforceTypes bool) (ast.Circuit, *source.Maps[ast.Node], []SyntaxError) {
45-
var circuit ast.Circuit
46-
// (for now) at most one error per source file is supported.
47-
var errors []SyntaxError
48-
// Construct an initially empty source map
49-
srcmaps := source.NewSourceMaps[ast.Node]()
50-
// Contents map holds the combined fragments of each module.
51-
contents := make(map[string]ast.Module, 0)
52-
// Names identifies the names of each unique module.
53-
names := make([]string, 0)
45+
var (
46+
circuit ast.Circuit
47+
// (for now) at most one error per source file is supported.
48+
errors []SyntaxError
49+
// Construct an initially empty source map
50+
srcmaps = source.NewSourceMaps[ast.Node]()
51+
// Contents map holds the combined fragments of each module.
52+
contents = make(map[string]ast.Module, 0)
53+
// Names identifies the names of each unique module.
54+
names = make([]string, 0)
55+
// Handles used for detecting duplicate constraint handles
56+
handles = make(map[string]bool)
57+
)
5458
//
5559
for _, file := range files {
56-
c, srcmap, errs := ParseSourceFile(file, enforceTypes)
60+
c, srcmap, errs := parseSourceFile(file, enforceTypes, handles)
5761
// Handle errors
5862
if len(errs) > 0 {
5963
// Report any errors encountered
@@ -94,10 +98,9 @@ func ParseSourceFiles(files []source.File, enforceTypes bool) (ast.Circuit, *sou
9498
return circuit, srcmaps, nil
9599
}
96100

97-
// ParseSourceFile parses the contents of a single lisp file into one or more
98-
// modules. Observe that every lisp file starts in the "prelude" or "root"
99-
// module, and may declare items for additional modules as necessary.
100-
func ParseSourceFile(srcfile source.File, enforceTypes bool) (ast.Circuit, *source.Map[ast.Node], []SyntaxError) {
101+
func parseSourceFile(srcfile source.File, enforceTypes bool,
102+
handles map[string]bool) (ast.Circuit, *source.Map[ast.Node], []SyntaxError) {
103+
//
101104
//
102105
var (
103106
circuit ast.Circuit
@@ -111,7 +114,7 @@ func ParseSourceFile(srcfile source.File, enforceTypes bool) (ast.Circuit, *sour
111114
return circuit, nil, []SyntaxError{*err}
112115
}
113116
// Construct parser for corset syntax
114-
p := NewParser(srcfile, srcmap, enforceTypes)
117+
p := NewParser(srcfile, srcmap, enforceTypes, handles)
115118
// Parse whatever is declared at the beginning of the file before the first
116119
// module declaration. These declarations form part of the "prelude".
117120
if circuit.Declarations, terms, errors = p.parseModuleContents(path, terms); len(errors) > 0 {
@@ -150,6 +153,8 @@ func ParseSourceFile(srcfile source.File, enforceTypes bool) (ast.Circuit, *sour
150153
// ensuring that expressions are well-typed, etc) --- that is left up to the
151154
// compiler.
152155
type Parser struct {
156+
// Handles map used to detect duplicate handles
157+
handles map[string]bool
153158
// Translator used for recursive expressions.
154159
translator *sexp.Translator[ast.Expr]
155160
// Mapping from constructed S-Expressions to their spans in the original text.
@@ -160,12 +165,14 @@ type Parser struct {
160165

161166
// NewParser constructs a new parser using a given mapping from S-Expressions to
162167
// spans in the underlying source file.
163-
func NewParser(srcfile source.File, srcmap *source.Map[sexp.SExp], enforceTypes bool) *Parser {
168+
func NewParser(srcfile source.File, srcmap *source.Map[sexp.SExp], enforceTypes bool, handles map[string]bool,
169+
) *Parser {
170+
//
164171
p := sexp.NewTranslator[ast.Expr](&srcfile, srcmap)
165172
// Construct (initially empty) node map
166173
nodemap := source.NewSourceMap[ast.Node](srcmap.Source())
167174
// Construct parser
168-
parser := &Parser{p, nodemap, enforceTypes}
175+
parser := &Parser{handles, p, nodemap, enforceTypes}
169176
// Configure expression translator
170177
p.AddSymbolRule(constantParserRule)
171178
p.AddSymbolRule(varAccessParserRule)
@@ -215,9 +222,8 @@ func (p *Parser) parseModuleContents(path file.Path, terms []sexp.SExp) ([]ast.D
215222
[]SyntaxError) {
216223
//
217224
var (
218-
errors []SyntaxError
219-
handles = make(map[string]bool)
220-
decls = make([]ast.Declaration, 0)
225+
errors []SyntaxError
226+
decls = make([]ast.Declaration, 0)
221227
)
222228
//
223229
for i, s := range terms {
@@ -228,7 +234,7 @@ func (p *Parser) parseModuleContents(path file.Path, terms []sexp.SExp) ([]ast.D
228234
errors = append(errors, *err)
229235
} else if e.MatchSymbols(2, "module") {
230236
return decls, terms[i:], errors
231-
} else if decl, errs := p.parseDeclaration(path, e, handles); len(errs) > 0 {
237+
} else if decl, errs := p.parseDeclaration(path, e); len(errs) > 0 {
232238
errors = append(errors, errs...)
233239
} else {
234240
// Continue accumulating declarations for this module.
@@ -268,8 +274,7 @@ func (p *Parser) parseModuleStart(s sexp.SExp) (string, []SyntaxError) {
268274
return name, errors
269275
}
270276

271-
func (p *Parser) parseDeclaration(module file.Path, s *sexp.List,
272-
handles map[string]bool) (ast.Declaration, []SyntaxError) {
277+
func (p *Parser) parseDeclaration(module file.Path, s *sexp.List) (ast.Declaration, []SyntaxError) {
273278
//
274279
var (
275280
decl ast.Declaration
@@ -285,7 +290,7 @@ func (p *Parser) parseDeclaration(module file.Path, s *sexp.List,
285290
} else if s.Len() > 1 && s.MatchSymbols(1, "defconst") {
286291
decl, errors = p.parseDefConst(module, s.Elements)
287292
} else if s.Len() == 4 && s.MatchSymbols(2, "defconstraint") {
288-
decl, errors = p.parseDefConstraint(module, s.Elements, handles)
293+
decl, errors = p.parseDefConstraint(module, s.Elements)
289294
} else if s.Len() == 3 && s.MatchSymbols(1, "defpurefun") {
290295
decl, errors = p.parseDefFun(module, true, s.Elements)
291296
} else if s.Len() == 3 && s.MatchSymbols(1, "defun") {
@@ -295,21 +300,21 @@ func (p *Parser) parseDeclaration(module file.Path, s *sexp.List,
295300
} else if s.Len() == 3 && s.MatchSymbols(1, "definterleaved") {
296301
decl, errors = p.parseDefInterleaved(module, s.Elements)
297302
} else if s.Len() == 4 && s.MatchSymbols(1, "deflookup") {
298-
decl, errors = p.parseDefLookup(s.Elements, handles)
303+
decl, errors = p.parseDefLookup(module, s.Elements)
299304
} else if (s.Len() == 5 || s.Len() == 6) && s.MatchSymbols(1, "defclookup") {
300-
decl, errors = p.parseDefConditionalLookup(s.Elements, handles)
305+
decl, errors = p.parseDefConditionalLookup(module, s.Elements)
301306
} else if s.Len() == 4 && s.MatchSymbols(1, "defmlookup") {
302-
decl, errors = p.parseDefMultiLookup(s.Elements, handles)
307+
decl, errors = p.parseDefMultiLookup(module, s.Elements)
303308
} else if s.Len() == 3 && s.MatchSymbols(2, "defpermutation") {
304309
decl, errors = p.parseDefPermutation(module, s.Elements)
305310
} else if s.Len() == 4 && s.MatchSymbols(2, "defperspective") {
306311
decl, errors = p.parseDefPerspective(module, s.Elements)
307312
} else if 3 <= s.Len() && s.Len() <= 4 && s.MatchSymbols(2, "defproperty") {
308-
decl, errors = p.parseDefProperty(s.Elements, handles)
313+
decl, errors = p.parseDefProperty(module, s.Elements)
309314
} else if s.Len() == 3 && s.MatchSymbols(2, "defsorted") {
310-
decl, errors = p.parseDefSorted(false, s.Elements, handles)
315+
decl, errors = p.parseDefSorted(module, false, s.Elements)
311316
} else if 3 <= s.Len() && s.Len() <= 4 && s.MatchSymbols(2, "defstrictsorted") {
312-
decl, errors = p.parseDefSorted(true, s.Elements, handles)
317+
decl, errors = p.parseDefSorted(module, true, s.Elements)
313318
} else if s.Len() == 3 && s.MatchSymbols(1, "defcomputedcolumn") {
314319
decl, errors = p.parseDefComputedColumn(module, s.Elements)
315320
} else {
@@ -761,23 +766,20 @@ func (p *Parser) parseDefConstHead(head sexp.SExp) (*sexp.Symbol, ast.Type, bool
761766
}
762767

763768
// Parse a vanishing declaration
764-
func (p *Parser) parseDefConstraint(module file.Path, elements []sexp.SExp,
765-
handles map[string]bool) (ast.Declaration, []SyntaxError) {
766-
var (
767-
errors []SyntaxError
768-
handle string
769-
)
769+
func (p *Parser) parseDefConstraint(module file.Path, elements []sexp.SExp) (ast.Declaration, []SyntaxError) {
770+
var errors []SyntaxError
770771
// Initial sanity checks
771772
if !isIdentifier(elements[1]) {
772773
return nil, p.translator.SyntaxErrors(elements[1], "invalid constraint handle")
773-
} else {
774-
handle = elements[1].AsSymbol().Value
775774
}
775+
//
776+
handle := elements[1].AsSymbol().Value
777+
qualifiedHandle := fmt.Sprintf("%s:%s", module.String(), handle)
776778
// Check for duplicate
777-
if _, ok := handles[handle]; ok {
779+
if _, ok := p.handles[qualifiedHandle]; ok {
778780
return nil, p.translator.SyntaxErrors(elements[1], "duplicate handle")
779781
} else {
780-
handles[handle] = true
782+
p.handles[qualifiedHandle] = true
781783
}
782784
// Vanishing constraints do not have global scope, hence qualified column
783785
// accesses are not permitted.
@@ -869,9 +871,9 @@ func (p *Parser) parseDefInterleavedSourceArray(source *sexp.Array) (ast.TypedSy
869871
}
870872

871873
// Parse a lookup declaration
872-
func (p *Parser) parseDefLookup(elements []sexp.SExp, handles map[string]bool) (ast.Declaration, []SyntaxError) {
874+
func (p *Parser) parseDefLookup(module file.Path, elements []sexp.SExp) (ast.Declaration, []SyntaxError) {
873875
// Extract items
874-
handle, checked, errors := p.parseLookupHandle(elements[1], handles)
876+
handle, checked, errors := p.parseLookupHandle(module, elements[1])
875877
targets, tgtErrors := p.parseDefLookupSources("target", elements[2])
876878
sources, srcErrors := p.parseDefLookupSources("source", elements[3])
877879
// Combine any and all errors
@@ -897,16 +899,15 @@ func (p *Parser) parseDefLookup(elements []sexp.SExp, handles map[string]bool) (
897899
}
898900

899901
// Parse a conditional lookup declaration
900-
func (p *Parser) parseDefConditionalLookup(elements []sexp.SExp,
901-
handles map[string]bool) (ast.Declaration, []SyntaxError) {
902+
func (p *Parser) parseDefConditionalLookup(module file.Path, elements []sexp.SExp) (ast.Declaration, []SyntaxError) {
902903
// Extract items
903904
var (
904905
targets, sources []ast.Expr
905906
targetSelector, sourceSelector ast.Expr
906907
errs1, errs2, errs3, errs4 []SyntaxError
907908
)
908909
//
909-
handle, checked, errors := p.parseLookupHandle(elements[1], handles)
910+
handle, checked, errors := p.parseLookupHandle(module, elements[1])
910911
//
911912
if len(elements) == 6 {
912913
targetSelector, errs1 = p.translator.Translate(elements[2])
@@ -942,9 +943,9 @@ func (p *Parser) parseDefConditionalLookup(elements []sexp.SExp,
942943
[][]ast.Expr{targets}), nil
943944
}
944945

945-
func (p *Parser) parseDefMultiLookup(elements []sexp.SExp, handles map[string]bool) (ast.Declaration, []SyntaxError) {
946+
func (p *Parser) parseDefMultiLookup(module file.Path, elements []sexp.SExp) (ast.Declaration, []SyntaxError) {
946947
// Extract items
947-
handle, checked, errors := p.parseLookupHandle(elements[1], handles)
948+
handle, checked, errors := p.parseLookupHandle(module, elements[1])
948949
m, targets, tgtErrors := p.parseDefLookupMultiSources("target", elements[2])
949950
n, sources, srcErrors := p.parseDefLookupMultiSources("source", elements[3])
950951
// Combine any and all errors
@@ -966,7 +967,7 @@ func (p *Parser) parseDefMultiLookup(elements []sexp.SExp, handles map[string]bo
966967
return ast.NewDefLookup(handle, checked, sourceSelectors, sources, targetSelectors, targets), nil
967968
}
968969

969-
func (p *Parser) parseLookupHandle(element sexp.SExp, handles map[string]bool) (string, bool, []SyntaxError) {
970+
func (p *Parser) parseLookupHandle(module file.Path, element sexp.SExp) (string, bool, []SyntaxError) {
970971
var (
971972
checked = true
972973
errors []SyntaxError
@@ -981,12 +982,14 @@ func (p *Parser) parseLookupHandle(element sexp.SExp, handles map[string]bool) (
981982
} else {
982983
handle = element.AsSymbol().Value
983984
}
985+
//
986+
qualifiedHandle := fmt.Sprintf("%s:%s", module.String(), handle)
984987
// Check for duplicate handle
985-
if _, ok := handles[handle]; ok {
988+
if _, ok := p.handles[qualifiedHandle]; ok {
986989
return "", checked, p.translator.SyntaxErrors(element, "duplicate handle")
987990
} else if len(errors) == 0 {
988991
// Done
989-
handles[handle] = true
992+
p.handles[qualifiedHandle] = true
990993
}
991994
//
992995
return handle, checked, errors
@@ -1149,8 +1152,7 @@ func (p *Parser) parseDefPermutation(module file.Path, elements []sexp.SExp) (as
11491152
}
11501153

11511154
// Parse a permutation declaration
1152-
func (p *Parser) parseDefSorted(strict bool, elements []sexp.SExp,
1153-
handles map[string]bool) (ast.Declaration, []SyntaxError) {
1155+
func (p *Parser) parseDefSorted(module file.Path, strict bool, elements []sexp.SExp) (ast.Declaration, []SyntaxError) {
11541156
//
11551157
var (
11561158
selector util.Option[ast.Expr]
@@ -1159,7 +1161,6 @@ func (p *Parser) parseDefSorted(strict bool, elements []sexp.SExp,
11591161
sexpSourcesElement sexp.SExp
11601162
sexpSources *sexp.List
11611163
signs []bool
1162-
handle string
11631164
)
11641165
//
11651166
if len(elements) == 3 {
@@ -1179,15 +1180,16 @@ func (p *Parser) parseDefSorted(strict bool, elements []sexp.SExp,
11791180
// Check Handle
11801181
if !isIdentifier(elements[1]) {
11811182
errors = append(errors, *p.translator.SyntaxError(elements[1], "malformed handle"))
1182-
} else {
1183-
handle = elements[1].AsSymbol().Value
11841183
}
1184+
//
1185+
handle := elements[1].AsSymbol().Value
1186+
qualifiedHandle := fmt.Sprintf("%s:%s", module.String(), handle)
11851187
// Check for duplicate handle
1186-
if _, ok := handles[handle]; ok {
1188+
if _, ok := p.handles[qualifiedHandle]; ok {
11871189
return nil, p.translator.SyntaxErrors(elements[1], "duplicate handle")
11881190
} else if len(errors) == 0 {
11891191
// Record handle
1190-
handles[handle] = true
1192+
p.handles[qualifiedHandle] = true
11911193
}
11921194
// Check source Expressions
11931195
if sexpSources == nil {
@@ -1327,7 +1329,7 @@ func (p *Parser) parseDefPerspective(module file.Path, elements []sexp.SExp) (as
13271329
}
13281330

13291331
// Parse a property assertion
1330-
func (p *Parser) parseDefProperty(elements []sexp.SExp, handles map[string]bool) (ast.Declaration, []SyntaxError) {
1332+
func (p *Parser) parseDefProperty(module file.Path, elements []sexp.SExp) (ast.Declaration, []SyntaxError) {
13311333
var (
13321334
errors []SyntaxError
13331335
assertion int = len(elements) - 1
@@ -1341,12 +1343,14 @@ func (p *Parser) parseDefProperty(elements []sexp.SExp, handles map[string]bool)
13411343
// Extract handle
13421344
handle = elements[1].AsSymbol().Value
13431345
}
1346+
// Generate qualified name
1347+
qualifiedHandle := fmt.Sprintf("%s:%s", module.String(), handle)
13441348
// Check for duplicate handle
1345-
if _, ok := handles[handle]; ok {
1349+
if _, ok := p.handles[qualifiedHandle]; ok {
13461350
return nil, p.translator.SyntaxErrors(elements[1], "duplicate handle")
13471351
} else if len(errors) == 0 {
13481352
// Record handle
1349-
handles[handle] = true
1353+
p.handles[qualifiedHandle] = true
13501354
}
13511355
// Check for any attributes
13521356
if len(elements) > 3 {

testdata/corset/invalid/basic_invalid_20.lisp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
;;
1+
;;error:8:16-20:duplicate handle
22
(module m1)
33

44
(defcolumns (X :i16) (Y :i16))

testdata/corset/invalid/lookup_invalid_19.lisp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
;;error:7:13-17:duplicate handle
12
(module m1)
23
(defcolumns (X :i16) (Y :i16))
34
(defclookup test (Y) 1 (X))

0 commit comments

Comments
 (0)