11 // We have some function naming conventions.
14 // scanX takes buf and position, returns new position (and maybe a value)
15 // peekX takes *parser, returns bool or string
16 // consumeX takes *parser and maybe a required literal, maybe returns value
17 // also updates the parser position
20 // parseX takes *parser, returns AST node, updates parser position
27 func (p *parser) errorf(format string, args ...interface{}) {
28 panic(parserErr{buf: p.buf, offset: p.pos, format: format, args: args})
31 // parse is the main entry point to the parser
32 func parse(buf []byte) (contracts []*Contract, err error) {
34 if val := recover(); val != nil {
35 if e, ok := val.(parserErr); ok {
42 p := &parser{buf: buf}
43 contracts = parseContracts(p)
48 func parseContracts(p *parser) []*Contract {
49 var result []*Contract
50 contracts := parseImportDirectives(p)
51 for _, c := range contracts {
52 result = append(result, c)
55 if pos := scanKeyword(p.buf, p.pos, "contract"); pos < 0 {
56 p.errorf("expected contract")
58 for peekKeyword(p) == "contract" {
59 contract := parseContract(p)
60 result = append(result, contract)
65 // contract name(p1, p2: t1, p3: t2) locks value { ... }
66 func parseContract(p *parser) *Contract {
67 consumeKeyword(p, "contract")
68 name := consumeIdentifier(p)
69 params := parseParams(p)
70 // locks amount of asset
71 consumeKeyword(p, "locks")
73 value.Amount = consumeIdentifier(p)
74 consumeKeyword(p, "of")
75 value.Asset = consumeIdentifier(p)
77 clauses := parseClauses(p)
79 return &Contract{Name: name, Params: params, Clauses: clauses, Value: value}
82 // (p1, p2: t1, p3: t2)
83 func parseParams(p *parser) []*Param {
87 for !peekTok(p, ")") {
93 pt := parseParamsType(p)
94 params = append(params, pt...)
100 func parseClauses(p *parser) []*Clause {
101 var clauses []*Clause
102 for !peekTok(p, "}") {
104 clauses = append(clauses, c)
109 func parseParamsType(p *parser) []*Param {
110 firstName := consumeIdentifier(p)
111 params := []*Param{&Param{Name: firstName}}
112 for peekTok(p, ",") {
114 name := consumeIdentifier(p)
115 params = append(params, &Param{Name: name})
118 typ := consumeIdentifier(p)
119 for _, parm := range params {
120 if tdesc, ok := types[typ]; ok {
123 p.errorf("unknown type %s", typ)
129 func parseClause(p *parser) *Clause {
131 consumeKeyword(p, "clause")
132 c.Name = consumeIdentifier(p)
133 c.Params = parseParams(p)
135 c.statements = parseStatements(p)
140 func parseStatements(p *parser) []statement {
141 var statements []statement
142 for !peekTok(p, "}") {
143 s := parseStatement(p)
144 statements = append(statements, s)
149 func parseStatement(p *parser) statement {
150 switch peekKeyword(p) {
152 return parseIfStmt(p)
154 return parseDefineStmt(p)
156 return parseAssignStmt(p)
158 return parseVerifyStmt(p)
160 return parseLockStmt(p)
162 return parseUnlockStmt(p)
164 panic(parseErr(p.buf, p.pos, "unknown keyword \"%s\"", peekKeyword(p)))
167 func parseIfStmt(p *parser) *ifStatement {
168 consumeKeyword(p, "if")
169 condition := parseExpr(p)
170 body := &IfStatmentBody{}
172 body.trueBody = parseStatements(p)
174 if peekKeyword(p) == "else" {
175 consumeKeyword(p, "else")
177 body.falseBody = parseStatements(p)
180 return &ifStatement{condition: condition, body: body}
183 func parseDefineStmt(p *parser) *defineStatement {
184 defineStat := &defineStatement{}
185 consumeKeyword(p, "define")
187 param.Name = consumeIdentifier(p)
189 variableType := consumeIdentifier(p)
190 if tdesc, ok := types[variableType]; ok {
193 p.errorf("unknown type %s", variableType)
195 defineStat.variable = param
198 defineStat.expr = parseExpr(p)
203 func parseAssignStmt(p *parser) *assignStatement {
204 consumeKeyword(p, "assign")
205 varName := consumeIdentifier(p)
208 return &assignStatement{variable: &Param{Name: varName}, expr: expr}
211 func parseVerifyStmt(p *parser) *verifyStatement {
212 consumeKeyword(p, "verify")
214 return &verifyStatement{expr: expr}
217 func parseLockStmt(p *parser) *lockStatement {
218 consumeKeyword(p, "lock")
219 lockedAmount := parseExpr(p)
220 consumeKeyword(p, "of")
221 lockedAsset := parseExpr(p)
222 consumeKeyword(p, "with")
223 program := parseExpr(p)
224 return &lockStatement{lockedAmount: lockedAmount, lockedAsset: lockedAsset, program: program}
227 func parseUnlockStmt(p *parser) *unlockStatement {
228 consumeKeyword(p, "unlock")
229 unlockedAmount := parseExpr(p)
230 consumeKeyword(p, "of")
231 unlockedAsset := parseExpr(p)
232 return &unlockStatement{unlockedAmount: unlockedAmount, unlockedAsset: unlockedAsset}
235 func parseExpr(p *parser) expression {
236 // Uses the precedence-climbing algorithm
237 // <https://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing_method>
238 expr := parseUnaryExpr(p)
239 expr2, pos := parseExprCont(p, expr, 0)
241 p.errorf("expected expression")
247 func parseUnaryExpr(p *parser) expression {
248 op, pos := scanUnaryOp(p.buf, p.pos)
253 expr := parseUnaryExpr(p)
254 return &unaryExpr{op: op, expr: expr}
257 func parseExprCont(p *parser, lhs expression, minPrecedence int) (expression, int) {
259 op, pos := scanBinaryOp(p.buf, p.pos)
260 if pos < 0 || op.precedence < minPrecedence {
265 rhs := parseUnaryExpr(p)
268 op2, pos2 := scanBinaryOp(p.buf, p.pos)
269 if pos2 < 0 || op2.precedence <= op.precedence {
272 rhs, p.pos = parseExprCont(p, rhs, op2.precedence)
274 return nil, -1 // or is this an error?
277 lhs = &binaryExpr{left: lhs, right: rhs, op: op}
282 func parseExpr2(p *parser) expression {
283 if expr, pos := scanLiteralExpr(p.buf, p.pos); pos >= 0 {
290 func parseExpr3(p *parser) expression {
294 return &callExpr{fn: e, args: args}
299 func parseExpr4(p *parser) expression {
307 var elts []expression
310 for !peekTok(p, "]") {
317 elts = append(elts, e)
320 return listExpr(elts)
322 name := consumeIdentifier(p)
326 func parseArgs(p *parser) []expression {
327 var exprs []expression
330 for !peekTok(p, ")") {
337 exprs = append(exprs, e)
345 func peekKeyword(p *parser) string {
346 name, _ := scanIdentifier(p.buf, p.pos)
350 func peekTok(p *parser, token string) bool {
351 pos := scanTok(p.buf, p.pos, token)
357 var keywords = []string{
358 "contract", "clause", "verify", "locks", "of",
359 "lock", "with", "unlock", "if", "else",
360 "define", "assign", "true", "false",
363 func consumeKeyword(p *parser, keyword string) {
364 pos := scanKeyword(p.buf, p.pos, keyword)
366 p.errorf("expected keyword %s", keyword)
371 func consumeIdentifier(p *parser) string {
372 name, pos := scanIdentifier(p.buf, p.pos)
374 p.errorf("expected identifier")
380 func consumeTok(p *parser, token string) {
381 pos := scanTok(p.buf, p.pos, token)
383 p.errorf("expected %s token", token)
390 func scanUnaryOp(buf []byte, offset int) (*unaryOp, int) {
391 // Maximum munch. Make sure "-3" scans as ("-3"), not ("-", "3").
392 if _, pos := scanIntLiteral(buf, offset); pos >= 0 {
395 for _, op := range unaryOps {
396 newOffset := scanTok(buf, offset, op.op)
398 return &op, newOffset
404 func scanBinaryOp(buf []byte, offset int) (*binaryOp, int) {
405 offset = skipWsAndComments(buf, offset)
410 for i, op := range binaryOps {
411 offset2 := scanTok(buf, offset, op.op)
413 if found == nil || len(op.op) > len(found.op) {
414 found = &binaryOps[i]
419 return found, newOffset
422 // TODO(bobg): boolean literals?
423 func scanLiteralExpr(buf []byte, offset int) (expression, int) {
424 offset = skipWsAndComments(buf, offset)
425 intliteral, newOffset := scanIntLiteral(buf, offset)
427 return intliteral, newOffset
429 strliteral, newOffset := scanStrLiteral(buf, offset)
431 return strliteral, newOffset
433 bytesliteral, newOffset := scanBytesLiteral(buf, offset) // 0x6c249a...
435 return bytesliteral, newOffset
437 booleanLiteral, newOffset := scanBoolLiteral(buf, offset) // true or false
439 return booleanLiteral, newOffset
444 func scanIdentifier(buf []byte, offset int) (string, int) {
445 offset = skipWsAndComments(buf, offset)
447 for ; i < len(buf) && isIDChar(buf[i], i == offset); i++ {
452 return string(buf[offset:i]), i
455 func scanTok(buf []byte, offset int, s string) int {
456 offset = skipWsAndComments(buf, offset)
458 if bytes.HasPrefix(buf[offset:], prefix) {
459 return offset + len(prefix)
464 func scanKeyword(buf []byte, offset int, keyword string) int {
465 id, newOffset := scanIdentifier(buf, offset)
475 func scanIntLiteral(buf []byte, offset int) (integerLiteral, int) {
476 offset = skipWsAndComments(buf, offset)
478 if offset < len(buf) && buf[offset] == '-' {
482 for ; i < len(buf) && unicode.IsDigit(rune(buf[i])); i++ {
483 // the literal is BytesLiteral when it starts with 0x/0X
484 if buf[i] == '0' && i < len(buf)-1 && (buf[i+1] == 'x' || buf[i+1] == 'X') {
489 n, err := strconv.ParseInt(string(buf[start:i]), 10, 64)
493 return integerLiteral(n), i
498 func scanStrLiteral(buf []byte, offset int) (bytesLiteral, int) {
499 offset = skipWsAndComments(buf, offset)
500 if offset >= len(buf) || !(buf[offset] == '\'' || buf[offset] == '"') {
501 return bytesLiteral{}, -1
503 var byteBuf bytesLiteral
504 for i := offset + 1; i < len(buf); i++ {
505 if (buf[offset] == '\'' && buf[i] == '\'') || (buf[offset] == '"' && buf[i] == '"') {
506 return byteBuf, i + 1
508 if buf[i] == '\\' && i < len(buf)-1 {
509 if c, ok := scanEscape(buf[i+1]); ok {
510 byteBuf = append(byteBuf, c)
515 byteBuf = append(byteBuf, buf[i])
517 panic(parseErr(buf, offset, "unterminated string literal"))
520 func scanBytesLiteral(buf []byte, offset int) (bytesLiteral, int) {
521 offset = skipWsAndComments(buf, offset)
522 if offset+4 >= len(buf) {
525 if buf[offset] != '0' || (buf[offset+1] != 'x' && buf[offset+1] != 'X') {
528 if !isHexDigit(buf[offset+2]) || !isHexDigit(buf[offset+3]) {
532 for ; i < len(buf); i += 2 {
534 panic(parseErr(buf, offset, "odd number of digits in hex literal"))
536 if !isHexDigit(buf[i]) {
539 if !isHexDigit(buf[i+1]) {
540 panic(parseErr(buf, offset, "odd number of digits in hex literal"))
543 decoded := make([]byte, hex.DecodedLen(i-(offset+2)))
544 _, err := hex.Decode(decoded, buf[offset+2:i])
546 return bytesLiteral{}, -1
548 return bytesLiteral(decoded), i
551 func scanBoolLiteral(buf []byte, offset int) (booleanLiteral, int) {
552 offset = skipWsAndComments(buf, offset)
553 if offset >= len(buf) {
557 newOffset := scanKeyword(buf, offset, "true")
559 if newOffset = scanKeyword(buf, offset, "false"); newOffset < 0 {
562 return false, newOffset
564 return true, newOffset
567 func skipWsAndComments(buf []byte, offset int) int {
569 for ; offset < len(buf); offset++ {
576 if c == '/' && offset < len(buf)-1 && buf[offset+1] == '/' {
578 offset++ // skip two chars instead of one
579 } else if !unicode.IsSpace(rune(c)) {
587 func isHexDigit(b byte) bool {
588 return (b >= '0' && b <= '9') || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F')
591 func isIDChar(c byte, initial bool) bool {
592 if c >= 'a' && c <= 'z' {
595 if c >= 'A' && c <= 'Z' {
604 return unicode.IsDigit(rune(c))
607 type parserErr struct {
614 func parseErr(buf []byte, offset int, format string, args ...interface{}) error {
615 return parserErr{buf: buf, offset: offset, format: format, args: args}
618 func (p parserErr) Error() string {
619 // Lines start at 1, columns start at 0, like nature intended.
622 for i := 0; i < p.offset; i++ {
623 if p.buf[i] == '\n' {
630 args := []interface{}{line, col}
631 args = append(args, p.args...)
632 return fmt.Sprintf("line %d, col %d: "+p.format, args...)
635 func scanEscape(c byte) (byte, bool) {
638 case '\'', '"', '\\':