mirror of
https://github.com/arnaucube/go-snark-study.git
synced 2026-02-02 17:26:41 +01:00
starting circuitcompiler, lexer and parser (simple version)
This commit is contained in:
10
circuitcompiler/circuit.go
Normal file
10
circuitcompiler/circuit.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package circuitcompiler
|
||||
|
||||
type Circuit struct {
|
||||
NVars int
|
||||
NPublic int
|
||||
NSignals int
|
||||
Inputs []int
|
||||
Witness []int
|
||||
Constraints []Constraint
|
||||
}
|
||||
36
circuitcompiler/circuit_test.go
Normal file
36
circuitcompiler/circuit_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package circuitcompiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCircuitParser(t *testing.T) {
|
||||
/*
|
||||
input:
|
||||
def test():
|
||||
y = x**3
|
||||
return x + y + 5
|
||||
|
||||
flattened:
|
||||
m1 = s1 * s1
|
||||
m2 = m1 * s1
|
||||
m3 = m2 + s1
|
||||
out = m3 + 5
|
||||
*/
|
||||
raw := `
|
||||
y = x^x
|
||||
z = x + y
|
||||
out = z + 5
|
||||
`
|
||||
parser := NewParser(strings.NewReader(raw))
|
||||
res, err := parser.Parse()
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(res)
|
||||
|
||||
// flat code
|
||||
// flat code to R1CS
|
||||
}
|
||||
144
circuitcompiler/lexer.go
Normal file
144
circuitcompiler/lexer.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package circuitcompiler
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type OperatorSymbol int
|
||||
type Token int
|
||||
|
||||
const (
|
||||
ILLEGAL Token = iota
|
||||
WS
|
||||
EOF
|
||||
|
||||
IDENT // val
|
||||
|
||||
VAR // var
|
||||
CONST // const value
|
||||
|
||||
EQ // =
|
||||
PLUS // +
|
||||
MINUS // -
|
||||
MULTIPLY // *
|
||||
DIVIDE // /
|
||||
EXP // ^
|
||||
|
||||
OUT
|
||||
)
|
||||
|
||||
var eof = rune(0)
|
||||
|
||||
func isWhitespace(ch rune) bool {
|
||||
return ch == ' ' || ch == '\t' || ch == '\n'
|
||||
}
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||
}
|
||||
func isDigit(ch rune) bool {
|
||||
return (ch >= '0' && ch <= '9')
|
||||
}
|
||||
|
||||
type Scanner struct {
|
||||
r *bufio.Reader
|
||||
}
|
||||
|
||||
func NewScanner(r io.Reader) *Scanner {
|
||||
return &Scanner{r: bufio.NewReader(r)}
|
||||
}
|
||||
|
||||
func (s *Scanner) read() rune {
|
||||
ch, _, err := s.r.ReadRune()
|
||||
if err != nil {
|
||||
return eof
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *Scanner) unread() {
|
||||
_ = s.r.UnreadRune()
|
||||
}
|
||||
|
||||
func (s *Scanner) Scan() (tok Token, lit string) {
|
||||
ch := s.read()
|
||||
|
||||
if isWhitespace(ch) {
|
||||
// space
|
||||
s.unread()
|
||||
return s.scanWhitespace()
|
||||
} else if isLetter(ch) {
|
||||
// letter
|
||||
s.unread()
|
||||
return s.scanIndent()
|
||||
} else if isDigit(ch) {
|
||||
s.unread()
|
||||
return s.scanIndent()
|
||||
}
|
||||
|
||||
switch ch {
|
||||
case eof:
|
||||
return EOF, ""
|
||||
case '=':
|
||||
return EQ, "="
|
||||
case '+':
|
||||
return PLUS, "+"
|
||||
case '-':
|
||||
return MINUS, "-"
|
||||
case '*':
|
||||
return MULTIPLY, "*"
|
||||
case '/':
|
||||
return DIVIDE, "/"
|
||||
case '^':
|
||||
return EXP, "^"
|
||||
}
|
||||
|
||||
return ILLEGAL, string(ch)
|
||||
}
|
||||
|
||||
func (s *Scanner) scanWhitespace() (token Token, lit string) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
for {
|
||||
if ch := s.read(); ch == eof {
|
||||
break
|
||||
} else if !isWhitespace(ch) {
|
||||
s.unread()
|
||||
break
|
||||
} else {
|
||||
_, _ = buf.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
return WS, buf.String()
|
||||
}
|
||||
|
||||
func (s *Scanner) scanIndent() (tok Token, lit string) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
for {
|
||||
if ch := s.read(); ch == eof {
|
||||
break
|
||||
} else if !isLetter(ch) && !isDigit(ch) {
|
||||
s.unread()
|
||||
break
|
||||
} else {
|
||||
_, _ = buf.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
switch buf.String() {
|
||||
case "var":
|
||||
return VAR, buf.String()
|
||||
}
|
||||
|
||||
if len(buf.String()) == 1 {
|
||||
return Token(rune(buf.String()[0])), buf.String()
|
||||
}
|
||||
if buf.String() == "out" {
|
||||
return OUT, buf.String()
|
||||
}
|
||||
return IDENT, buf.String()
|
||||
}
|
||||
92
circuitcompiler/parser.go
Normal file
92
circuitcompiler/parser.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package circuitcompiler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
s *Scanner
|
||||
buf struct {
|
||||
tok Token // last read token
|
||||
lit string // last read literal
|
||||
n int // buffer size (max=1)
|
||||
}
|
||||
}
|
||||
|
||||
type Constraint struct {
|
||||
// v1 op v2 = out
|
||||
Op Token
|
||||
V1 Token
|
||||
V2 Token
|
||||
Out Token
|
||||
Literal string
|
||||
}
|
||||
|
||||
func NewParser(r io.Reader) *Parser {
|
||||
return &Parser{s: NewScanner(r)}
|
||||
}
|
||||
|
||||
func (p *Parser) scan() (tok Token, lit string) {
|
||||
// if there is a token in the buffer return it
|
||||
if p.buf.n != 0 {
|
||||
p.buf.n = 0
|
||||
return p.buf.tok, p.buf.lit
|
||||
}
|
||||
tok, lit = p.s.Scan()
|
||||
|
||||
p.buf.tok, p.buf.lit = tok, lit
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Parser) unscan() {
|
||||
p.buf.n = 1
|
||||
}
|
||||
|
||||
func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) {
|
||||
tok, lit = p.scan()
|
||||
if tok == WS {
|
||||
tok, lit = p.scan()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Parser) ParseLine() (*Constraint, error) {
|
||||
/*
|
||||
in this version,
|
||||
line will be for example s3 = s1 * s4
|
||||
this is:
|
||||
val op val op val
|
||||
ident op ident op ident
|
||||
*/
|
||||
c := &Constraint{}
|
||||
var lit string
|
||||
c.Out, lit = p.scanIgnoreWhitespace()
|
||||
c.Literal += lit
|
||||
_, lit = p.scanIgnoreWhitespace() // skip =
|
||||
c.Literal += lit
|
||||
c.V1, lit = p.scanIgnoreWhitespace()
|
||||
c.Literal += lit
|
||||
c.Op, lit = p.scanIgnoreWhitespace()
|
||||
c.Literal += lit
|
||||
c.V2, lit = p.scanIgnoreWhitespace()
|
||||
c.Literal += lit
|
||||
if c.Out == EOF {
|
||||
return nil, errors.New("eof in parseline")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (p *Parser) Parse() (*Circuit, error) {
|
||||
circuit := &Circuit{}
|
||||
for {
|
||||
constraint, err := p.ParseLine()
|
||||
if err != nil {
|
||||
// return circuit, err
|
||||
break
|
||||
}
|
||||
circuit.Constraints = append(circuit.Constraints, *constraint)
|
||||
}
|
||||
return circuit, nil
|
||||
}
|
||||
Reference in New Issue
Block a user