arnaucube
arnaucube/go-snarkPublic Self-hosted repository
go-snark/circuitcompiler/parser.go
circuitcompiler/parser.goRaw
  1package circuitcompiler
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"io"
  7	"os"
  8	"regexp"
  9	"strings"
 10)
 11
 12// Parser data structure holds the Scanner and the Parsing functions
 13type Parser struct {
 14	s   *Scanner
 15	buf struct {
 16		tok Token  // last read token
 17		lit string // last read literal
 18		n   int    // buffer size (max=1)
 19	}
 20}
 21
 22// NewParser creates a new parser from a io.Reader
 23func NewParser(r io.Reader) *Parser {
 24	return &Parser{s: NewScanner(r)}
 25}
 26
 27func (p *Parser) scan() (tok Token, lit string) {
 28	// if there is a token in the buffer return it
 29	if p.buf.n != 0 {
 30		p.buf.n = 0
 31		return p.buf.tok, p.buf.lit
 32	}
 33	tok, lit = p.s.scan()
 34
 35	p.buf.tok, p.buf.lit = tok, lit
 36
 37	return
 38}
 39
 40func (p *Parser) unscan() {
 41	p.buf.n = 1
 42}
 43
 44func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) {
 45	tok, lit = p.scan()
 46	if tok == WS {
 47		tok, lit = p.scan()
 48	}
 49	return
 50}
 51
 52// parseLine parses the current line
 53func (p *Parser) parseLine() (*Constraint, error) {
 54	/*
 55		in this version,
 56		line will be for example s3 = s1 * s4
 57		this is:
 58		val eq val op val
 59	*/
 60	c := &Constraint{}
 61	tok, lit := p.scanIgnoreWhitespace()
 62	c.Out = lit
 63	c.Literal += lit
 64
 65	if c.Literal == "func" {
 66		// format: `func name(in):`
 67		line, err := p.s.r.ReadString(':')
 68		if err != nil {
 69			return c, err
 70		}
 71		// read string inside ( )
 72		rgx := regexp.MustCompile(`\((.*?)\)`)
 73		insideParenthesis := rgx.FindStringSubmatch(line)
 74		varsString := strings.Replace(insideParenthesis[1], " ", "", -1)
 75		allInputs := strings.Split(varsString, ",")
 76
 77		// from allInputs, get the private and the public separated
 78		for _, in := range allInputs {
 79			if strings.Contains(in, "private") {
 80				input := strings.Replace(in, "private", "", -1)
 81				c.PrivateInputs = append(c.PrivateInputs, input)
 82			} else if strings.Contains(in, "public") {
 83				input := strings.Replace(in, "public", "", -1)
 84				c.PublicInputs = append(c.PublicInputs, input)
 85			} else {
 86				// TODO give more info about the circuit code error
 87				fmt.Println("error on declaration of public and private inputs")
 88				os.Exit(0)
 89			}
 90		}
 91		return c, nil
 92	}
 93	if c.Literal == "equals" {
 94		// format: `equals(a, b)`
 95		line, err := p.s.r.ReadString(')')
 96		if err != nil {
 97			return c, err
 98		}
 99		// read string inside ( )
100		rgx := regexp.MustCompile(`\((.*?)\)`)
101		insideParenthesis := rgx.FindStringSubmatch(line)
102		varsString := strings.Replace(insideParenthesis[1], " ", "", -1)
103		params := strings.Split(varsString, ",")
104		c.V1 = params[0]
105		c.V2 = params[1]
106		return c, nil
107	}
108	// if c.Literal == "out" {
109	//         // TODO
110	//         return c, nil
111	// }
112
113	_, lit = p.scanIgnoreWhitespace() // skip =
114	c.Literal += lit
115
116	// v1
117	_, lit = p.scanIgnoreWhitespace()
118	c.V1 = lit
119	c.Literal += lit
120	// operator
121	_, lit = p.scanIgnoreWhitespace()
122	c.Op = lit
123	c.Literal += lit
124	// v2
125	_, lit = p.scanIgnoreWhitespace()
126	c.V2 = lit
127	c.Literal += lit
128	if tok == EOF {
129		return nil, errors.New("eof in parseline")
130	}
131	return c, nil
132}
133
134func existInArray(arr []string, elem string) bool {
135	for _, v := range arr {
136		if v == elem {
137			return true
138		}
139	}
140	return false
141}
142
143func addToArrayIfNotExist(arr []string, elem string) []string {
144	for _, v := range arr {
145		if v == elem {
146			return arr
147		}
148	}
149	arr = append(arr, elem)
150	return arr
151}
152
153// Parse parses the lines and returns the compiled Circuit
154func (p *Parser) Parse() (*Circuit, error) {
155	circuit := &Circuit{}
156	circuit.Signals = append(circuit.Signals, "one")
157	nInputs := 0
158	for {
159		constraint, err := p.parseLine()
160		if err != nil {
161			break
162		}
163		if constraint.Literal == "func" {
164			// one constraint for each input
165			for _, in := range constraint.PublicInputs {
166				newConstr := &Constraint{
167					Op:  "in",
168					Out: in,
169				}
170				circuit.Constraints = append(circuit.Constraints, *newConstr)
171				nInputs++
172				circuit.Signals = addToArrayIfNotExist(circuit.Signals, in)
173				circuit.NPublic++
174			}
175			for _, in := range constraint.PrivateInputs {
176				newConstr := &Constraint{
177					Op:  "in",
178					Out: in,
179				}
180				circuit.Constraints = append(circuit.Constraints, *newConstr)
181				nInputs++
182				circuit.Signals = addToArrayIfNotExist(circuit.Signals, in)
183			}
184			circuit.PublicInputs = constraint.PublicInputs
185			circuit.PrivateInputs = constraint.PrivateInputs
186			continue
187		}
188		if constraint.Literal == "equals" {
189			constr1 := &Constraint{
190				Op:      "*",
191				V1:      constraint.V2,
192				V2:      "1",
193				Out:     constraint.V1,
194				Literal: "equals(" + constraint.V1 + ", " + constraint.V2 + "): " + constraint.V1 + "==" + constraint.V2 + " * 1",
195			}
196			circuit.Constraints = append(circuit.Constraints, *constr1)
197			constr2 := &Constraint{
198				Op:      "*",
199				V1:      constraint.V1,
200				V2:      "1",
201				Out:     constraint.V2,
202				Literal: "equals(" + constraint.V1 + ", " + constraint.V2 + "): " + constraint.V2 + "==" + constraint.V1 + " * 1",
203			}
204			circuit.Constraints = append(circuit.Constraints, *constr2)
205			continue
206		}
207		circuit.Constraints = append(circuit.Constraints, *constraint)
208		isVal, _ := isValue(constraint.V1)
209		if !isVal {
210			circuit.Signals = addToArrayIfNotExist(circuit.Signals, constraint.V1)
211		}
212		isVal, _ = isValue(constraint.V2)
213		if !isVal {
214			circuit.Signals = addToArrayIfNotExist(circuit.Signals, constraint.V2)
215		}
216
217		// if constraint.Out == "out" {
218		// if Out is "out", put it after first value (one) and before the inputs
219		// if constraint.Out == circuit.PublicInputs[0] {
220		// if existInArray(circuit.PublicInputs, constraint.Out) {
221		//         // if Out is a public signal, put it after first value (one) and before the private inputs
222		//         if !existInArray(circuit.Signals, constraint.Out) {
223		//                 // if already don't exists in signal array
224		//                 signalsCopy := copyArray(circuit.Signals)
225		//                 var auxSignals []string
226		//                 auxSignals = append(auxSignals, signalsCopy[0])
227		//                 auxSignals = append(auxSignals, constraint.Out)
228		//                 auxSignals = append(auxSignals, signalsCopy[1:]...)
229		//                 circuit.Signals = auxSignals
230		//                 // circuit.PublicInputs = append(circuit.PublicInputs, constraint.Out)
231		//                 circuit.NPublic++
232		//         }
233		// } else {
234		circuit.Signals = addToArrayIfNotExist(circuit.Signals, constraint.Out)
235		// }
236	}
237	circuit.NVars = len(circuit.Signals)
238	circuit.NSignals = len(circuit.Signals)
239	return circuit, nil
240}
241func copyArray(in []string) []string { // tmp
242	var out []string
243	for _, e := range in {
244		out = append(out, e)
245	}
246	return out
247}