Skip to content

Commit

Permalink
Enable name resolution
Browse files Browse the repository at this point in the history
This adds support for name resolution, which allows names to be
referenced out-of-order in a given source file.
  • Loading branch information
DavePearce committed Nov 25, 2024
1 parent b52abec commit a919667
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 125 deletions.
1 change: 0 additions & 1 deletion pkg/cmd/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ var debugCmd = &cobra.Command{
stats := GetFlag(cmd, "stats")
// Parse constraints
hirSchema := readSchema(args)

// Print constraints
if stats {
printStats(hirSchema, hir, mir, air)
Expand Down
7 changes: 2 additions & 5 deletions pkg/cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func readSourceFiles(filenames []string) *hir.Schema {
// Parse and compile source files
schema, errs := corset.CompileSourceFiles(srcfiles)
// Check for any errors
if errs == nil {
if len(errs) == 0 {
return schema
}
// Report errors
Expand All @@ -201,10 +201,7 @@ func readSourceFiles(filenames []string) *hir.Schema {
// Print a syntax error with appropriate highlighting.
func printSyntaxError(err *sexp.SyntaxError) {
span := err.Span()
// Construct empty source map in order to determine enclosing line.
srcmap := sexp.NewSourceMap[sexp.SExp](err.SourceFile().Contents())
//
line := srcmap.FindFirstEnclosingLine(span)
line := err.FirstEnclosingLine()
// Print error + line number
fmt.Printf("%s:%d: %s\n", err.SourceFile().Filename(), line.Number(), err.Message())
// Print separator line
Expand Down
7 changes: 4 additions & 3 deletions pkg/corset/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Declaration interface {

// DefColumns captures a set of one or more columns being declared.
type DefColumns struct {
Columns []DefColumn
Columns []*DefColumn
}

// Resolve something.
Expand All @@ -58,8 +58,9 @@ func (p *DefColumns) Lisp() sexp.SExp {
// DefColumn packages together those piece relevant to declaring an individual
// column, such its name and type.
type DefColumn struct {
Name string
DataType sc.Type
Name string
DataType sc.Type
LengthMultiplier uint
}

// Lisp converts this node into its lisp representation. This is primarily used
Expand Down
16 changes: 10 additions & 6 deletions pkg/corset/compiler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package corset

import (
"fmt"

"github.com/consensys/go-corset/pkg/hir"
"github.com/consensys/go-corset/pkg/sexp"
)
Expand Down Expand Up @@ -61,14 +63,16 @@ func NewCompiler(circuit Circuit, srcmaps *sexp.SourceMaps[Node]) *Compiler {
// expression refers to a non-existent module or column, or is not well-typed,
// etc.
func (p *Compiler) Compile() (*hir.Schema, []SyntaxError) {
schema := hir.EmptySchema()
// Allocate columns?
//
// Resolve variables (via nested scopes)
env, errs := ResolveCircuit(p.srcmap, &p.circuit)
// Check whether any errors were encountered. If so, terminate since we
// cannot proceed with translation.
if len(errs) != 0 {
return nil, errs
}
// Check constraint contexts (e.g. for constraints, lookups, etc)
// Type check constraints
fmt.Println("Translating circuit...")
// Finally, translate everything and add it to the schema.
errors := translateCircuit(&p.circuit, schema)
// Done
return schema, errors
return TranslateCircuit(env, p.srcmap, &p.circuit)
}
134 changes: 134 additions & 0 deletions pkg/corset/environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package corset

import (
"fmt"

"github.com/consensys/go-corset/pkg/schema"
"github.com/consensys/go-corset/pkg/trace"
)

// ===================================================================
// Environment
// ===================================================================

// Identifies a specific column within the environment.
type colRef struct {
module uint
column string
}

// Packages up information about a declared column (either input or assignment).
type colInfo struct {
// Column index
cid uint
// Length multiplier
multiplier uint
// Datatype
datatype schema.Type
}

// Environment maps module and column names to their (respective) module and
// column indices. The environment separates input columns from assignment
// columns because they are disjoint in the schema being constructed (i.e. input
// columns always have a lower index than assignments).
type Environment struct {
// Maps module names to their module indices.
modules map[string]uint
// Maps input columns to their column indices.
columns map[colRef]colInfo
}

// EmptyEnvironment constructs an empty environment.
func EmptyEnvironment() *Environment {
modules := make(map[string]uint)
columns := make(map[colRef]colInfo)
//
return &Environment{modules, columns}
}

// RegisterModule registers a new module within this environment. Observe that
// this will panic if the module already exists. Furthermore, the module
// identifier is always determined as the next available identifier.
func (p *Environment) RegisterModule(module string) trace.Context {
if p.HasModule(module) {
panic(fmt.Sprintf("module %s already exists", module))
}
// Update schema
mid := uint(len(p.modules))
// Update cache
p.modules[module] = mid
// Done
return trace.NewContext(mid, 1)
}

// RegisterColumn registers a new column (input or assignment) within a given
// module. Observe that this will panic if the column already exists.
// Furthermore, the column identifier is always determined as the next available
// identifier. Hence, care must be taken when declaring columns to ensure they
// are allocated in the right order.
func (p *Environment) RegisterColumn(context trace.Context, column string, datatype schema.Type) uint {
if p.HasColumn(context.Module(), column) {
panic(fmt.Sprintf("column %d:%s already exists", context.Module(), column))
}
// Update cache
cid := uint(len(p.columns))
cref := colRef{context.Module(), column}
p.columns[cref] = colInfo{cid, context.LengthMultiplier(), datatype}
// Done
return cid
}

// LookupModule determines the module index for a given named module, or return
// false if no such module exists.
func (p *Environment) LookupModule(module string) (uint, bool) {
mid, ok := p.modules[module]
return mid, ok
}

// LookupColumn determines the column index for a given named column in a given
// module, or return false if no such column exists.
func (p *Environment) LookupColumn(module uint, column string) (uint, bool) {
cref := colRef{module, column}
cinfo, ok := p.columns[cref]

return cinfo.cid, ok
}

// Module determines the module index for a given module. This assumes the
// module exists, and will panic otherwise.
func (p *Environment) Module(module string) uint {
ctx, ok := p.LookupModule(module)
// Sanity check we found something
if !ok {
panic(fmt.Sprintf("unknown module %s", module))
}
// Discard column index
return ctx
}

// Column determines the column index for a given column declared in a given
// module. This assumes the column / module exist, and will panic otherwise.
func (p *Environment) Column(module uint, column string) uint {
// FIXME: doesn't make sense using context here.
cid, ok := p.LookupColumn(module, column)
// Sanity check we found something
if !ok {
panic(fmt.Sprintf("unknown column %s", column))
}
// Discard column index
return cid
}

// HasModule checks whether a given module exists, or not.
func (p *Environment) HasModule(module string) bool {
_, ok := p.LookupModule(module)
// Discard column index
return ok
}

// HasColumn checks whether a given module has a given column, or not.
func (p *Environment) HasColumn(module uint, column string) bool {
_, ok := p.LookupColumn(module, column)
// Discard column index
return ok
}
47 changes: 39 additions & 8 deletions pkg/corset/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func ParseSourceFile(srcfile *sexp.SourceFile) (Circuit, *sexp.SourceMap[Node],
}
}
// Done
return circuit, p.nodemap, nil
return circuit, p.NodeMap(), nil
}

// Parser implements a simple parser for the Corset language. The parser itself
Expand All @@ -137,7 +137,7 @@ type Parser struct {
func NewParser(srcfile *sexp.SourceFile, srcmap *sexp.SourceMap[sexp.SExp]) *Parser {
p := sexp.NewTranslator[Expr](srcfile, srcmap)
// Construct (initially empty) node map
nodemap := sexp.NewSourceMap[Node](srcmap.Text())
nodemap := sexp.NewSourceMap[Node](srcmap.Source())
// Construct parser
parser := &Parser{p, nodemap}
// Configure expression translator
Expand All @@ -155,6 +155,23 @@ func NewParser(srcfile *sexp.SourceFile, srcmap *sexp.SourceMap[sexp.SExp]) *Par
return parser
}

// NodeMap extract the node map constructec by this parser. A key task here is
// to copy all mappings from the expression translator, which maintains its own
// map.
func (p *Parser) NodeMap() *sexp.SourceMap[Node] {
// Copy all mappings from translator's source map into this map. A mapping
// function is required to coerce the types.
sexp.JoinMaps(p.nodemap, p.translator.SourceMap(), func(e Expr) Node { return e })
// Done
return p.nodemap
}

// Register a source mapping from a given S-Expression to a given target node.
func (p *Parser) mapSourceNode(from sexp.SExp, to Node) {
span := p.translator.SpanOf(from)
p.nodemap.Put(to, span)
}

// Extract all declarations associated with a given module and package them up.
func (p *Parser) parseModuleContents(terms []sexp.SExp) ([]Declaration, []sexp.SExp, *SyntaxError) {
//
Expand Down Expand Up @@ -201,10 +218,17 @@ func (p *Parser) parseModuleStart(s sexp.SExp) (string, *SyntaxError) {
}

func (p *Parser) parseDeclaration(s *sexp.List) (Declaration, *SyntaxError) {
var (
decl Declaration
error *SyntaxError
)
//
if s.MatchSymbols(1, "defcolumns") {
return p.parseColumnDeclarations(s)
decl, error = p.parseColumnDeclarations(s)
} else if s.Len() == 4 && s.MatchSymbols(2, "defconstraint") {
return p.parseConstraintDeclaration(s.Elements)
decl, error = p.parseConstraintDeclaration(s.Elements)
} else {
error = p.translator.SyntaxError(s, "malformed declaration")
}
/*
else if e.Len() == 3 && e.MatchSymbols(2, "assert") {
Expand All @@ -220,12 +244,17 @@ func (p *Parser) parseDeclaration(s *sexp.List) (Declaration, *SyntaxError) {
} else if e.Len() == 3 && e.MatchSymbols(1, "defpurefun") {
return p.parsePureFunDeclaration(env, e)
} */
return nil, p.translator.SyntaxError(s, "malformed declaration")
// Register node if appropriate
if decl != nil {
p.mapSourceNode(s, decl)
}
// done
return decl, error
}

// Parse a column declaration
func (p *Parser) parseColumnDeclarations(l *sexp.List) (*DefColumns, *SyntaxError) {
columns := make([]DefColumn, l.Len()-1)
columns := make([]*DefColumn, l.Len()-1)
// Sanity check declaration
if len(l.Elements) == 1 {
return nil, p.translator.SyntaxError(l, "malformed column declaration")
Expand All @@ -244,8 +273,8 @@ func (p *Parser) parseColumnDeclarations(l *sexp.List) (*DefColumns, *SyntaxErro
return &DefColumns{columns}, nil
}

func (p *Parser) parseColumnDeclaration(e sexp.SExp) (DefColumn, *SyntaxError) {
var defcolumn DefColumn
func (p *Parser) parseColumnDeclaration(e sexp.SExp) (*DefColumn, *SyntaxError) {
defcolumn := &DefColumn{"", nil, 1}
// Default to field type
defcolumn.DataType = &sc.FieldType{}
// Check whether extended declaration or not.
Expand All @@ -269,6 +298,8 @@ func (p *Parser) parseColumnDeclaration(e sexp.SExp) (DefColumn, *SyntaxError) {
} else {
defcolumn.Name = e.String(false)
}
// Update source mapping
p.mapSourceNode(e, defcolumn)
//
return defcolumn, nil
}
Expand Down
Loading

0 comments on commit a919667

Please sign in to comment.