Skip to content

Commit

Permalink
Organize examples, executables and README
Browse files Browse the repository at this point in the history
  • Loading branch information
bugarela committed Aug 9, 2022
1 parent 33bee8e commit 0f4fae3
Show file tree
Hide file tree
Showing 96 changed files with 2,825 additions and 4,136 deletions.
File renamed without changes.
File renamed without changes.
35 changes: 13 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
# TLA+ Transmutation
Elixir code generation from TLA+ specifications

I've started to work on this for my bachelor thesis, which can be found [here](https://github.com/bugarela/bachelor-thesis) (Portuguese only).

The formalization of the translation rules are available at [rules.pdf](rules.pdf)

Notice that this covers only a small portion of TLA+ definitions and it is yet to be incremented with more translations. The Parser itself isn't able to recognize much. Contributions are welcome :D.
Elixir code and test generation from TLA+ specifications

## Dependencies

Expand All @@ -20,27 +14,24 @@ Notice that this covers only a small portion of TLA+ definitions and it is yet t
[Instructions for installation with asdf package manager](https://elixirgirls.com/install-guides/linux.html)

## Build
```sh
ghc Main.hs

``` sh
make compile
```

## Run

This currently supports partially 2 forms of parsing:
1. From `.tla` files: this is deprecated and supports a minimal fragment of the language
2. From `.json` files produced by [Apalache](https://github.com/informalsystems/apalache)'s parsing: Work in progress.
The best parsing implementation takes JSON files previously parsed from TLA with [Apalache](https://github.com/informalsystems/apalache):

To generate code, specify the args:
1. Mode (`tla` or `json`)
2. Filepath (for the `.tla` or `.json` file)
3. Init definition name (i.e. `MyInit`)
4. Next definition name (i.e. `MyNext`)

Some examples:
```sh
./Main json tla_specifications/token-transfer/TokenTransfer1.json Init Next
``` sh
apalache-mc parse --output=file.json file.tla
```

All compliled files (`CodeGenerator`, `WhiteboxTestGenerator` and `BlackboxTestGenerator`) take a single argument which is a JSON-formatted configuration file similar to [config-sample.json](./config-sample.json)

Folders inside [tla_specifications](./tla_specifications) are examples containing the required files for running the three executables. You can `cd` into any of them and run:

``` sh
./Main tla tla_specifications/EfetivacaoEmDuasFases.tla DFInit DFNext
../../CodeGenerator config.json && ../../BlackboxTestGenerator config.json && ../../WhiteboxTestGenerator config.json
```

2 changes: 1 addition & 1 deletion StateGraphParser.hs → WhiteboxTestGenerator.hs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ testForNode ms g Node {nodeId = i, label = l} = do
, " expectedStates = [" ++ intercalate ",\n" ss ++ "]"
, ""
, " actions = List.flatten([" ++ intercalate ", " (map (++ ".next(variables)") ms) ++ "])"
, " states = Enum.map(actions, fn action -> action[:state] end)"
, " states = Enum.map(actions, fn action -> action[:transition].(variables) end)"
, ""
, " assert Enum.sort(Enum.uniq(states)) == Enum.sort(Enum.uniq(expectedStates))"
, "end"
Expand Down
16 changes: 15 additions & 1 deletion config-sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,19 @@
]
}
],
"shared_variables": ["tcolor", "tpos", "active"]
"shared_variables": ["tcolor", "tpos", "active"],
"constants": [
{
"name": "N",
"value": "3"
}
],
"init": "Init",
"next": "Next",
"module_name": "EWD840",
"input_format": "json",
"input_file": "EWD840.json",
"state_graph": "states.json",
"blackbox_tests": [{"name": "Termination", "trace": "trace.out"}],
"destination_folder": "../../elixir"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule APAEWD840 do
defmodule EWD840 do
def shared_variables do
[
:tcolor,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule APAEWD840_node0 do
defmodule EWD840_node0 do
require Oracle

import APAEWD840
import EWD840

def next(variables) do
Enum.filter(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule APAEWD840_node1 do
defmodule EWD840_node1 do
require Oracle

import APAEWD840
import EWD840

def next(variables) do
Enum.filter(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule APAEWD840_node2 do
defmodule EWD840_node2 do
require Oracle

import APAEWD840
import EWD840

def next(variables) do
Enum.filter(
Expand Down
166 changes: 0 additions & 166 deletions elixir/lib/generated_code/jarros_de_agua.ex

This file was deleted.

21 changes: 11 additions & 10 deletions elixir/lib/generated_code/traffic_semaphores.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ defmodule TrafficSemaphores do
]
end
require Oracle
@semaphores "<value for SEMAPHORES>"
@semaphores MapSet.new([0, 1])
def semaphores, do: @semaphores


def turn_green_condition(variables, s) do
Enum.all?(@semaphores, fn(s2) -> variables[:colors][s2] == "red" end)
Enum.all?([Enum.all?(@semaphores, fn(s2) -> variables[:colors][s2] == "red" end), variables[:next_to_open] == s])
end

def turn_green(variables, s) do
%{
colors: Map.put(variables[:colors], s, "green"),
next_to_open: variables[:next_to_open]
colors: variables[:colors]|>Map.put(s, "green"),
next_to_open: rem((s + 1), (Enum.count(@semaphores)))
}
end

Expand All @@ -28,7 +28,7 @@ defmodule TrafficSemaphores do

def turn_yellow(variables, s) do
%{
colors: Map.put(variables[:colors], s, "yellow"),
colors: variables[:colors]|>Map.put(s, "yellow"),
next_to_open: variables[:next_to_open]
}
end
Expand All @@ -40,24 +40,25 @@ defmodule TrafficSemaphores do

def turn_red(variables, s) do
%{
colors: Map.put(variables[:colors], s, "red"),
colors: variables[:colors]|>Map.put(s, "red"),
next_to_open: variables[:next_to_open]
}
end


# "Spec": OperEx "AND" [OperEx "OPER_APP" [NameEx "Init"],OperEx "GLOBALLY" [OperEx "STUTTER" [OperEx "OPER_APP" [NameEx "Next"],OperEx "TUPLE" [NameEx "colors",NameEx "next_to_open"]]]]

def decide_action(oracle, variables, actions, step) do
different_states = Enum.uniq_by(actions, fn(action) -> action[:state] end)
different_states = Enum.uniq(Enum.map(actions, fn(action) -> action[:transition].(variables) end))

cond do
Enum.count(different_states) == 1 ->
Enum.at(actions, 0)[:state]
Enum.at(different_states, 0)
true ->
send oracle, {:choose, self(), actions}
send oracle, {:choose, self(), different_states}

receive do
{:ok, n} -> Enum.at(actions, n)[:state]
{:ok, n} -> Enum.at(different_states, n)
{:cancel} -> variables
{:stop} -> exit(0)
end
Expand Down
11 changes: 5 additions & 6 deletions elixir/lib/generated_code/traffic_semaphores_main.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ defmodule TrafficSemaphores_main do
def next(variables) do
Enum.filter(
List.flatten([
Enum.map(MapSet.new([0, 1]), fn (s) -> [
%{ action: "TurnGreen(#{inspect s})", condition: turn_green_condition(variables, s), state: turn_green(variables, s) },
%{ action: "TurnYellow(#{inspect s})", condition: turn_yellow_condition(variables, s), state: turn_yellow(variables, s) },
%{ action: "TurnRed(#{inspect s})", condition: turn_red_condition(variables, s), state: turn_red(variables, s) }
Enum.map(TrafficSemaphores.semaphores, fn (s) -> [
%{ action: "TurnGreen(#{inspect s})", condition: turn_green_condition(variables, s), transition: fn (variables) -> turn_green(variables, s) end },
%{ action: "TurnYellow(#{inspect s})", condition: turn_yellow_condition(variables, s), transition: fn (variables) -> turn_yellow(variables, s) end },
%{ action: "TurnRed(#{inspect s})", condition: turn_red_condition(variables, s), transition: fn (variables) -> turn_red(variables, s) end }
] end
)
]),
Expand All @@ -21,11 +21,10 @@ defmodule TrafficSemaphores_main do
shared_state = wait_lock(oracle)
variables = Map.merge(private_variables, shared_state)

IO.puts(inspect(variables))
actions = next(variables)

next_variables = decide_action(oracle, variables, actions, step)
send(oracle, {:notify, step, variables, next_variables})
send(oracle, {:notify, self(), variables, next_variables})
Process.sleep(2000)

main(oracle, next_variables, step + 1)
Expand Down
Loading

0 comments on commit 0f4fae3

Please sign in to comment.