From 1e87be859d06cb186dbb42d5571790f49fb3f5b6 Mon Sep 17 00:00:00 2001 From: Iskander Sharipov <quasilyte@gmail.com> Date: Fri, 2 Feb 2024 17:09:00 +0400 Subject: [PATCH] pkg/hintrunner/zero: allow multiplication binary ops in references References may include the multiplication inside them. Let's take this like of code for the example: https://github.com/starkware-libs/cairo-lang/blob/caba294d82eeeccc3d86a158adb8ba209bf2d8fc/src/starkware/cairo/common/math.cairo#L193 It will produce a reference like this: ```json { "cairo_type": "felt", "full_name": "starkware.cairo.common.math.assert_le_felt.arc_prod", "references": [ { "ap_tracking_data": { "group": 1, "offset": 8 }, "pc": 14, "value": "cast([ap + (-5)] * [ap + (-1)], felt)" } ], "type": "reference" } ``` --- pkg/hintrunner/zero/hintparser.go | 33 +++++++++++++++++++++++--- pkg/hintrunner/zero/hintparser_test.go | 20 ++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/pkg/hintrunner/zero/hintparser.go b/pkg/hintrunner/zero/hintparser.go index 19113d1cd..15f8fe919 100644 --- a/pkg/hintrunner/zero/hintparser.go +++ b/pkg/hintrunner/zero/hintparser.go @@ -3,6 +3,7 @@ package zero import ( "fmt" + "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" op "github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter" "github.com/alecthomas/participle/v2" ) @@ -22,6 +23,7 @@ var parser *participle.Parser[IdentifierExp] = participle.MustBuild[IdentifierEx // 2 dereferences off1 omitted: cast([reg] + [reg + off2], type) // 2 dereferences off2 omitted: cast([reg + off1] + [reg], type) // 2 dereferences both offs omitted: cast([reg] + [reg], type) +// 2 dereferences with multiplication: cast([reg + off1] * [reg + off2], felt) // Reference no dereference 2 offsets - + : cast(reg - off1 + off2, type) // Note: The same cases apply with an external dereference. Example: [cast(number, type)] @@ -62,7 +64,8 @@ type DerefExp struct { } type BinOpExp struct { - LeftExp *LeftExp `@@ "+"` + LeftExp *LeftExp `@@` + Operator string `@("+" | "*")` RightExp *RightExp `@@` } @@ -83,10 +86,12 @@ type RightExp struct { type DerefOffset struct { Deref op.Deref + Op op.Operator Offset *int } type DerefDeref struct { LeftDeref op.Deref + Op op.Operator RightDeref op.Deref } @@ -141,8 +146,9 @@ func (expression CastExp) Evaluate() (any, error) { return result, nil case DerefOffset: return op.BinaryOp{ - Operator: 0, + Operator: result.Op, Lhs: result.Deref.Deref, + // TODO: why we're not using something like f.NewElement here? Rhs: op.Immediate{ uint64(0), uint64(0), @@ -152,7 +158,7 @@ func (expression CastExp) Evaluate() (any, error) { }, nil case DerefDeref: return op.BinaryOp{ - Operator: 0, + Operator: result.Op, Lhs: result.LeftDeref.Deref, Rhs: result.RightDeref, }, nil @@ -238,8 +244,16 @@ func (expression BinOpExp) Evaluate() (any, error) { return nil, err } + operation, err := parseOperator(expression.Operator) + if err != nil { + return nil, err + } + switch lResult := leftExp.(type) { case op.CellRefer: + // Right now we assume that there is no expression like `reg - off1 * off2`, + // but if there are, we would need to come up with an idea how to handle it. + // Right now we only cover `off1 + off2` expressions here. offset, ok := rightExp.(*int) if !ok { return nil, fmt.Errorf("invalid type operation") @@ -267,11 +281,13 @@ func (expression BinOpExp) Evaluate() (any, error) { case op.Deref: return DerefDeref{ lResult, + operation, rResult, }, nil case *int: return DerefOffset{ lResult, + operation, rResult, }, nil } @@ -308,3 +324,14 @@ func ParseIdentifier(value string) (any, error) { return identifierExp.Evaluate() } + +func parseOperator(op string) (hinter.Operator, error) { + switch op { + case "+": + return hinter.Add, nil + case "*": + return hinter.Mul, nil + default: + return 0, fmt.Errorf("unexpected op: %q", op) + } +} diff --git a/pkg/hintrunner/zero/hintparser_test.go b/pkg/hintrunner/zero/hintparser_test.go index 39b8fd8a1..0fcbf5a63 100644 --- a/pkg/hintrunner/zero/hintparser_test.go +++ b/pkg/hintrunner/zero/hintparser_test.go @@ -45,6 +45,26 @@ func TestHintParser(t *testing.T) { }, }, }, + { + Parameter: "cast([ap + (-5)] * [ap + (-1)], felt)", + ExpectedCellRefer: nil, + ExpectedResOperander: hinter.BinaryOp{ + Operator: hinter.Mul, + Lhs: hinter.ApCellRef(-5), + Rhs: hinter.Deref{ + Deref: hinter.ApCellRef(-1), + }, + }, + }, + { + Parameter: "cast([ap] * 3, felt)", + ExpectedCellRefer: nil, + ExpectedResOperander: hinter.BinaryOp{ + Operator: hinter.Mul, + Lhs: hinter.ApCellRef(0), + Rhs: hinter.Immediate{0, 0, 0, 3}, + }, + }, } for _, test := range testSet {