Skip to content

Commit

Permalink
Bellmar/index position (#36)
Browse files Browse the repository at this point in the history
* constants can't be accessed via index

* index expressions should include the index number in the variable rawid

* index expressions in llvm

* indexing supposed in smt

* uncommenting tests

* extra test for indexes in asserts

* clock support

* updating README.md
  • Loading branch information
mbellotti committed Apr 27, 2023
1 parent 3e53fd8 commit 1e39479
Show file tree
Hide file tree
Showing 19 changed files with 613 additions and 220 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ The development Fault is documented in the series "Marianne Writes a Programming
- [audio](https://anchor.fm/mwapl)
- [transcripts](https://dev.to/bellmar/series/9711)

### Current Status (4/13/2023)
### Current Status (4/26/2023)
Adding support for indexes (ie accessing historical values in the model) there are some edge cases around branches that need to be worked through.

#### (4/13/2023)
BREAKING CHANGE. Adding support for altering the initial values of new instances of stocks and flows ended up requiring a lot of fundalmental changes, the big one being separating the initialization of model structs for the logic of a loop. Previously these both lived in the run block (`for 2 run{...}`) but now the run block has a optional `init{}` clause where initializations go, leaving the run block with just the steps the happen in the loop. The documentation and example specs have been updated to reflect this new construction.

#### (3/29/2023)
Expand Down
6 changes: 4 additions & 2 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,9 @@ func (i *Identifier) operandNode() {}
func (i *Identifier) expressionNode() {}
func (i *Identifier) TokenLiteral() string { return i.Token.Literal }
func (i *Identifier) Position() []int { return i.Token.GetPosition() }
func (i *Identifier) String() string { return i.Value }
func (i *Identifier) String() string {
return i.Value
}
func (i *Identifier) Type() string {
t := i.InferredType
if t != nil {
Expand Down Expand Up @@ -1094,7 +1096,7 @@ type Clock struct {
func (c *Clock) expressionNode() {}
func (c *Clock) TokenLiteral() string { return c.Token.Literal }
func (c *Clock) Position() []int { return c.Token.GetPosition() }
func (c *Clock) String() string { return c.Token.Literal }
func (c *Clock) String() string { return "now" }
func (c *Clock) Type() string {
t := c.InferredType
if t != nil {
Expand Down
2 changes: 1 addition & 1 deletion ast/ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func TestString(t *testing.T) {
want = "foo.bar"
case *Clock:
got = t.String()
want = "test"
want = "now"
case *Nil:
got = t.String()
want = "test"
Expand Down
8 changes: 7 additions & 1 deletion listener/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,6 @@ func (l *FaultListener) ExitRunInit(c *parser.RunInitContext) {
l.push(inst)
}


func (l *FaultListener) ExitRunSwap(c *parser.SwapContext) {
token := util.GenerateToken("SWAP", "SWAP", c.GetStart(), c.GetStop())

Expand Down Expand Up @@ -1384,6 +1383,13 @@ func (l *FaultListener) ExitAssertion(c *parser.AssertionContext) {
}
case *ast.InvariantClause:
con = e
case *ast.IndexExpression:
con = &ast.InvariantClause{
Token: e.Token,
Left: e,
Operator: "==",
Right: &ast.Boolean{Value: true},
}
}

l.push(&ast.AssertionStatement{
Expand Down
107 changes: 18 additions & 89 deletions llvm/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ func NewCompiler() *Compiler {
return c
}


func Execute(tree *ast.Spec, specRec map[string]*preprocess.SpecRecord, uncertains map[string][]float64, unknowns []string, aliases map[string]string, testing bool) *Compiler {
compiler := NewCompiler()
compiler.LoadMeta(specRec, uncertains, unknowns, aliases, testing)
Expand Down Expand Up @@ -387,6 +386,8 @@ func (c *Compiler) compileValue(node ast.Node) value.Value {
return c.compileThis(v)
case *ast.BuiltIn:
return c.compileFunction(v)
case *ast.IndexExpression:
return c.compileIndex(v)
default:
pos := v.Position()
panic(fmt.Sprintf("unknown value type %T line: %d col: %d", v, pos[0], pos[1]))
Expand Down Expand Up @@ -517,8 +518,8 @@ func (c *Compiler) compileParameterCall(pc *ast.ParameterCall) value.Value {
var err error
id := c.AliasToBaseRaw(pc.RawId())
spec := c.specStructs[id[0]]
ty, _ := spec.GetStructType(id[0:2]) //Removing the key
st := id[len(id)-2] //The struct is the second to last item
ty, _ := spec.GetStructType(id) //Removing the key
st := strings.Join(id[1:len(id)-1], "_") //The struct
key := id[len(id)-1]

var branches map[string]ast.Node
Expand Down Expand Up @@ -646,6 +647,7 @@ func (c *Compiler) compileFunction(node ast.Node) value.Value {
c.compileInstance(v)

case *ast.IndexExpression:
c.compileIndex(v)

case *ast.ParameterCall:
return c.compileParameterCall(v)
Expand Down Expand Up @@ -685,6 +687,18 @@ func (c *Compiler) compileFunction(node ast.Node) value.Value {
return nil
}

func (c *Compiler) compileIndex(node *ast.IndexExpression) *ir.InstLoad {
var value value.Value
if node.Left.Type() == "BOOLEAN" {
value = constant.NewBool(false)
} else {
value = constant.NewFloat(irtypes.Double, float64(0.000000000009))
}
c.setConst(node.RawId(), value)
c.globalVariable(node.Id(), value, node.Position())
return c.lookupIdent(node.Id(), node.Position())
}

func (c *Compiler) compilePrefix(node *ast.PrefixExpression) value.Value {
val := c.compileInfixNode(node.Right)
switch node.Operator {
Expand Down Expand Up @@ -1577,7 +1591,7 @@ func negate(e ast.Expression) ast.Expression {
n.Left = negate(n.Left)
n.Right = negate(n.Right)

node := evaluate(n) // If Int/Float, evaluate and return the value
node := util.Evaluate(n) // If Int/Float, evaluate and return the value
return node
case *ast.Boolean:
if n.Value {
Expand Down Expand Up @@ -1610,91 +1624,6 @@ func negateTemporal(op string, n int) (string, int) {
return op2, n2
}

func evaluate(n *ast.InfixExpression) ast.Expression {
if util.IsCompare(n.Operator) {
return n
}
f1, ok1 := n.Left.(*ast.FloatLiteral)
i1, ok2 := n.Left.(*ast.IntegerLiteral)

if !ok1 && !ok2 {
return n
}

f2, ok1 := n.Right.(*ast.FloatLiteral)
i2, ok2 := n.Right.(*ast.IntegerLiteral)

if !ok1 && !ok2 {
return n
}

if f1 != nil {
if f2 != nil {
v := evalFloat(f1.Value, f2.Value, n.Operator)
return &ast.FloatLiteral{
Token: n.Token,
Value: v,
}
} else {
v := evalFloat(f1.Value, float64(i2.Value), n.Operator)
return &ast.FloatLiteral{
Token: n.Token,
Value: v,
}
}
} else {
if f2 != nil {
v := evalFloat(float64(i1.Value), f2.Value, n.Operator)
return &ast.FloatLiteral{
Token: n.Token,
Value: v,
}
} else {
if n.Operator == "/" {
//Return a float in the case of division
v := evalFloat(float64(i1.Value), float64(i2.Value), n.Operator)
return &ast.FloatLiteral{
Token: n.Token,
Value: v,
}
}
v := evalInt(i1.Value, i2.Value, n.Operator)
return &ast.IntegerLiteral{
Token: n.Token,
Value: v,
}
}
}
}

func evalFloat(f1 float64, f2 float64, op string) float64 {
switch op {
case "+":
return f1 + f2
case "-":
return f1 - f2
case "*":
return f1 * f2
case "/":
return f1 / f2
default:
panic(fmt.Sprintf("unsupported operator %s", op))
}
}

func evalInt(i1 int64, i2 int64, op string) int64 {
switch op {
case "+":
return i1 + i2
case "-":
return i1 - i2
case "*":
return i1 * i2
default:
panic(fmt.Sprintf("unsupported operator %s", op))
}
}

func (c *Compiler) GetIR() string {
return c.module.String()
}
Expand Down
121 changes: 58 additions & 63 deletions llvm/llvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,66 +418,6 @@ func TestNegate(t *testing.T) {
}
}

func TestEval(t *testing.T) {
tests := []*ast.InfixExpression{{
Left: &ast.IntegerLiteral{Value: 2},
Right: &ast.IntegerLiteral{Value: 2},
},
{
Left: &ast.FloatLiteral{Value: 2.5},
Right: &ast.IntegerLiteral{Value: 2},
},
{
Left: &ast.IntegerLiteral{Value: 2},
Operator: "+",
Right: &ast.FloatLiteral{Value: 2.5},
}}

operators := []string{"+", "-", "/", "*"}

results := []ast.Node{
&ast.IntegerLiteral{Value: 4},
&ast.FloatLiteral{Value: 4.5},
&ast.FloatLiteral{Value: 4.5},
&ast.IntegerLiteral{Value: 0},
&ast.FloatLiteral{Value: .5},
&ast.FloatLiteral{Value: -.5},
&ast.FloatLiteral{Value: 1},
&ast.FloatLiteral{Value: 1.25},
&ast.FloatLiteral{Value: .8},
&ast.IntegerLiteral{Value: 4},
&ast.FloatLiteral{Value: 5},
&ast.FloatLiteral{Value: 5},
}

i := 0
for _, o := range operators {
for _, n := range tests {
n.Operator = o
test := evaluate(n)
switch actual := test.(type) {
case *ast.IntegerLiteral:
expected, ok := results[i].(*ast.IntegerLiteral)
if !ok {
t.Fatalf("expected value a different type from actual expected=%s actual=%s", results[i], test)
}
if expected.Value != actual.Value {
t.Fatalf("expected value a different from actual expected=%s actual=%s", expected, actual)
}
case *ast.FloatLiteral:
expected, ok := results[i].(*ast.FloatLiteral)
if !ok {
t.Fatalf("expected value a different type from actual expected=%s actual=%s", results[i], test)
}
if expected.Value != actual.Value {
t.Fatalf("expected value a different from actual expected=%s actual=%s", expected, actual)
}
}
i++
}
}
}

func TestIsVarSet(t *testing.T) {
c := NewCompiler()
c.specStructs["test"] = preprocess.NewSpecRecord()
Expand Down Expand Up @@ -683,9 +623,64 @@ func TestComponentIR(t *testing.T) {

}

// init values
// clock
// index
func TestIndexExp(t *testing.T) {
test := `spec test1;
def foo = flow{
buzz: new bar,
fizz: func{
buzz.a = buzz.a[1] - 2;
},
};
def bar = stock{
a: 10,
};
for 1 init{test = new foo;} run{
test.fizz;
};
`

expecting := `@__rounds = global i16 0
@__parallelGroup = global [5 x i8] c"start"
@test1_test_buzz_a_1 = global double 0x3DA3CA8CB153A753
define void @__run() {
block-27:
store i16 0, i16* @__rounds
%test1_test_buzz_a = alloca double
store double 10.0, double* %test1_test_buzz_a
call void @test1_test_fizz(double* %test1_test_buzz_a), !\37f977975ebdfc28b778ed4618a0af327 !DIBasicType(tag: DW_TAG_string_type)
ret void
}
define void @test1_test_fizz(double* %test1_test_buzz_a) {
block-28:
%0 = load double, double* @test1_test_buzz_a_1
%1 = fsub double %0, 2.0
store double %1, double* %test1_test_buzz_a
ret void
}`

llvm, err := prepTest(test, true)

if err != nil {
t.Fatalf("compilation failed on valid spec. got=%s", err)
}

ir, err := validateIR(llvm)

if err != nil {
t.Fatalf("generated IR is not valid. got=%s", err)
}

err = compareResults(llvm, expecting, string(ir))

if err != nil {
t.Fatalf(err.Error())
}
}

func compareResults(llvm string, expecting string, ir string) error {
if !strings.Contains(ir, "source_filename = \"<stdin>\"") {
Expand Down
34 changes: 0 additions & 34 deletions llvm/llvm_xmisc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,6 @@ func TestNegateTemporal(t *testing.T) {
}
}

func TestEvalFloat(t *testing.T) {
test1 := evalFloat(2.1, 1.5, "+")
if test1 != 3.6 {
t.Fatal("evalFloat failed to eval + correctly")
}
test2 := evalFloat(2.5, 1.5, "-")
if test2 != 1 {
t.Fatal("evalFloat failed to eval - correctly")
}
test3 := evalFloat(2.1, 1.0, "*")
if test3 != 2.1 {
t.Fatal("evalFloat failed to eval * correctly")
}
test4 := evalFloat(2.0, 2.0, "/")
if test4 != 1.0 {
t.Fatal("evalFloat failed to eval / correctly")
}
}

func TestEvalInt(t *testing.T) {
test1 := evalInt(2, 1, "+")
if test1 != 3 {
t.Fatal("evalInt failed to eval + correctly")
}
test2 := evalInt(2, 1, "-")
if test2 != 1 {
t.Fatal("evalInt failed to eval - correctly")
}
test3 := evalInt(2, 1, "*")
if test3 != 2 {
t.Fatal("evalInt failed to eval * correctly")
}
}

func TestValidOperator(t *testing.T) {
c := NewCompiler()
boolTy := &ast.Type{Type: "BOOL"}
Expand Down

0 comments on commit 1e39479

Please sign in to comment.