├── .circleci └── config.yml ├── .github ├── ISSUE_TEMPLATE │ └── broken-model-issue.md └── workflows │ └── goreleaser.yml ├── .gitignore ├── .goreleaser.yaml ├── CHANGELOG.md ├── CONTRIBUTORS.md ├── LICENSE ├── Makefile ├── README.md ├── ast ├── ast.go ├── ast_test.go └── ast_utils.go ├── execute ├── execute.go ├── execute_test.go ├── format.go ├── format_test.go ├── history.go ├── parser │ ├── Makefile │ ├── SMTLIBv2.g4 │ ├── readme.md │ ├── smtlibv2_base_listener.go │ ├── smtlibv2_base_visitor.go │ ├── smtlibv2_lexer.go │ ├── smtlibv2_listener.go │ ├── smtlibv2_parser.go │ └── smtlibv2_visitor.go ├── responses.go └── responses_test.go ├── go.mod ├── go.sum ├── grammar ├── FaultLexer.g4 └── FaultParser.g4 ├── listener ├── listener.go ├── listener_error.go └── listener_test.go ├── llvm ├── alloc.go ├── compiler.go ├── llvm_test.go ├── llvm_xmisc_test.go ├── lr_asserts_test.go ├── name │ ├── name.go │ └── name_test.go ├── spec.go ├── spec_test.go └── variables │ ├── lookup.go │ └── pointers.go ├── main.go ├── parser ├── fault_lexer.go ├── fault_parser.go ├── faultparser_base_listener.go ├── faultparser_base_visitor.go ├── faultparser_listener.go └── faultparser_visitor.go ├── preprocess ├── preprocess.go ├── preprocess_errors_test.go ├── preprocess_test.go ├── record.go ├── record_errors_test.go └── record_test.go ├── reachability ├── reachability.go └── reachability_test.go ├── smt ├── asserts.go ├── asserts_test.go ├── forks │ └── forks.go ├── generator.go ├── generator_log_test.go ├── generator_test.go ├── generator_unit_test.go ├── llvm_passes.md ├── log │ └── log.go ├── rules │ ├── rules.go │ └── rules_test.go ├── testdata │ ├── asserts.fspec │ ├── bathtub.fspec │ ├── bathtub.smt2 │ ├── bathtub2.fspec │ ├── bathtub2.ll │ ├── bathtub2.smt2 │ ├── bathtub2opt.ll │ ├── booleans.fspec │ ├── booleans.smt2 │ ├── conditionals │ │ ├── condwelse.fspec │ │ ├── condwelse.smt2 │ │ ├── multicond.fspec │ │ ├── multicond.smt2 │ │ ├── multicond2.fspec │ │ ├── multicond2.smt2 │ │ ├── multicond3.fspec │ │ ├── multicond3.smt2 │ │ ├── multicond4.fspec │ │ ├── multicond4.smt2 │ │ ├── multicond5.fspec │ │ └── multicond5.smt2 │ ├── imports │ │ ├── circle_import.smt2 │ │ ├── circle_import1.fspec │ │ ├── circle_import2.fspec │ │ ├── renamed_import.fspec │ │ ├── renamed_import.smt2 │ │ ├── single_import.fspec │ │ └── single_import.smt2 │ ├── indexes.fspec │ ├── indexes.smt2 │ ├── indexes_assert.smt2 │ ├── indexes_branches.fspec │ ├── indexes_branches.smt2 │ ├── multifile1.fspec │ ├── multifile1.smt2 │ ├── multifile2.fspec │ ├── simple.fspec │ ├── simple.smt2 │ ├── statecharts │ │ ├── advanceand.fsystem │ │ ├── advanceand.smt2 │ │ ├── advanceor.fsystem │ │ ├── advanceor.smt2 │ │ ├── mixedcalls.fsystem │ │ ├── mixedcalls.smt2 │ │ ├── multioradvance.fsystem │ │ ├── multioradvance.smt2 │ │ ├── statechart.fsystem │ │ ├── statechart.smt2 │ │ ├── triggerfunc.fsystem │ │ └── triggerfunc.smt2 │ ├── strings.fspec │ ├── strings.smt2 │ ├── strings2.fspec │ ├── strings2.smt2 │ ├── swaps │ │ ├── swaps.fspec │ │ ├── swaps.smt2 │ │ ├── swaps1.fspec │ │ ├── swaps1.smt2 │ │ ├── swaps2.fspec │ │ └── swaps2.smt2 │ ├── unknowns.fspec │ └── unknowns.smt2 └── variables │ └── variables.go ├── swaps └── swaps.go ├── types ├── types.go └── types_test.go ├── util ├── util.go └── util_test.go └── visualize ├── visualize.go └── visualize_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Use the latest 2.1 version of CircleCI pipeline process engine. 2 | # See: https://circleci.com/docs/2.0/configuration-reference 3 | version: 2.1 4 | parameters: 5 | github: 6 | type: boolean 7 | default: true 8 | 9 | # Define a job to be invoked later in a workflow. 10 | # See: https://circleci.com/docs/2.0/configuration-reference/#jobs 11 | jobs: 12 | test: 13 | working_directory: ~/Fault 14 | docker: 15 | - image: cimg/go:1.19.2 16 | steps: 17 | - run: 18 | name: Install LLVM 19 | command: | 20 | sudo apt-get update 21 | sudo apt-get install llvm 22 | - checkout 23 | - restore_cache: 24 | keys: 25 | - go-mod-v5-{{ checksum "go.sum" }} 26 | - run: 27 | name: Install Dependencies 28 | command: 29 | go mod download 30 | - save_cache: 31 | key: go-mod-v5-{{ checksum "go.sum" }} 32 | paths: 33 | - "/go/pkg/mod" 34 | - run: 35 | name: Run tests 36 | command: | 37 | mkdir -p /tmp/test-reports 38 | gotestsum --junitfile /tmp/test-reports/unit-tests.xml $(go list ./... | grep -v fault/execute ) 39 | - store_test_results: 40 | path: /tmp/test-reports 41 | 42 | # mutate: 43 | # working_directory: ~/Fault 44 | # docker: 45 | # - image: cimg/go:1.17.5 46 | # parallelism: 4 47 | # steps: 48 | # - run: 49 | # name: Install LLVM 50 | # command: | 51 | # sudo apt-get update 52 | # sudo apt-get install llvm 53 | # sudo apt-get install git 54 | # - checkout 55 | # - restore_cache: 56 | # keys: 57 | # - go-mod-v5-{{ checksum "go.sum" }} 58 | # - run: 59 | # name: Install Dependencies 60 | # command: 61 | # go mod download 62 | # - save_cache: 63 | # key: go-mod-v5-{{ checksum "go.sum" }} 64 | # paths: 65 | # - "/go/pkg/mod" 66 | # - run: 67 | # name: Install go-mutesting 68 | # command: | 69 | # git clone https://github.com/mbellotti/go-mutesting.git 70 | # cd go-mutesting 71 | # go build cmd/go-mutesting/main.go 72 | # chmod +x main 73 | # sudo mv main /usr/local/bin/go-mutesting 74 | # - run: 75 | # name: Mutate and take over the world 76 | # command: | # Don't test the fuzzer or main.go, for the time being skip execute because it needs to be rewritten 77 | # mkdir -p /tmp/test-mutate-reports 78 | # go-mutesting $(go list ./... | grep -v fault/execute | grep -v fault/parser | grep -v "^fault$" | circleci tests split) --fail-only --score=.52 79 | # - store_test_results: 80 | # path: /tmp/test-mutate-reports 81 | 82 | 83 | # Invoke jobs via workflows 84 | # See: https://circleci.com/docs/2.0/configuration-reference/#workflows 85 | workflows: 86 | oncommit: # On commit run unit tests 87 | jobs: 88 | - test 89 | 90 | # mutations: # Scheduled pipeline, will run mutation tests once a week 91 | # unless: << pipeline.parameters.github >> 92 | # jobs: 93 | # - mutate 94 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/broken-model-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Broken Model Issue 3 | about: For submitting models that break the compiler 4 | title: '' 5 | labels: model 6 | assignees: mbellotti 7 | 8 | --- 9 | 10 | # Model Description 11 | 12 | ## Compiler Details 13 | - [ ] With Docker 14 | - [ ] Without Docker 15 | - [ ] Linux 16 | - [ ] MacOS 17 | - [ ] Windows 18 | 19 | ## Error Message 20 | _if the compiler returned an error message copy & paste it here_ 21 | 22 | ## Model Details 23 | ### Fsystem File 24 | _copy & paste fsystem file here (if any)_ 25 | 26 | ### Fspec files 27 | _copy & paste fspec files here (if any)_ 28 | -------------------------------------------------------------------------------- /.github/workflows/goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | # run only against tags 6 | tags: 7 | - '*' 8 | 9 | permissions: 10 | contents: write 11 | # packages: write 12 | # issues: write 13 | 14 | jobs: 15 | goreleaser: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - 19 | name: Checkout 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - run: git fetch --force --tags 24 | - 25 | name: Setup 26 | uses: actions/setup-go@v4 27 | with: 28 | go-version: stable 29 | # More assembly might be required: Docker logins, GPG, etc. It all depends 30 | # on your needs. 31 | - 32 | name: Import GPG key 33 | id: import_gpg 34 | uses: crazy-max/ghaction-import-gpg@v5 35 | with: 36 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 37 | passphrase: ${{ secrets.PASSPHRASE }} 38 | - 39 | name: Run GoReleaser 40 | uses: goreleaser/goreleaser-action@v4 41 | with: 42 | # either 'goreleaser' (default) or 'goreleaser-pro': 43 | distribution: goreleaser 44 | version: latest 45 | args: release --clean 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.FAULT_REPO_TOKEN }} 48 | # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' 49 | # distribution: 50 | # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Spec files 2 | *.spec 3 | 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # temp files 12 | fault.Dockerfile 13 | .vscode 14 | __debug_bin 15 | 16 | # Test binary, built with `go test -c` 17 | *.test 18 | *.tmp 19 | 20 | # Output of the go coverage tool, specifically when used with LiteIDE 21 | *.out 22 | 23 | # Dependency directories (remove the comment below to include it) 24 | # vendor/ 25 | 26 | # Exclude java files created by antlr during testing 27 | *.class 28 | *.java 29 | *.interp 30 | *.tokens 31 | grammar/Makefile 32 | 33 | __pycache__ 34 | dist/ 35 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | project_name: fault 2 | 3 | before: 4 | hooks: 5 | # You may remove this if you don't use go modules. 6 | - go mod tidy 7 | # you may remove this if you don't need go generate 8 | - go generate ./... 9 | builds: 10 | - env: 11 | - CGO_ENABLED=0 12 | goos: 13 | - linux 14 | - windows 15 | - darwin 16 | 17 | archives: 18 | - format: tar.gz 19 | # this name template makes the OS and Arch compatible with the results of uname. 20 | name_template: >- 21 | {{ .ProjectName }}_ 22 | {{- title .Os }}_ 23 | {{- if eq .Arch "amd64" }}x86_64 24 | {{- else if eq .Arch "386" }}i386 25 | {{- else }}{{ .Arch }}{{ end }} 26 | {{- if .Arm }}v{{ .Arm }}{{ end }} 27 | # use zip for windows archives 28 | format_overrides: 29 | - goos: windows 30 | format: zip 31 | checksum: 32 | name_template: 'checksums.txt' 33 | snapshot: 34 | name_template: "{{ incpatch .Version }}-next" 35 | changelog: 36 | sort: asc 37 | filters: 38 | exclude: 39 | - '^docs:' 40 | - '^test:' 41 | 42 | signs: 43 | - artifacts: checksum 44 | args: ["--batch", "-u", "{{ .Env.GPG_FINGERPRINT }}", "--output", "${signature}", "--detach-sign", "${artifact}"] 45 | # The lines beneath this are called `modelines`. See `:help modeline` 46 | # Feel free to remove those if you don't want/use them. 47 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json 48 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj 49 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Current Status (6/8/2023) 2 | Changing how imports work to allow for more complex specs. Also fixing some unrelated grammar ambiguities and parsing prioritization issues. 3 | 4 | #### (9/19/2023) 5 | Pushing a bunch of small patches and bug fixes in the ramp-up to presenting Fault at Strange Loop 2023 \o/ 6 | 7 | #### (8/31/2023) 8 | Been doing a lot of work on how model outputs are displayed. Needed to fix how dead branches are filtered first. The default display is now formatted like an event log. Also added a static format that just outputs variable values. 9 | 10 | #### (5/24/2023) 11 | Introducing strings to Fault, plus fixing some bugs in the behavior of imports. Strings in Fault behave like booleans and allow easily readable rules to be defined for the model checker to solve. They are not treated as immutable but haven't yet figured out how I want to syntax to change their state to look yet. 12 | 13 | #### (4/26/2023) 14 | Adding support for indexes (ie accessing historical values in the model) there are some edge cases around branches that need to be worked through. 15 | 16 | #### (4/13/2023) 17 | 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. 18 | 19 | #### (3/29/2023) 20 | Been doing a lot of work on improving how Fault is packaged and ultimately released. The Dockerfiles will remain available, but going to be stepping away from Docker as the preferred way to installing Fault in favor of a traditional build and release pipeline. 21 | 22 | #### (2/15/2023) 23 | Pretty substantial rewrite of assert and assumption rule generation. First ditched assumptions as a unique AST node and added an assume flag to AssertionStatements so they could be treated the same. Then modified the order of rules involving infixs and found several logic bugs from the old approach. 24 | 25 | Along the way, made changes to LLVM IR generation that allow rounds to be tracked so that Fault knows when two states of different variables coexist in time. Will eventually use the same approach to get rid of LLVM IR metadata for tracking concurrency, which will eliminate the issues with some of LLVM optimization passes removing metadata. 26 | 27 | #### (12/30/2022) 28 | Taking another stab at the interface question and display of results. Settled on using mermaid to visualize the solutions received from the solver which is much more useful than what I was trying otherwise. This reintroduces the dotviz generation from way back with the prototype so I've also included generated mermaid viz for the state machine and the active stock-flow subsystems. 29 | 30 | #### (11/30/2022) 31 | Trying to finish up state charts exposed some problems with conditionals that needed to be addressed, plus some bugs and funny edge cases. But state charts are now done, including a reachability analysis verifying that the system is appropriately specified that I think will be opt-in for now. 32 | 33 | #### (11/2/2022) 34 | While working on adding state chart support I finally hit the limit on what the half-assed namespace implementation I started with could support. So I ended up spending the entire month of October writing a preprocesser that annotated the AST with the proper ids for each nameable node, then integrating it into a compiler. It took a long time but the code in the LLVM compilation stage is so much cleaner and neater now. Along the way found some previously unknown bugs and added some more tests to bone up mutation scores (will need more of this later) 35 | 36 | #### (9/28/2022) 37 | Adding state chart support to the parser and LLVM compiler, plus implementing logic around "this", cleaning up some dead code, plus some minor adjustments here and there. 38 | 39 | #### (8/05/2022) 40 | In order to prepare for state chart organization, added support for Booleans. Also tweaked the syntax to allow values to be overwritten within a flow. 41 | 42 | #### (7/24/2022) 43 | For the past couples of months I've been stuck on the Fault interface. When Z3 returns results how should that be displayed so that the user understands the failure case described by the model? This is tricky because Z3 will solve for all defined variables, even the ones in branches not relevant to the rest of the model. 44 | Initially I was playing around with the idea of using Bubble Tea to do a more robust visual interface from the command line, but eventually a scrapped that as being too complicated. The real problem was filtering out inactive branches after the phi. I had a couple of pieced together approaches, but there was a lot of technical debt in figuring out SMT generation that made the code difficult to read and overwhelming. I finally decided to just completely rewrite the SMT package to make it a little bit easier to figure out and the bake in filtering of return values for the interface. 45 | 46 | #### (3/18/2022) 47 | Took another look at assert generation and made them tweaks. Found some bugs in unknown variables. Big deal is implemented temporal logic on assert generation. Now in addition to generating asserts for traditional temporal logic like "always", "eventually" and "eventually-always", Fault also has a set of specific temporal functions like "no more than" (nmt) and "no fewer than" (nft) which allow model checking a stable loop. 48 | 49 | #### (2/2/2022) 50 | In the process of adding support for unknown variables I realized I never fully connected the dots on uncertain values (whoops). So finished both of those although the logic around asserts over multiple instances is kind of wonky and brittle. May need a rethink. 51 | 52 | #### (1/26/2022) 53 | Removed the fuzzer for the time being. It wasn't really doing what I needed it to do and the newest version Go starts to roll out fuzzing functionality by default 🎉 As added some error handling around the lexer/parser. 54 | 55 | #### Status (1/7/2022) 56 | This started with an honest attempt to set up CI/CD on Fault's repo so that other people can start contributing, but debugging the CI/CD pipeline made me realize I have cross platform capability issues :facepalm:. Tried a bunch of different things and conveniently forgot to swash my commits before merging to main. All my dirty laundry is there to study! 57 | 58 | Anyway! Long story short: Fault now has an installer and runs on Docker. It also has support for alternative SMT solvers rather than a dependency on Z3 ... at least in theory! 59 | 60 | #### Status (12/05/2021) 61 | Development of Fault kind of goes like this: I write a spec and then implement whatever features are currently missing in order to get it to run. This time I added something unexpected: support for nestled stocks (stocks of stocks). Adds a little complexity but makes the specs look so much cleaner. 62 | 63 | #### Status (12/01/2021) 64 | Using Go channels to compile parallel runs seemed like a clevel solution, but truthfully the problems with correctly handling SSA weren't offset by any benefits in performance. Might revist in the future as models grow more complex. For right now plain ordinary sequencial processing of all premutations works well. 65 | 66 | Other major thing is parsing the SMT returned by the solver and formatting those results into a human friendly form. Laid some of the ground work on Uncertain types. 67 | 68 | #### Status (10/11/2021) 69 | Just finished the happy path on conditionals, want to shift to spec imports next. Still have to test LLVM IR -> SMTLib2 after LLVM optimization passes. 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Marianne Bellotti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME := fault-lang/fault 2 | TAG := $$(git log -1 --pretty=%h) 3 | IMG := ${NAME}:${TAG} 4 | LATEST := ${NAME}:latest 5 | VERSION := $$(git describe --tags --abbrev=0) 6 | 7 | fault-z3: 8 | $(shell touch "fault.Dockerfile") 9 | cat Dockerfile ./solvers/z3.Dockerfile > fault.Dockerfile 10 | @docker build -t ${NAME}-z3:${TAG} --no-cache --build-arg BUILD_VERSION=${VERSION} --build-arg BUILD_DATE=$(date) -f fault.Dockerfile . 11 | @docker tag ${NAME}-z3:${TAG} ${NAME}-z3:latest 12 | @rm fault.Dockerfile 13 | @cp fault-lang.sh /usr/local/bin/fault 14 | 15 | image: 16 | @docker build -t ${IMG} . 17 | @docker tag ${IMG} ${LATEST} 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fault 2 | Fault is a modeling language for building system dynamic models and checking them using a combination of first order logic and probability 3 | 4 | ## Project Status 5 | Pre-alpha. 6 | 7 | ## Install 8 | Fault can be built from source if you like, but the best way to install Fault is by [downloading the correct release for your machine](https://github.com/Fault-lang/Fault/releases). 9 | 10 | Once installed the model checker of Fault needs access to a SMT solver, otherwise Fault will default to generating SMT of models only. Microsoft's Z3 is the recommended solver at this time and [can be downloaded here](https://github.com/Z3Prover/z3/releases) 11 | 12 | Then in order for Fault to find your solver you need to set two configuration variables 13 | 14 | ``` 15 | export SOLVERCMD="z3" 16 | export SOLVERARG="-in" 17 | ``` 18 | 19 | For other install options please [see the Fault documentation](https://www.fault.tech) 20 | 21 | ## Why "Fault"? 22 | It is not possible to completely specify a system. All specifications must decide what parts of the system are in-scope and out-of-scope, and at what level of detail. Many formal specification approaches are designed to prove the system correct and it is very easy for an inexperienced practitioner to write a bad spec that gives a thumbs up to a flawed system. 23 | 24 | Instead Fault is designed with the assumption that all systems will fail at some point, under some set of conditions. The name Fault was chosen to emphasize this point for users: Fault models that return no failure points are bad models. The user should keep trying until they've built a model that produces interesting and compelling failure scenarios. 25 | 26 | ## Origin Story 27 | The development Fault is documented in the series "Marianne Writes a Programming Language": 28 | 29 | - [audio](https://anchor.fm/mwapl) 30 | - [transcripts](https://dev.to/bellmar/series/9711) 31 | -------------------------------------------------------------------------------- /ast/ast_utils.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/antlr4-go/antlr/v4" 8 | ) 9 | 10 | func GenerateToken(token string, literal string, start antlr.Token, stop antlr.Token) Token { 11 | return Token{ 12 | Type: TokenType(token), 13 | Literal: literal, 14 | Position: []int{start.GetLine(), 15 | start.GetColumn(), 16 | stop.GetLine(), 17 | stop.GetColumn(), 18 | }, 19 | } 20 | } 21 | 22 | func Preparse(pairs map[*Identifier]Expression) map[string]Node { 23 | properties := make(map[string]Node) 24 | for k, v := range pairs { 25 | id := strings.TrimSpace(k.String()) 26 | properties[id] = v 27 | } 28 | return properties 29 | } 30 | 31 | func MergeNodeMaps(m1 map[string]Node, m2 map[string]Node) map[string]Node { 32 | for k, v := range m2 { 33 | m1[k] = v 34 | } 35 | return m1 36 | } 37 | 38 | func Keys(m map[string]Node) []string { 39 | var ret []string 40 | for k := range m { 41 | ret = append(ret, k) 42 | } 43 | return ret 44 | } 45 | 46 | func ExtractBranches(b map[string]*StructProperty) map[string]Node { 47 | ret := make(map[string]Node) 48 | for k, v := range b { 49 | ret[k] = v.Value 50 | } 51 | return ret 52 | } 53 | 54 | func WrapBranches(b map[string]Node) map[string]*StructProperty { 55 | ret := make(map[string]*StructProperty) 56 | for k, v := range b { 57 | rawid := v.(Nameable).RawId() 58 | ret[k] = &StructProperty{Value: v} 59 | ret[k].ProcessedName = rawid 60 | ret[k].SetType(&Type{Type: v.Type()}) 61 | ret[k].Spec = rawid[0] 62 | ret[k].Name = k 63 | } 64 | return ret 65 | } 66 | 67 | func evalFloat(f1 float64, f2 float64, op string) float64 { 68 | switch op { 69 | case "+": 70 | return f1 + f2 71 | case "-": 72 | return f1 - f2 73 | case "*": 74 | return f1 * f2 75 | case "/": 76 | return f1 / f2 77 | default: 78 | panic(fmt.Sprintf("unsupported operator %s", op)) 79 | } 80 | } 81 | 82 | func evalInt(i1 int64, i2 int64, op string) int64 { 83 | switch op { 84 | case "+": 85 | return i1 + i2 86 | case "-": 87 | return i1 - i2 88 | case "*": 89 | return i1 * i2 90 | default: 91 | panic(fmt.Sprintf("unsupported operator %s", op)) 92 | } 93 | } 94 | 95 | func IsCompare(op string) bool { 96 | switch op { 97 | case ">": 98 | return true 99 | case "<": 100 | return true 101 | case "==": 102 | return true 103 | case "!=": 104 | return true 105 | case "<=": 106 | return true 107 | case ">=": 108 | return true 109 | case "&&": 110 | return true 111 | case "||": 112 | return true 113 | case "!": 114 | return true 115 | default: 116 | return false 117 | } 118 | } 119 | 120 | func Evaluate(n *InfixExpression) Expression { 121 | if IsCompare(n.Operator) { 122 | return n 123 | } 124 | f1, ok1 := n.Left.(*FloatLiteral) 125 | i1, ok2 := n.Left.(*IntegerLiteral) 126 | 127 | if !ok1 && !ok2 { 128 | return n 129 | } 130 | 131 | f2, ok1 := n.Right.(*FloatLiteral) 132 | i2, ok2 := n.Right.(*IntegerLiteral) 133 | 134 | if !ok1 && !ok2 { 135 | return n 136 | } 137 | 138 | if f1 != nil { 139 | if f2 != nil { 140 | v := evalFloat(f1.Value, f2.Value, n.Operator) 141 | return &FloatLiteral{ 142 | Token: n.Token, 143 | Value: v, 144 | } 145 | } else { 146 | v := evalFloat(f1.Value, float64(i2.Value), n.Operator) 147 | return &FloatLiteral{ 148 | Token: n.Token, 149 | Value: v, 150 | } 151 | } 152 | } else { 153 | if f2 != nil { 154 | v := evalFloat(float64(i1.Value), f2.Value, n.Operator) 155 | return &FloatLiteral{ 156 | Token: n.Token, 157 | Value: v, 158 | } 159 | } else { 160 | if n.Operator == "/" { 161 | //Return a float in the case of division 162 | v := evalFloat(float64(i1.Value), float64(i2.Value), n.Operator) 163 | return &FloatLiteral{ 164 | Token: n.Token, 165 | Value: v, 166 | } 167 | } 168 | v := evalInt(i1.Value, i2.Value, n.Operator) 169 | return &IntegerLiteral{ 170 | Token: n.Token, 171 | Value: v, 172 | } 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /execute/execute_test.go: -------------------------------------------------------------------------------- 1 | package execute 2 | 3 | import ( 4 | resultlog "fault/smt/log" 5 | "fault/smt/variables" 6 | "testing" 7 | ) 8 | 9 | func TestSMTOk(t *testing.T) { 10 | test := `(declare-fun imports_fl3_vault_value_0 () Real) 11 | (declare-fun imports_fl3_vault_value_1 () Real) 12 | (declare-fun imports_fl3_vault_value_2 () Real)(assert (= imports_fl3_vault_value_0 30.0)) 13 | (assert (= imports_fl3_vault_value_1 (+ imports_fl3_vault_value_0 10.0))) 14 | (assert (= imports_fl3_vault_value_2 (+ imports_fl3_vault_value_1 10.0))) 15 | ` 16 | model := prepTest(test, make(map[string][]float64), []string{}, map[string][]*variables.VarChange{}) 17 | 18 | response, err := model.Check() 19 | 20 | if err != nil { 21 | t.Fatalf("SMT Solver failed on valid expression. got=%s", err) 22 | } 23 | 24 | if !response { 25 | t.Fatalf("SMT Solver failed on valid expression.") 26 | } 27 | 28 | solution, err := model.Solve() 29 | 30 | if err != nil { 31 | t.Fatalf("SMT Solver failed to provide solution. got=%s", err) 32 | } 33 | 34 | if solution["imports_fl3_vault_value"] == nil { 35 | t.Fatal("SMT Solver failed to provide solution.") 36 | } 37 | 38 | got := solution["imports_fl3_vault_value"].(*FloatTrace).Get() 39 | expected := map[int64]float64{0: 30.0, 1: 40.0, 2: 50.0} 40 | if got[0] != expected[0] { 41 | t.Fatalf("SMT Solver solution not expected. want=%f got=%f", expected[0], got[0]) 42 | } 43 | } 44 | 45 | func TestProbability(t *testing.T) { 46 | test := `(declare-fun imports_fl3_vault_value_0 () Real) 47 | (declare-fun imports_fl3_vault_value_1 () Real) 48 | (declare-fun imports_fl3_vault_value_2 () Real)(assert (= imports_fl3_vault_value_0 30.0)) 49 | (assert (= imports_fl3_vault_value_1 (+ imports_fl3_vault_value_0 10.0))) 50 | (assert (= imports_fl3_vault_value_2 (+ imports_fl3_vault_value_1 10.0))) 51 | ` 52 | uncertains := make(map[string][]float64) 53 | uncertains["imports_fl3_vault_value"] = []float64{30.0, 5} 54 | 55 | model := prepTest(test, uncertains, []string{}, map[string][]*variables.VarChange{}) 56 | 57 | model.Check() 58 | solution, _ := model.Solve() 59 | filter := model.Filter(solution) 60 | got := filter["imports_fl3_vault_value"].(*FloatTrace).GetWeights() 61 | expected := map[int64]float64{0: 0.07978845608028654, 1: 0.010798193302637605, 2: 2.6766045152977058e-05} 62 | if got[0] != expected[0] { 63 | t.Fatalf("Probability distribution not weighting correctly. want=%f got=%f", expected[0], got[0]) 64 | } 65 | 66 | } 67 | 68 | func TestEventLog(t *testing.T) { 69 | test := `(declare-fun imports_fl3_vault_value_0 () Real) 70 | (declare-fun imports_fl3_vault_value_1 () Real) 71 | (declare-fun imports_fl3_vault_value_2 () Real)(assert (= imports_fl3_vault_value_0 30.0)) 72 | (assert (= imports_fl3_vault_value_1 (+ imports_fl3_vault_value_0 10.0))) 73 | (assert (= imports_fl3_vault_value_2 (+ imports_fl3_vault_value_1 10.0))) 74 | ` 75 | model := prepTest(test, make(map[string][]float64), []string{}, map[string][]*variables.VarChange{}) 76 | 77 | model.Log = resultlog.NewLog() 78 | model.Log.Add(resultlog.NewInit(0, "", "imports_fl3_vault_value_0")) 79 | model.Log.Add(resultlog.NewInit(0, "", "imports_fl3_vault_value_1")) 80 | model.Log.Add(resultlog.NewInit(0, "", "imports_fl3_vault_value_2")) 81 | model.Log.Add(resultlog.NewChange(0, "", "imports_fl3_vault_value_1")) 82 | model.Log.Add(resultlog.NewChange(0, "", "imports_fl3_vault_value_2")) 83 | 84 | response, err := model.Check() 85 | 86 | if err != nil { 87 | t.Fatalf("SMT Solver failed on valid expression. got=%s", err) 88 | } 89 | 90 | if !response { 91 | t.Fatalf("SMT Solver failed on valid expression.") 92 | } 93 | 94 | solution, err := model.Solve() 95 | 96 | if err != nil { 97 | t.Fatalf("SMT Solver failed to provide solution. got=%s", err) 98 | } 99 | 100 | if solution["imports_fl3_vault_value"] == nil { 101 | t.Fatal("SMT Solver failed to provide solution.") 102 | } 103 | 104 | model.mapToLog("imports_fl3_vault_value", solution["imports_fl3_vault_value"]) 105 | 106 | if model.Log.Events[0].String() != "0,INIT,,imports_fl3_vault_value_0,,30,\n" { 107 | t.Fatalf("Incorrect event log format at index 0 got=%s", model.Log.Events[0].String()) 108 | } 109 | 110 | if model.Log.Events[1].String() != "0,INIT,,imports_fl3_vault_value_1,,,\n" { 111 | t.Fatalf("Incorrect event log format at index 1 got=%s", model.Log.Events[1].String()) 112 | } 113 | 114 | if model.Log.Events[3].String() != "0,CHANGE,,imports_fl3_vault_value_1,,40,\n" { 115 | t.Fatalf("Incorrect event log format at index 3 got=%s", model.Log.Events[3].String()) 116 | } 117 | } 118 | 119 | func TestEval(t *testing.T) { 120 | mc := NewModelChecker() 121 | mc.Log = resultlog.NewLog() 122 | 123 | a := &resultlog.Assert{ 124 | Left: &resultlog.BoolClause{Value: true}, 125 | Right: &resultlog.BoolClause{Value: true}, 126 | Op: "and", 127 | } 128 | if !mc.Eval(a) { 129 | t.Fatalf("Incorrect evaluation got=%v", mc.Eval(a)) 130 | } 131 | 132 | a1 := &resultlog.Assert{ 133 | Left: &resultlog.BoolClause{Value: true}, 134 | Right: &resultlog.BoolClause{Value: false}, 135 | Op: "=", 136 | } 137 | if mc.Eval(a1) { 138 | t.Fatalf("Incorrect evaluation got=%v", mc.Eval(a1)) 139 | } 140 | 141 | a2 := &resultlog.Assert{ 142 | Left: &resultlog.FlClause{Value: 2.0}, 143 | Right: &resultlog.FlClause{Value: 5.0}, 144 | Op: ">", 145 | } 146 | if mc.Eval(a2) { 147 | t.Fatalf("Incorrect evaluation got=%v", mc.Eval(a2)) 148 | } 149 | } 150 | 151 | func TestEvalAmbiguous(t *testing.T) { 152 | mc := NewModelChecker() 153 | mc.Log = resultlog.NewLog() 154 | 155 | a := &resultlog.Assert{ 156 | Left: &resultlog.BoolClause{Value: true}, 157 | Right: &resultlog.BoolClause{Value: true}, 158 | Op: "=", 159 | } 160 | if !mc.EvalAmbiguous(a) { 161 | t.Fatalf("Incorrect evaluation got=%v", mc.Eval(a)) 162 | } 163 | 164 | a1 := &resultlog.Assert{ 165 | Left: &resultlog.FlClause{Value: 2.0}, 166 | Right: &resultlog.FlClause{Value: 2.0}, 167 | Op: "=", 168 | } 169 | if !mc.EvalAmbiguous(a) { 170 | t.Fatalf("Incorrect evaluation got=%v", mc.Eval(a1)) 171 | } 172 | 173 | } 174 | 175 | func TestEvalClause(t *testing.T) { 176 | mc := NewModelChecker() 177 | mc.Log = resultlog.NewLog() 178 | 179 | mc.ResultValues["test_var_foo"] = "false" 180 | 181 | cf := &resultlog.FlClause{ 182 | Value: 5.0, 183 | } 184 | 185 | a, err := mc.EvalClause(cf) 186 | 187 | if err == nil { 188 | t.Fatalf("Incorrect evaluation got=%v", a) 189 | } 190 | 191 | cb := &resultlog.BoolClause{ 192 | Value: true, 193 | } 194 | 195 | a1, err := mc.EvalClause(cb) 196 | 197 | if !a1 { 198 | t.Fatalf("Incorrect evaluation got=%v", a1) 199 | } 200 | 201 | cs := &resultlog.StringClause{ 202 | Value: "test_var_foo", 203 | } 204 | 205 | a2, err := mc.EvalClause(cs) 206 | 207 | if a2 { 208 | t.Fatalf("Incorrect evaluation got=%v", a2) 209 | } 210 | } 211 | 212 | func prepTest(smt string, uncertains map[string][]float64, unknowns []string, results map[string][]*variables.VarChange) *ModelChecker { 213 | ex := NewModelChecker() 214 | ex.LoadModel(smt, uncertains, unknowns, results, &resultlog.ResultLog{}) 215 | return ex 216 | } 217 | -------------------------------------------------------------------------------- /execute/history.go: -------------------------------------------------------------------------------- 1 | package execute 2 | 3 | type Branch struct { 4 | trail []int16 5 | phi int16 6 | base string 7 | } 8 | 9 | func (b *Branch) End() int16 { 10 | return b.trail[len(b.trail)-1] 11 | } 12 | 13 | func (b *Branch) InTrail(i int16) bool { 14 | for _, v := range b.trail { 15 | if i == v { 16 | return true 17 | } 18 | } 19 | return false 20 | } 21 | 22 | type Scenario interface{} 23 | 24 | type FloatTrace struct { 25 | Scenario 26 | Base string 27 | results map[int16]float64 28 | weights map[int16]float64 29 | } 30 | 31 | func NewFloatTrace() *FloatTrace { 32 | return &FloatTrace{ 33 | results: make(map[int16]float64), 34 | weights: make(map[int16]float64), 35 | } 36 | } 37 | 38 | func (ft *FloatTrace) Index(i int16) (float64, bool) { 39 | v, ok := ft.results[i] 40 | return v, ok 41 | } 42 | 43 | func (ft *FloatTrace) Get() map[int16]float64 { 44 | return ft.results 45 | } 46 | 47 | func (ft *FloatTrace) GetWeights() map[int16]float64 { 48 | return ft.weights 49 | } 50 | 51 | func (ft *FloatTrace) Add(i int16, f float64) { 52 | ft.results[i] = f 53 | } 54 | 55 | func (ft *FloatTrace) AddWeight(i int16, f float64) { 56 | ft.weights[i] = f 57 | } 58 | 59 | func (ft *FloatTrace) Remove(i int16) { 60 | delete(ft.results, i) 61 | delete(ft.weights, i) 62 | } 63 | 64 | type IntTrace struct { 65 | Scenario 66 | Base string 67 | results map[int16]int64 68 | weights map[int16]float64 69 | } 70 | 71 | func NewIntTrace() *IntTrace { 72 | return &IntTrace{ 73 | results: make(map[int16]int64), 74 | weights: make(map[int16]float64), 75 | } 76 | } 77 | 78 | func (it *IntTrace) Index(i int16) (int64, bool) { 79 | v, ok := it.results[i] 80 | return v, ok 81 | } 82 | 83 | func (it *IntTrace) Get() map[int16]int64 { 84 | return it.results 85 | } 86 | 87 | func (it *IntTrace) GetWeights() map[int16]float64 { 88 | return it.weights 89 | } 90 | 91 | func (it *IntTrace) Add(i int16, in int64) { 92 | it.results[i] = in 93 | } 94 | 95 | func (it *IntTrace) AddWeight(i int16, f float64) { 96 | it.weights[i] = f 97 | } 98 | 99 | func (it *IntTrace) Remove(i int16) { 100 | delete(it.results, i) 101 | delete(it.weights, i) 102 | } 103 | 104 | type BoolTrace struct { 105 | Scenario 106 | Base string 107 | results map[int16]bool 108 | weights map[int16]float64 109 | } 110 | 111 | func NewBoolTrace() *BoolTrace { 112 | return &BoolTrace{ 113 | results: make(map[int16]bool), 114 | weights: make(map[int16]float64), 115 | } 116 | } 117 | 118 | func (bt *BoolTrace) Index(i int16) (bool, bool) { 119 | v, ok := bt.results[i] 120 | return v, ok 121 | } 122 | 123 | func (bt *BoolTrace) Get() map[int16]bool { 124 | return bt.results 125 | } 126 | 127 | func (bt *BoolTrace) GetWeights() map[int16]float64 { 128 | return bt.weights 129 | } 130 | 131 | func (bt *BoolTrace) Add(i int16, b bool) { 132 | bt.results[i] = b 133 | } 134 | 135 | func (bt *BoolTrace) AddWeight(i int16, f float64) { 136 | bt.weights[i] = f 137 | } 138 | 139 | func (bt *BoolTrace) Remove(i int16) { 140 | delete(bt.results, i) 141 | delete(bt.weights, i) 142 | } 143 | -------------------------------------------------------------------------------- /execute/parser/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all smtparser java gui clean 2 | 3 | all: smtparser java 4 | 5 | java: 6 | @command -v antlr >/dev/null 2>&1 || { echo >&2 "I require antlr but it's not installed or not in PATH. Aborting."; exit 1; } 7 | antlr SMTLIBv2.g4 8 | javac -classpath /opt/homebrew/Cellar/antlr/4.13.1/antlr-4.13.1-complete.jar *.java 9 | 10 | 11 | gui: 12 | @command -v antlr >/dev/null 2>&1 || { echo >&2 "I require antlr but it's not installed or not in PATH. Aborting."; exit 1; } 13 | grun SMTLIBv2 start -gui 14 | 15 | smtparser: 16 | @command -v antlr >/dev/null 2>&1 || { echo >&2 "I require antlr but it's not installed or not in PATH. Aborting."; exit 1; } 17 | antlr SMTLIBv2.g4 -Dlanguage=Go -visitor -o ../parser 18 | 19 | clean: 20 | rm -rf *.class *.java *.tokens *.interp *.go -------------------------------------------------------------------------------- /execute/parser/readme.md: -------------------------------------------------------------------------------- 1 | # SMTLib2 Parser 2 | ANTLR Grammar for SMTLib2. Not to be confused with Fault's parser, this parsers the responses from the SMT solver while the model checker is running 3 | 4 | If updating from source repo, note that `string` and `String` need to be changed to `string_` and `String_` respectively in order to compile the Go files correctly. 5 | 6 | Source Repo: 7 | https://github.com/antlr/grammars-v4/blob/master/smtlibv2/SMTLIBv2.g4 -------------------------------------------------------------------------------- /execute/responses.go: -------------------------------------------------------------------------------- 1 | package execute 2 | 3 | import ( 4 | "fault/execute/parser" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type SMTListener struct { 11 | *parser.BaseSMTLIBv2Listener 12 | stack []interface{} 13 | Results map[string]Scenario 14 | Values map[string]string 15 | } 16 | 17 | func NewSMTListener() *SMTListener { 18 | return &SMTListener{ 19 | Results: make(map[string]Scenario), 20 | Values: make(map[string]string), 21 | } 22 | } 23 | 24 | func (l *SMTListener) push(n interface{}) { 25 | l.stack = append(l.stack, n) 26 | } 27 | 28 | func (l *SMTListener) pop() interface{} { 29 | var s interface{} 30 | if len(l.stack) > 0 { 31 | s, l.stack = l.stack[len(l.stack)-1], l.stack[:len(l.stack)-1] 32 | return s 33 | } 34 | return nil 35 | } 36 | 37 | func (l *SMTListener) peek() interface{} { 38 | if len(l.stack) > 0 { 39 | return l.stack[len(l.stack)-1] 40 | } 41 | return nil 42 | } 43 | 44 | func mergeTermParts(parts []string) string { 45 | if len(parts) == 1 { 46 | return parts[0] 47 | } 48 | 49 | if len(parts) > 2 { 50 | panic("Too many term parts received") 51 | } 52 | 53 | value1, err := strconv.ParseFloat(parts[0], 64) 54 | if err != nil { 55 | return strings.Join(parts, "") // a negative value 56 | } 57 | 58 | value2, err := strconv.ParseFloat(parts[1], 64) 59 | if err != nil { 60 | panic("unclear term part") 61 | } 62 | 63 | value3 := value1 / value2 64 | return fmt.Sprintf("%f", value3) 65 | } 66 | 67 | func (l *SMTListener) ExitGet_model_response(c *parser.Get_model_responseContext) { 68 | 69 | } 70 | 71 | func (l *SMTListener) ExitModel_response(c *parser.Model_responseContext) { 72 | 73 | } 74 | 75 | func (l *SMTListener) ExitFunction_def(c *parser.Function_defContext) { 76 | term := l.pop() 77 | sort := l.pop() 78 | sym := l.pop() 79 | 80 | t := term.(string) 81 | if string(t[0]) == "(" { // Happens in negative values 82 | t = t[1 : len(t)-2] 83 | } 84 | 85 | l.Values[sym.(string)] = t 86 | 87 | value := convertTerm(sort.(string), t) 88 | key, id := splitIdent(sym.(string)) 89 | i, err := strconv.ParseInt(key, 10, 16) 90 | k := int16(i) 91 | if err != nil { 92 | panic(fmt.Sprintf("symbol returned from model is malformed. got=%s", sym.(string))) 93 | } 94 | 95 | switch v := value.(type) { 96 | case float64: 97 | if l.Results[id] != nil { 98 | l.Results[id].(*FloatTrace).Add(k, v) 99 | } else { 100 | l.Results[id] = NewFloatTrace() 101 | l.Results[id].(*FloatTrace).Add(k, v) 102 | } 103 | case bool: 104 | if l.Results[id] != nil { 105 | l.Results[id].(*BoolTrace).Add(k, v) 106 | } else { 107 | l.Results[id] = NewBoolTrace() 108 | l.Results[id].(*BoolTrace).Add(k, v) 109 | } 110 | 111 | case int64: 112 | if l.Results[id] != nil { 113 | l.Results[id].(*IntTrace).Add(k, v) 114 | } else { 115 | l.Results[id] = NewIntTrace() 116 | l.Results[id].(*IntTrace).Add(k, v) 117 | } 118 | } 119 | } 120 | 121 | func (l *SMTListener) ExitVariable(c *parser.VariableContext) { 122 | l.push(c.GetText()) 123 | } 124 | 125 | func (l *SMTListener) ExitTerm(c *parser.TermContext) { 126 | term := c.GetText() 127 | 128 | if c.GetChildCount() > 1 { 129 | parts := []string{} 130 | for i := 0; i < len(c.AllTerm()); i++ { 131 | p := l.pop() 132 | parts = append([]string{p.(string)}, parts...) 133 | } 134 | merge := mergeTermParts(parts) 135 | if strings.Contains(term, "-") { 136 | term = fmt.Sprintf("-%s", merge) 137 | } else { 138 | term = merge 139 | } 140 | } 141 | l.push(term) 142 | } 143 | 144 | func (l *SMTListener) ExitSort(c *parser.SortContext) { 145 | l.push(c.GetText()) 146 | } 147 | 148 | func convertTerm(sort string, term string) interface{} { 149 | var value interface{} 150 | var err error 151 | 152 | switch sort { 153 | case "Real": 154 | value, err = strconv.ParseFloat(term, 64) 155 | if err != nil { 156 | panic(err) 157 | } 158 | case "Bool": 159 | if term == "true" { 160 | value = true 161 | } else if term == "false" { 162 | value = false 163 | } else { 164 | panic(fmt.Sprintf("bool not a valid bool. got=%s", term)) 165 | } 166 | case "Int": 167 | value, err = strconv.ParseInt(term, 10, 64) 168 | if err != nil { 169 | panic(err) 170 | } 171 | default: 172 | value = term 173 | } 174 | return value 175 | } 176 | 177 | func splitIdent(ident string) (string, string) { 178 | s := strings.Split(ident, "_") 179 | return s[len(s)-1], strings.Join(s[0:len(s)-1], "_") 180 | } 181 | -------------------------------------------------------------------------------- /execute/responses_test.go: -------------------------------------------------------------------------------- 1 | package execute 2 | 3 | import ( 4 | "fault/execute/parser" 5 | "testing" 6 | 7 | "github.com/antlr4-go/antlr/v4" 8 | 9 | ) 10 | 11 | func TestParseReals(t *testing.T) { 12 | test := `(model 13 | (define-fun imports_fl3_vault_value_2 () Real 14 | 50.0) 15 | ) 16 | ` 17 | response := prepTestParser(test) 18 | 19 | if response["imports_fl3_vault_value"] == nil { 20 | t.Fatalf("SMT parser failed to parse reals in solution returned. got=%s", response) 21 | } 22 | } 23 | 24 | func TestParsePrecise(t *testing.T) { 25 | test := `(model 26 | (define-fun imports_fl3_vault_value_2 () Real 27 | (/ 3.0 20.0)) 28 | ) 29 | ` 30 | response := prepTestParser(test) 31 | 32 | if response["imports_fl3_vault_value"] == nil { 33 | t.Fatalf("SMT parser failed to parse reals in solution returned. got=%s", response) 34 | } 35 | } 36 | 37 | func TestParseNegPrecise(t *testing.T) { 38 | test := `(model 39 | (define-fun imports_fl3_vault_value_2 () Real 40 | (-(/ 3.0 20.0))) 41 | ) 42 | ` 43 | response := prepTestParser(test) 44 | 45 | if response["imports_fl3_vault_value"] == nil { 46 | t.Fatalf("SMT parser failed to parse reals in solution returned. got=%s", response) 47 | } 48 | } 49 | 50 | func TestParseNeg(t *testing.T) { 51 | test := `(model 52 | (define-fun imports_fl3_vault_value_2 () Real 53 | (- 20.0)) 54 | ) 55 | ` 56 | response := prepTestParser(test) 57 | 58 | if response["imports_fl3_vault_value"] == nil { 59 | t.Fatalf("SMT parser failed to parse reals in solution returned. got=%s", response) 60 | } 61 | } 62 | 63 | func TestParseInts(t *testing.T) { 64 | test := `(model 65 | (define-fun imports_fl3_vault_value_2 () Int 66 | 50) 67 | ) 68 | ` 69 | response := prepTestParser(test) 70 | 71 | if response["imports_fl3_vault_value"] == nil { 72 | t.Fatalf("SMT parser failed to parse int in solution returned. got=%s", response) 73 | } 74 | } 75 | 76 | func TestParseBools(t *testing.T) { 77 | test := `(model 78 | (define-fun imports_fl3_vault_value_2 () Bool 79 | true) 80 | ) 81 | ` 82 | response := prepTestParser(test) 83 | 84 | if response["imports_fl3_vault_value"] == nil { 85 | t.Fatalf("SMT parser failed to parse bools in solution returned. got=%s", response) 86 | } 87 | } 88 | 89 | func prepTestParser(response string) map[string]Scenario { 90 | is := antlr.NewInputStream(response) 91 | lexer := parser.NewSMTLIBv2Lexer(is) 92 | stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) 93 | 94 | p := parser.NewSMTLIBv2Parser(stream) 95 | l := NewSMTListener() 96 | antlr.ParseTreeWalkerDefault.Walk(l, p.Start_()) 97 | return l.Results 98 | } 99 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module fault 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df 7 | github.com/google/uuid v1.3.0 8 | github.com/llir/llvm v0.3.3 9 | github.com/olekukonko/tablewriter v0.0.5 10 | gonum.org/v1/gonum v0.9.3 11 | ) 12 | 13 | require ( 14 | github.com/antlr4-go/antlr/v4 v4.13.1 // indirect 15 | github.com/llir/ll v0.0.0-20210426224459-a0543cd69183 // indirect 16 | github.com/mattn/go-runewidth v0.0.13 // indirect 17 | github.com/mewmew/float v0.0.0-20201204173432-505706aa38fa // indirect 18 | github.com/pkg/errors v0.9.1 // indirect 19 | github.com/rivo/uniseg v0.2.0 // indirect 20 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect 21 | golang.org/x/mod v0.17.0 // indirect 22 | golang.org/x/sys v0.20.0 // indirect 23 | golang.org/x/tools v0.21.0 // indirect 24 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /grammar/FaultParser.g4: -------------------------------------------------------------------------------- 1 | parser grammar FaultParser; 2 | 3 | options { 4 | tokenVocab=FaultLexer; 5 | } 6 | 7 | /* 8 | State charts of the whole system 9 | */ 10 | 11 | sysSpec 12 | : sysClause importDecl* globalDecl* componentDecl* (assertion | assumption | stringDecl)* startBlock? forStmt? 13 | ; 14 | 15 | sysClause 16 | : 'system' IDENT eos 17 | ; 18 | 19 | globalDecl 20 | : 'global' IDENT '=' operand eos (swap eos)* 21 | ; 22 | 23 | swap 24 | : paramCall '=' (functionLit | numeric | string_ | bool_ | operandName | prefix | solvable) 25 | ; 26 | 27 | componentDecl 28 | : 'component' IDENT '=' 'states' '{' (comProperties ',')* '}' eos 29 | ; 30 | 31 | startBlock 32 | : 'start' '{' (startPair ',')* '}' eos 33 | ; 34 | 35 | startPair 36 | : IDENT ':' IDENT 37 | ; 38 | /* 39 | Individual specs of state changes 40 | */ 41 | 42 | spec 43 | : specClause declaration* forStmt? 44 | ; 45 | 46 | specClause 47 | : 'spec' IDENT eos 48 | ; 49 | 50 | importDecl 51 | : 'import' (importSpec | '(' importSpec* ')') eos 52 | ; 53 | 54 | importSpec 55 | : ('.' | IDENT)? importPath ','? 56 | ; 57 | 58 | importPath 59 | : string_ 60 | ; 61 | 62 | declaration 63 | : importDecl 64 | | constDecl 65 | | structDecl 66 | | assertion 67 | | assumption 68 | | stringDecl 69 | ; 70 | 71 | comparison 72 | : EQUALS 73 | | NOT_EQUALS 74 | | LESS 75 | | LESS_OR_EQUALS 76 | | GREATER 77 | | GREATER_OR_EQUALS 78 | ; 79 | 80 | constDecl 81 | : 'const' ((constSpec eos) | '(' constSpec* ')' eos) 82 | ; 83 | 84 | constSpec 85 | : identList ('=' constants)? 86 | ; 87 | 88 | stringDecl 89 | : IDENT '=' string_ eos 90 | | IDENT '=' compoundString eos 91 | ; 92 | 93 | compoundString 94 | : operandName 95 | | '!' operandName 96 | | '(' compoundString ')' 97 | | compoundString '&&' compoundString 98 | | compoundString '||' compoundString 99 | ; 100 | 101 | identList 102 | : operandName (',' operandName)* 103 | ; 104 | 105 | constants 106 | : numeric 107 | | string_ 108 | | bool_ 109 | | solvable 110 | | nil 111 | ; 112 | 113 | nil 114 | : NIL 115 | ; 116 | 117 | 118 | expressionList 119 | : expression (',' expression)* 120 | ; 121 | 122 | structDecl 123 | : 'def' IDENT '=' structType eos 124 | ; 125 | 126 | structType 127 | : 'flow' '{' (sfProperties ',')* '}' #Flow 128 | | 'stock' '{' (sfProperties ',')* '}' #Stock 129 | ; 130 | 131 | sfProperties 132 | : IDENT ':' functionLit #PropFunc 133 | | structProperties #sfMisc 134 | ; 135 | 136 | comProperties 137 | : IDENT ':' stateLit #StateFunc 138 | | structProperties #compMisc 139 | ; 140 | 141 | structProperties 142 | : IDENT ':' numeric #PropInt 143 | | IDENT ':' string_ #PropString 144 | | IDENT ':' bool_ #PropBool 145 | | IDENT ':' operandName #PropVar 146 | | IDENT ':' prefix #PropVar 147 | | IDENT ':' solvable #PropSolvable 148 | | IDENT #PropSolvable 149 | ; 150 | 151 | initDecl 152 | : 'init' operand eos 153 | ; 154 | 155 | block 156 | : '{' statementList? '}' 157 | ; 158 | 159 | statementList 160 | : statement+ 161 | ; 162 | 163 | statement 164 | : constDecl 165 | | initDecl 166 | | simpleStmt eos 167 | | block 168 | | ifStmt 169 | ; 170 | 171 | simpleStmt 172 | : expression 173 | | incDecStmt 174 | | assignment 175 | | emptyStmt 176 | ; 177 | 178 | incDecStmt 179 | : expression (PLUS_PLUS | MINUS_MINUS) 180 | ; 181 | 182 | stateChange 183 | : 'advance' '(' paramCall ')' #builtins 184 | | 'stay' '(' ')' #builtins 185 | | stateChange '&&' stateChange #builtinInfix 186 | | stateChange '||' stateChange #builtinInfix 187 | ; 188 | 189 | accessHistory 190 | : operandName ('[' expression ']')+ 191 | ; 192 | 193 | assertion 194 | : 'assert' invariant temporal? eos 195 | ; 196 | 197 | assumption 198 | : 'assume' invariant temporal? eos 199 | ; 200 | 201 | temporal 202 | : ('eventually' | 'always' | 'eventually-always' ) 203 | | ('nmt' | 'nft') integer 204 | ; 205 | 206 | invariant 207 | : operand '=' expression # defInvariant 208 | | expression # invar 209 | | 'when' expression 'then' expression # stageInvariant 210 | ; 211 | 212 | assignment 213 | : expressionList ('+' | '-' | '^' | '*' | '/' | '%' | '<<' | '>>' | '&' | '&^')? '=' expressionList #MiscAssign 214 | | expressionList ('->' | '<-') expressionList #FaultAssign 215 | ; 216 | 217 | emptyStmt 218 | : ';' 219 | ; 220 | 221 | ifStmt 222 | : 'if' (simpleStmt ';')? expression block ('else' (ifStmt | block))? 223 | ; 224 | 225 | ifStmtRun 226 | : 'if' (simpleStmt ';')? expression runBlock ('else' (ifStmtRun | runBlock))? 227 | ; 228 | 229 | ifStmtState 230 | : 'if' (simpleStmt ';')? expression stateBlock ('else' (ifStmtState | stateBlock))? 231 | ; 232 | 233 | forStmt 234 | : 'for' rounds ('init' initBlock)? 'run' runBlock eos? 235 | ; 236 | 237 | rounds 238 | : integer 239 | ; 240 | 241 | paramCall 242 | : (IDENT|THIS) '.' IDENT ('.' IDENT)* 243 | ; 244 | 245 | stateBlock 246 | : '{' stateStep* '}' 247 | ; 248 | 249 | stateStep 250 | : paramCall ('|' paramCall)? eos #stateStepExpr 251 | | stateChange eos #stateChain 252 | | ifStmtState #stateExpr 253 | ; 254 | 255 | runBlock 256 | : '{' runStep* '}' 257 | ; 258 | 259 | initBlock 260 | : '{' initStep* '}' 261 | ; 262 | 263 | initStep 264 | : IDENT '=' 'new' (paramCall | IDENT) eos (swap eos)* #runInit 265 | ; 266 | 267 | runStep 268 | : paramCall ('|' paramCall)* eos #runStepExpr 269 | | simpleStmt eos #runExpr 270 | | ifStmtRun #runExpr 271 | ; 272 | 273 | faultType 274 | : TY_STRING 275 | | TY_BOOL 276 | | TY_INT 277 | | TY_FLOAT 278 | | TY_NATURAL 279 | | TY_UNCERTAIN 280 | | TY_UNKNOWN 281 | ; 282 | 283 | solvable 284 | : faultType '(' operand? (',' operand)* ')' 285 | ; 286 | 287 | postfix 288 | : operand 289 | | solvable 290 | ; 291 | 292 | expression 293 | : operand #Expr 294 | | solvable #Typed 295 | | prefix #ExprPrefix 296 | | expression '**' expression #lrExpr 297 | | expression ('*' | '/' | '%' | '<<' | '>>' | '&' | '&^') expression #lrExpr 298 | | expression ('+' | '-' | '^') expression #lrExpr 299 | | expression ('==' | '!=' | '<' | '<=' | '>' | '>=') expression #lrExpr 300 | | expression '&&' expression #lrExpr 301 | | expression '||' expression #lrExpr 302 | ; 303 | 304 | operand 305 | : nil 306 | | numeric 307 | | string_ 308 | | bool_ 309 | | operandName 310 | | accessHistory 311 | | '(' expression ')' 312 | ; 313 | 314 | operandName 315 | : IDENT #OpName 316 | | paramCall #OpParam 317 | | THIS #OpThis 318 | | CLOCK #OpClock 319 | | 'new' IDENT ('.' IDENT)? #OpInstance 320 | ; 321 | 322 | prefix 323 | : 324 | ('+' | '-' | '!' | '^' | '*' | '&' ) postfix 325 | ; 326 | 327 | numeric 328 | : integer 329 | | negative 330 | | float_ 331 | ; 332 | 333 | integer 334 | : DECIMAL_LIT 335 | | OCTAL_LIT 336 | | HEX_LIT 337 | ; 338 | 339 | negative 340 | : '-' integer 341 | | '-' float_ 342 | ; 343 | 344 | float_ 345 | : FLOAT_LIT 346 | ; 347 | 348 | string_ 349 | : RAW_STRING_LIT 350 | | INTERPRETED_STRING_LIT 351 | ; 352 | 353 | bool_ 354 | : TRUE 355 | | FALSE 356 | ; 357 | 358 | functionLit 359 | : 'func' block 360 | ; 361 | 362 | stateLit 363 | : 'func' stateBlock 364 | ; 365 | 366 | eos 367 | : ';' 368 | ; 369 | 370 | -------------------------------------------------------------------------------- /listener/listener_error.go: -------------------------------------------------------------------------------- 1 | package listener 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/antlr4-go/antlr/v4" 9 | ) 10 | 11 | type FaultErrorListener struct { 12 | *antlr.DefaultErrorListener 13 | Filename string 14 | } 15 | 16 | func (f *FaultErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) { 17 | file := strings.Split(f.Filename, string(os.PathSeparator)) 18 | 19 | sym, ok := offendingSymbol.(antlr.Token) 20 | if !ok { 21 | panic(fmt.Sprintf("Invalid spec syntax on line %d col %d in spec %s", line, column, file[len(file)-1])) 22 | } else { 23 | panic(fmt.Sprintf("Invalid spec syntax %s on line %d col %d in spec %s", sym.GetText(), line, column, file[len(file)-1])) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /llvm/alloc.go: -------------------------------------------------------------------------------- 1 | package llvm 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/llir/llvm/ir" 8 | "github.com/llir/llvm/ir/constant" 9 | irtypes "github.com/llir/llvm/ir/types" 10 | "github.com/llir/llvm/ir/value" 11 | ) 12 | 13 | func (c *Compiler) updateVariableStateName(id []string) string { 14 | if len(id) == 2 { // This is a constant, doesn't change 15 | return strings.Join(id, "_") 16 | } 17 | s := c.specs[id[0]] 18 | 19 | incr := s.GetSpecVarState(id[1:]) 20 | return fmt.Sprint(strings.Join(id, "_"), incr+1) 21 | } 22 | 23 | func (c *Compiler) allocVariable(id []string, val value.Value, pos []int) { 24 | name := strings.Join(id, "_") 25 | 26 | var alloc *ir.InstAlloca 27 | var store *ir.InstStore 28 | 29 | switch v := val.(type) { 30 | case *constant.CharArray: 31 | l := uint64(len(v.X)) 32 | alloc = c.contextBlock.NewAlloca(&irtypes.ArrayType{TypeName: "string", Len: l, ElemType: irtypes.I8}) 33 | alloc.SetName(name) 34 | store = c.contextBlock.NewStore(v, alloc) 35 | case *constant.Int: 36 | alloc = c.contextBlock.NewAlloca(irtypes.I1) 37 | alloc.SetName(name) 38 | store = c.contextBlock.NewStore(v, alloc) 39 | case *constant.Float: 40 | alloc = c.contextBlock.NewAlloca(irtypes.Double) 41 | alloc.SetName(name) 42 | store = c.contextBlock.NewStore(v, alloc) 43 | case *constant.Null: 44 | return //Figure out what to do here 45 | case *ir.InstFAdd: 46 | alloc = c.contextBlock.NewAlloca(irtypes.Double) 47 | alloc.SetName(name) 48 | store = c.contextBlock.NewStore(v, alloc) 49 | case *ir.InstFSub: 50 | alloc = c.contextBlock.NewAlloca(irtypes.Double) 51 | alloc.SetName(name) 52 | store = c.contextBlock.NewStore(v, alloc) 53 | case *ir.InstFMul: 54 | alloc = c.contextBlock.NewAlloca(irtypes.Double) 55 | alloc.SetName(name) 56 | store = c.contextBlock.NewStore(v, alloc) 57 | case *ir.InstFDiv: 58 | alloc = c.contextBlock.NewAlloca(irtypes.Double) 59 | alloc.SetName(name) 60 | store = c.contextBlock.NewStore(v, alloc) 61 | case *ir.InstFRem: 62 | alloc = c.contextBlock.NewAlloca(irtypes.Double) 63 | alloc.SetName(name) 64 | store = c.contextBlock.NewStore(v, alloc) 65 | case *ir.InstICmp: //Needed for if true {} constructions 66 | alloc = c.contextBlock.NewAlloca(irtypes.I1) 67 | alloc.SetName(name) 68 | store = c.contextBlock.NewStore(v, alloc) 69 | case *ir.InstFCmp: 70 | alloc = c.contextBlock.NewAlloca(irtypes.I1) 71 | alloc.SetName(name) 72 | store = c.contextBlock.NewStore(v, alloc) 73 | case *ir.InstAnd: 74 | alloc = c.contextBlock.NewAlloca(irtypes.I1) 75 | alloc.SetName(name) 76 | if v.Type() == nil { 77 | v.Typ = irtypes.I1 78 | } 79 | store = c.contextBlock.NewStore(v, alloc) 80 | case *ir.InstOr: 81 | alloc = c.contextBlock.NewAlloca(irtypes.I1) 82 | alloc.SetName(name) 83 | if v.Type() == nil { 84 | v.Typ = irtypes.I1 85 | } 86 | store = c.contextBlock.NewStore(v, alloc) 87 | case *ir.Func: 88 | return 89 | default: 90 | panic(fmt.Sprintf("unknown variable type %T line: %d col: %d", v, pos[0], pos[1])) 91 | } 92 | 93 | //Other metadata 94 | if c.contextMetadata != nil { 95 | store.Metadata = append(store.Metadata, c.contextMetadata) 96 | } 97 | 98 | c.storeAllocation(name, id, alloc) 99 | } 100 | 101 | func (c *Compiler) globalVariable(id []string, val value.Value, pos []int) { 102 | name := c.updateVariableStateName(id) 103 | 104 | switch v := val.(type) { 105 | case *constant.CharArray: 106 | alloc := c.module.NewGlobalDef(name, val.(constant.Constant)) 107 | c.storeGlobal(name, alloc) 108 | case *constant.Int: 109 | alloc := c.module.NewGlobalDef(name, val.(constant.Constant)) 110 | c.storeGlobal(name, alloc) 111 | case *constant.Float: 112 | alloc := c.module.NewGlobalDef(name, val.(constant.Constant)) 113 | c.storeGlobal(name, alloc) 114 | case *constant.Null: 115 | alloc := c.module.NewGlobalDef(name, val.(constant.Constant)) 116 | c.storeGlobal(name, alloc) 117 | case *ir.InstFAdd: 118 | c.allocVariable(id, val, pos) 119 | case *ir.InstFSub: 120 | c.allocVariable(id, val, pos) 121 | case *ir.InstFMul: 122 | c.allocVariable(id, val, pos) 123 | case *ir.InstFDiv: 124 | c.allocVariable(id, val, pos) 125 | case *ir.InstFRem: 126 | c.allocVariable(id, val, pos) 127 | case *ir.InstICmp: 128 | c.allocVariable(id, val, pos) 129 | case *ir.InstFCmp: 130 | c.allocVariable(id, val, pos) 131 | case *ir.Func: 132 | case *ir.InstAnd: 133 | placeholder := constant.NewAnd(v.X.(constant.Expression), v.Y.(constant.Expression)) 134 | alloc := c.module.NewGlobalDef(name, placeholder) 135 | c.storeGlobal(name, alloc) 136 | case *ir.InstOr: 137 | placeholder := constant.NewOr(v.X.(constant.Expression), v.Y.(constant.Expression)) 138 | alloc := c.module.NewGlobalDef(name, placeholder) 139 | c.storeGlobal(name, alloc) 140 | default: 141 | panic(fmt.Sprintf("unknown variable type %T line: %d col: %d", v, pos[0], pos[1])) 142 | } 143 | 144 | } 145 | 146 | func (c *Compiler) storeAllocation(name string, id []string, alloc *ir.InstAlloca) { 147 | s := c.specs[id[0]] 148 | s.vars.IncrState(id) 149 | s.vars.Store(id, name, alloc) 150 | } 151 | 152 | func (c *Compiler) storeGlobal(name string, alloc *ir.Global) { 153 | c.specGlobals[name] = alloc 154 | } 155 | -------------------------------------------------------------------------------- /llvm/llvm_xmisc_test.go: -------------------------------------------------------------------------------- 1 | package llvm 2 | 3 | // "xmisc" is a little hack to keep the order of test files 4 | // from screwing up the block numbering on LLVM IR (and thus 5 | // causing a bunch of tests to fail. 6 | 7 | import ( 8 | "fault/ast" 9 | "testing" 10 | ) 11 | 12 | func TestNegateTemporal(t *testing.T) { 13 | op1, n1 := negateTemporal("nft", 2) 14 | if op1 != "nmt" || n1 != 1 { 15 | t.Fatal("negateTemporal incorrect for nft") 16 | } 17 | op2, n2 := negateTemporal("nmt", 2) 18 | 19 | if op2 != "nft" || n2 != 3 { 20 | t.Fatal("negateTemporal incorrect for nmt") 21 | } 22 | } 23 | 24 | func TestValidOperator(t *testing.T) { 25 | c := NewCompiler() 26 | boolTy := &ast.Type{Type: "BOOL"} 27 | floatTy := &ast.Type{Type: "FLOAT"} 28 | test := &ast.InfixExpression{ 29 | Left: &ast.Identifier{InferredType: boolTy}, 30 | Right: &ast.Identifier{InferredType: boolTy}, 31 | Operator: "&&"} 32 | test1 := &ast.InfixExpression{ 33 | Left: &ast.Identifier{InferredType: floatTy}, 34 | Right: &ast.Identifier{InferredType: floatTy}, 35 | Operator: "&&"} 36 | 37 | if !c.validOperator(test, true) { 38 | t.Fatal("operator is valid but validOperator returned false") 39 | } 40 | 41 | if c.validOperator(test, false) { 42 | t.Fatal("operator is invalid but validOperator returned true") 43 | } 44 | 45 | if !c.validOperator(test1, true) { 46 | t.Fatal("operator is valid but validOperator returned false") 47 | } 48 | 49 | if !c.validOperator(test1, false) { 50 | t.Fatal("operator is valid but validOperator returned false") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /llvm/lr_asserts_test.go: -------------------------------------------------------------------------------- 1 | package llvm 2 | 3 | // Named xasserts to make go test to run these tests AFTER the main 4 | // tests in llvm_test 5 | 6 | import ( 7 | "fault/ast" 8 | "fault/listener" 9 | "fault/preprocess" 10 | "fault/swaps" 11 | "fault/types" 12 | "testing" 13 | ) 14 | 15 | func TestSimpleAssert(t *testing.T) { 16 | test := `spec test1; 17 | const hello = false; 18 | assert hello == true; 19 | ` 20 | 21 | llvm, err := prepAssertTest(test) 22 | 23 | if err != nil { 24 | t.Fatalf("compilation failed on valid spec. got=%s", err) 25 | } 26 | 27 | if len(llvm.Asserts) != 1 { 28 | t.Fatal("asserts failed to compile") 29 | } 30 | 31 | for _, v := range llvm.Asserts { 32 | 33 | c := v.Constraint 34 | av := c.Left.(*ast.AssertVar) 35 | if av.Instances[0] != "test1_hello" { 36 | t.Fatalf("assert assigned to wrong variable. got=%s", av.Instances[0]) 37 | } 38 | 39 | if c.Operator != "!=" { 40 | t.Fatalf("assert has wrong comparison. got=%s", c.Operator) 41 | } 42 | 43 | if _, ok := c.Right.(*ast.Boolean); !ok { 44 | t.Fatalf("assert has wrong operator. got=%s", c.Right) 45 | } 46 | 47 | } 48 | } 49 | 50 | func TestSimpleAssume(t *testing.T) { 51 | test := `spec test1; 52 | const hello = false; 53 | assume hello == true; 54 | ` 55 | 56 | llvm, err := prepAssertTest(test) 57 | 58 | if err != nil { 59 | t.Fatalf("compilation failed on valid spec. got=%s", err) 60 | } 61 | 62 | if len(llvm.Assumes) != 1 { 63 | t.Fatal("asserts failed to compile") 64 | } 65 | 66 | for _, v := range llvm.Asserts { 67 | 68 | c := v.Constraint 69 | av := c.Left.(*ast.AssertVar) 70 | if av.Instances[0] != "test1_hello" { 71 | t.Fatalf("assert assigned to wrong variable. got=%s", av.Instances[0]) 72 | } 73 | 74 | if c.Operator != "==" { 75 | t.Fatalf("assert has wrong comparison. got=%s", c.Operator) 76 | } 77 | 78 | if _, ok := c.Right.(*ast.Boolean); !ok { 79 | t.Fatalf("assert has wrong operator. got=%s", c.Right) 80 | } 81 | 82 | } 83 | } 84 | 85 | func TestAssertWConjunc(t *testing.T) { 86 | test := `spec test1; 87 | const hello = false; 88 | assert hello == true && 5 > 2; 89 | ` 90 | 91 | llvm, err := prepAssertTest(test) 92 | 93 | if err != nil { 94 | t.Fatalf("compilation failed on valid spec. got=%s", err) 95 | } 96 | 97 | for _, v := range llvm.Asserts { 98 | 99 | c := v.Constraint 100 | av := c.Left.(*ast.InfixExpression).Left.(*ast.AssertVar) 101 | if av.Instances[0] != "test1_hello" { 102 | t.Fatalf("assert assigned to wrong variable. got=%s", av.Instances[0]) 103 | } 104 | 105 | if c.Operator != "||" { 106 | t.Fatalf("assert has wrong comparison. got=%s", c.Operator) 107 | } 108 | 109 | if c.Left.(*ast.InfixExpression).Operator != "!=" { 110 | t.Fatalf("assert has wrong operator. got=%s", c.Left.(*ast.InfixExpression).Operator) 111 | } 112 | 113 | right := c.Right.(*ast.InfixExpression) 114 | if right.Operator != "<=" { 115 | t.Fatalf("assert has wrong operator. got=%s", right.Operator) 116 | } 117 | 118 | } 119 | } 120 | 121 | // THIS TEST IS ~AWFUL~ BUT SOME OF THIS SYNTAX HASN'T ACTUALLY BEEN 122 | // IMPLEMENTED. SO WILL INVESTIGATE THE FIX LATER 123 | // func TestAssertState(t *testing.T) { 124 | // test := `spec test1; 125 | // def fl = flow{ 126 | // value: 30, 127 | // scope: 10, 128 | // rate: func{ 129 | // value + 2; 130 | // }, 131 | // }; 132 | 133 | // assert fl.value > fl.scope; 134 | // assert fl.scope > -fl.value; 135 | // assert fl.value[1] > fl.scope; 136 | 137 | // for 2 run{ 138 | // x = new fl; 139 | // x.rate; 140 | // } 141 | // ` 142 | 143 | // llvm, err := prepAssertTest(test) 144 | 145 | // if err != nil { 146 | // t.Fatalf("compilation failed on valid spec. got=%s", err) 147 | // } 148 | 149 | // for i, v := range llvm.Asserts { 150 | // c := v.Constraints 151 | // compareAsserts(t, c.Left, i) 152 | 153 | // if c.Operator != "<=" { 154 | // t.Fatalf("assert %d has wrong comparison. got=%s", i, c.Operator) 155 | // } 156 | // compareAsserts(t, c.Right, i) 157 | // } 158 | // } 159 | 160 | // // HELPER FUNCTION FOR AWFUL TEST 161 | // func compareAsserts(t *testing.T, e ast.Expression, i int) { 162 | // switch ex := e.(type) { 163 | // case *ast.AssertVar: 164 | // var check int 165 | // for _, v := range ex.Instances { 166 | // switch v { 167 | // case "test1_x_scope": 168 | // check++ 169 | // case "test1_x_value": 170 | // check++ 171 | // case "test1_x_rate": 172 | // check++ 173 | // default: 174 | // t.Fatalf("assert %d has wrong expression. got=%s", i, v) 175 | // } 176 | // } 177 | 178 | // if check != 1 { 179 | // t.Fatalf("assert %d has wrong number of instances. got=%d want=3", i, check) 180 | // } 181 | // case *ast.IndexExpression: 182 | // compareAsserts(t, ex.Left, i) 183 | // if ex.Index.(*ast.IntegerLiteral).Value != 1 { 184 | // t.Fatalf("assert %d has wrong expression. got=%d", i, ex.Index.(*ast.IntegerLiteral).Value) 185 | // } 186 | // } 187 | // } 188 | 189 | // // END AWFUL TEST, NOTHING TO SEE HERE... MOVE ALONG ;) 190 | 191 | func prepAssertTest(test string) (*Compiler, error) { 192 | flags := make(map[string]bool) 193 | flags["specType"] = true 194 | flags["testing"] = true 195 | flags["skipRun"] = false 196 | 197 | l := listener.Execute(test, "", flags) 198 | pre := preprocess.Execute(l) 199 | ty := types.Execute(pre.Processed, pre) 200 | sw := swaps.NewPrecompiler(ty) 201 | tree := sw.Swap(ty.Checked) 202 | compiler := NewCompiler() 203 | compiler.LoadMeta(ty.SpecStructs, l.Uncertains, l.Unknowns, sw.Alias, true) 204 | err := compiler.Compile(tree) 205 | if err != nil { 206 | return nil, err 207 | } 208 | 209 | return compiler, err 210 | } 211 | -------------------------------------------------------------------------------- /llvm/name/name.go: -------------------------------------------------------------------------------- 1 | package name 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | ) 7 | 8 | var blockIndex uint64 9 | var parallelIndex uint64 10 | var anonFuncIndex uint64 11 | var assertIndex uint64 12 | 13 | func Block() string { 14 | name := fmt.Sprintf("block-%d", blockIndex) 15 | blockIndex++ 16 | return name 17 | } 18 | 19 | func ParallelGroup(group string) string { 20 | data := []byte(fmt.Sprint(group, parallelIndex)) 21 | parallelIndex++ 22 | return fmt.Sprintf("%x", md5.Sum(data)) 23 | } 24 | 25 | func AnonFunc() string { 26 | name := fmt.Sprintf("fn-%d", anonFuncIndex) 27 | anonFuncIndex++ 28 | return name 29 | } 30 | 31 | func Assert() string { 32 | name := fmt.Sprintf("__assert-%d", assertIndex) 33 | assertIndex++ 34 | return name 35 | } 36 | 37 | func Var(prefix string) string { 38 | name := fmt.Sprintf("%s-%d", prefix, blockIndex) 39 | blockIndex++ 40 | return name 41 | } 42 | -------------------------------------------------------------------------------- /llvm/name/name_test.go: -------------------------------------------------------------------------------- 1 | package name 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func TestBlock(t *testing.T) { 12 | b := Block() 13 | b2 := Block() 14 | 15 | s := strings.Split(b, "-") 16 | s2 := strings.Split(b2, "-") 17 | 18 | if s[0] != "block" || s2[0] != "block" { 19 | t.Fatal("Block function does not return a correct block name") 20 | } 21 | 22 | i, err := strconv.Atoi(s[1]) 23 | if err != nil { 24 | t.Fatalf("Block function does not return a correct block name got=%s want=block-1", b) 25 | } 26 | 27 | i2, err := strconv.Atoi(s2[1]) 28 | if err != nil { 29 | t.Fatalf("Block function does not return a correct block name got=%s want=block-2", b2) 30 | } 31 | 32 | if i2 <= i { 33 | t.Fatal("Block function does iterate block name correctly") 34 | } 35 | } 36 | 37 | func TestAnonFunc(t *testing.T) { 38 | b := AnonFunc() 39 | b2 := AnonFunc() 40 | 41 | s := strings.Split(b, "-") 42 | s2 := strings.Split(b2, "-") 43 | 44 | if s[0] != "fn" || s2[0] != "fn" { 45 | t.Fatal("AnonFunc function does not return a correct name") 46 | } 47 | 48 | i, err := strconv.Atoi(s[1]) 49 | if err != nil { 50 | t.Fatalf("AnonFunc function does not return a correct name got=%s want=fn-1", b) 51 | } 52 | 53 | i2, err := strconv.Atoi(s2[1]) 54 | if err != nil { 55 | t.Fatalf("AnonFunc function does not return a correct name got=%s want=fn-2", b2) 56 | } 57 | 58 | if i2 <= i { 59 | t.Fatal("AnonFunc function does iterate name correctly") 60 | } 61 | } 62 | 63 | func TestAssert(t *testing.T) { 64 | b := Assert() 65 | b2 := Assert() 66 | 67 | s := strings.Split(b, "-") 68 | s2 := strings.Split(b2, "-") 69 | 70 | if s[0] != "__assert" || s2[0] != "__assert" { 71 | t.Fatal("Assert function does not return a correct name") 72 | } 73 | 74 | i, err := strconv.Atoi(s[1]) 75 | if err != nil { 76 | t.Fatalf("Assert function does not return a correct name got=%s want=__assert-1", b) 77 | } 78 | 79 | i2, err := strconv.Atoi(s2[1]) 80 | if err != nil { 81 | t.Fatalf("Assert function does not return a correct name got=%s want=__assert-2", b2) 82 | } 83 | 84 | if i2 <= i { 85 | t.Fatal("Assert function does iterate name correctly") 86 | } 87 | } 88 | func TestVar(t *testing.T) { 89 | b := Var("test") 90 | b2 := Var("test") 91 | 92 | s := strings.Split(b, "-") 93 | s2 := strings.Split(b2, "-") 94 | 95 | if s[0] != "test" || s2[0] != "test" { 96 | t.Fatal("Var function does not return a correct name") 97 | } 98 | 99 | i, err := strconv.Atoi(s[1]) 100 | if err != nil { 101 | t.Fatalf("Var function does not return a correct name got=%s want=test-3", b) 102 | } 103 | 104 | i2, err := strconv.Atoi(s2[1]) 105 | if err != nil { 106 | t.Fatalf("Var function does not return a correct name got=%s want=test-4", b2) 107 | } 108 | 109 | if i2 <= i { 110 | t.Fatal("Var function does iterate name correctly") 111 | } 112 | } 113 | 114 | func TestParallelGroup(t *testing.T) { 115 | data := []byte(fmt.Sprint("test", 0)) 116 | expecting := fmt.Sprintf("%x", md5.Sum(data)) 117 | results := ParallelGroup("test") 118 | 119 | if expecting != results { 120 | t.Fatalf("ParallelGroup does not return correct hash got=%s want=%s", results, expecting) 121 | } 122 | 123 | data2 := []byte(fmt.Sprint("test", 1)) 124 | expecting2 := fmt.Sprintf("%x", md5.Sum(data2)) 125 | results2 := ParallelGroup("test") 126 | 127 | if expecting2 != results2 { 128 | t.Fatal("ParallelGroup does not iterate correctly") 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /llvm/spec.go: -------------------------------------------------------------------------------- 1 | package llvm 2 | 3 | import ( 4 | "fault/llvm/variables" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/llir/llvm/ir" 9 | irtypes "github.com/llir/llvm/ir/types" 10 | "github.com/llir/llvm/ir/value" 11 | ) 12 | 13 | // Representation of a spec 14 | type spec struct { 15 | name string 16 | vars *variables.LookupTable 17 | } 18 | 19 | func NewCompiledSpec(name string) *spec { 20 | return &spec{ 21 | name: name, 22 | vars: variables.NewTable(), 23 | } 24 | } 25 | 26 | func (s *spec) DefineSpecVar(rawid []string, val value.Value) { 27 | if s.GetSpecVar(rawid) != nil { 28 | s.vars.Update(rawid, val) 29 | } else { 30 | s.vars.Add(rawid, val) 31 | } 32 | } 33 | 34 | func (s *spec) GetSpecVar(rawid []string) value.Value { 35 | return s.vars.Get(rawid) 36 | } 37 | 38 | func (s *spec) GetSpecVarState(rawid []string) int16 { 39 | return s.vars.GetState(rawid) 40 | } 41 | 42 | func (s *spec) GetSpecVarPointer(rawid []string) *ir.InstAlloca { 43 | name := strings.Join(rawid, "_") 44 | return s.vars.GetPointer(name) 45 | } 46 | 47 | func (s *spec) GetParams(rawid []string) []value.Value { 48 | return s.vars.GetParams(rawid) 49 | } 50 | 51 | func (s *spec) AddParam(rawid []string, p value.Value) { 52 | s.vars.AddParam(rawid, p) 53 | } 54 | 55 | func (s *spec) AddParams(rawid []string, p []value.Value) { 56 | s.vars.AddParams(rawid, p) 57 | } 58 | 59 | func (s *spec) DefineSpecType(rawid []string, ty irtypes.Type) { 60 | s.vars.Type(rawid, ty) 61 | } 62 | 63 | func (s *spec) GetSpecType(name string) irtypes.Type { 64 | return s.vars.GetType(name) 65 | } 66 | 67 | func (s *spec) GetPointerType(name string) irtypes.Type { 68 | ty := s.vars.GetType(name) 69 | if ty != nil { 70 | switch ty { 71 | case irtypes.Double: 72 | return DoubleP 73 | case irtypes.I1: 74 | return I1P 75 | default: 76 | panic(fmt.Sprintf("invalid pointer type %T for variable %s", ty, name)) 77 | } 78 | } 79 | return DoubleP //Should reconsider this at some point and err here instead 80 | } 81 | 82 | type StateFunc struct { 83 | Id []string 84 | Func *ir.Func 85 | } 86 | -------------------------------------------------------------------------------- /llvm/spec_test.go: -------------------------------------------------------------------------------- 1 | package llvm 2 | 3 | import ( 4 | "fault/llvm/name" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/llir/llvm/ir" 9 | "github.com/llir/llvm/ir/constant" 10 | irtypes "github.com/llir/llvm/ir/types" 11 | ) 12 | 13 | func TestDefineSpecVar(t *testing.T) { 14 | id := []string{"test", "this", "func"} 15 | s := initSpec(id) 16 | 17 | if s.name != "test" { 18 | t.Fatal("spec this not created") 19 | } 20 | v := s.GetSpecVar(id) 21 | if v.(*constant.Int).X.Int64() != int64(0) { 22 | t.Fatal("spec var this.func not added correctly") 23 | } 24 | } 25 | 26 | func TestSpecState(t *testing.T) { 27 | id := []string{"test", "this", "func"} 28 | s := initSpec(id) 29 | 30 | state := s.GetSpecVarState(id) 31 | if state != 0 { 32 | t.Fatalf("spec var this.func has the wrong state label. got=%d want=0", state) 33 | } 34 | 35 | val := constant.NewInt(irtypes.I32, 5) 36 | s.DefineSpecVar(id, val) 37 | 38 | state2 := s.GetSpecVarState(id) 39 | if state2 != 1 { 40 | t.Fatalf("spec var this.func has the wrong state label. got=%d want=1", state2) 41 | } 42 | 43 | v := s.GetSpecVar(id) 44 | if v.(*constant.Int).X.Int64() != int64(5) { 45 | t.Fatalf("spec var this.func was not updated. got=%d want=5", v.(*constant.Int).X.Int64()) 46 | } 47 | } 48 | 49 | func TestSpecPointer(t *testing.T) { 50 | id := []string{"test", "this", "func"} 51 | s := initSpec(id) 52 | fvn := strings.Join(id, "_") 53 | b := ir.NewBlock(name.Block()) 54 | alloc := b.NewAlloca(irtypes.I1) 55 | alloc.SetName(fvn) 56 | s.vars.Store(id, fvn, alloc) 57 | 58 | pointer := s.GetSpecVarPointer(id) 59 | if pointer.LocalName != "test_this_func" { 60 | t.Fatal("spec var this.func is missing a pointer") 61 | } 62 | } 63 | 64 | func TestParams(t *testing.T) { 65 | id := []string{"test", "this", "func"} 66 | s := initSpec(id) 67 | param := constant.NewInt(irtypes.I32, 5) 68 | 69 | s.AddParam(id, param) 70 | p := s.GetParams(id) 71 | 72 | if p[0].(*constant.Int).X.Int64() != int64(5) { 73 | t.Fatal("spec var this.func is missing parameters") 74 | } 75 | 76 | } 77 | 78 | func TestSpecTypes(t *testing.T) { 79 | id := []string{"test", "this", "func"} 80 | s := initSpec(id) 81 | 82 | fvn := strings.Join(id, "_") 83 | s.DefineSpecType(id, irtypes.I32) 84 | 85 | if ty := s.GetSpecType(fvn); ty == nil { 86 | t.Fatal("spec var this.func is missing type") 87 | } 88 | 89 | } 90 | 91 | func TestPointerTypes(t *testing.T) { 92 | id := []string{"test", "this", "func"} 93 | s := initSpec(id) 94 | 95 | fvn := strings.Join(id, "_") 96 | s.DefineSpecType(id, irtypes.I1) 97 | 98 | if ty := s.GetPointerType(fvn); ty.String() != "i1*" { 99 | t.Fatalf("spec var this.func is the wrong type got=%s", ty.String()) 100 | } 101 | 102 | id2 := []string{"test", "this", "too"} 103 | fvn2 := strings.Join(id2, "_") 104 | s.DefineSpecType(id2, irtypes.Double) 105 | 106 | if ty := s.GetPointerType(fvn2); ty.String() != "double*" { 107 | t.Fatalf("spec var this.too is the wrong type got=%s", ty.String()) 108 | } 109 | 110 | } 111 | 112 | func initSpec(id []string) *spec { 113 | s := NewCompiledSpec("test") 114 | val := constant.NewInt(irtypes.I32, 0) 115 | s.DefineSpecVar(id, val) 116 | return s 117 | } 118 | -------------------------------------------------------------------------------- /llvm/variables/lookup.go: -------------------------------------------------------------------------------- 1 | package variables 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/llir/llvm/ir" 8 | irtypes "github.com/llir/llvm/ir/types" 9 | "github.com/llir/llvm/ir/value" 10 | ) 11 | 12 | type LookupTable struct { 13 | state map[string]int16 //ssa position 14 | pointers *Pointers 15 | values map[string][]value.Value 16 | params map[string][]value.Value 17 | types map[string]irtypes.Type 18 | } 19 | 20 | func NewTable() *LookupTable { 21 | l := &LookupTable{ 22 | state: make(map[string]int16), 23 | pointers: NewPointers(), 24 | values: make(map[string][]value.Value), 25 | params: make(map[string][]value.Value), 26 | types: make(map[string]irtypes.Type), 27 | } 28 | return l 29 | } 30 | 31 | func (l *LookupTable) List() []string { 32 | var vals []string 33 | for k := range l.values { 34 | vals = append(vals, k) 35 | } 36 | return vals 37 | } 38 | 39 | func (l *LookupTable) Add(id []string, val value.Value) { 40 | ident := strings.Join(id, "_") 41 | l.values[ident] = append(l.values[ident], val) 42 | l.state[ident] = 0 43 | } 44 | 45 | func (l *LookupTable) AddParam(id []string, p value.Value) { 46 | l.params[id[1]] = append(l.params[id[1]], p) 47 | } 48 | 49 | func (l *LookupTable) AddParams(id []string, p []value.Value) { 50 | l.params[id[1]] = append(l.params[id[1]], p...) 51 | } 52 | 53 | func (l *LookupTable) Store(id []string, name string, point *ir.InstAlloca) { 54 | ident := strings.Join(id, "_") 55 | if l.values[ident] != nil { 56 | l.pointers.store(name, point) 57 | } else { 58 | panic(fmt.Sprintf("variable %s not in the lookup table", strings.Join(id, "_"))) 59 | } 60 | } 61 | 62 | func (l *LookupTable) Get(id []string) value.Value { 63 | ident := strings.Join(id, "_") 64 | i := len(l.values[ident]) - 1 65 | if i == -1 { 66 | return nil 67 | } 68 | return l.values[ident][i] 69 | } 70 | 71 | func (l *LookupTable) GetState(id []string) int16 { 72 | ident := strings.Join(id, "_") 73 | s, ok := l.state[ident] 74 | if !ok { 75 | panic(fmt.Sprintf("no state found for variable %s", ident)) 76 | } 77 | return s 78 | } 79 | 80 | func (l *LookupTable) IncrState(id []string) { 81 | ident := strings.Join(id, "_") 82 | l.state[ident] = l.state[ident] + 1 83 | } 84 | 85 | func (l *LookupTable) ResetState(name string) { 86 | l.state[name] = 0 87 | } 88 | 89 | func (l *LookupTable) GetPointer(name string) *ir.InstAlloca { 90 | return l.pointers.get(name) 91 | } 92 | 93 | func (l *LookupTable) GetParams(id []string) []value.Value { 94 | return l.params[id[1]] 95 | } 96 | 97 | func (l *LookupTable) Update(id []string, val value.Value) { 98 | ident := strings.Join(id, "_") 99 | l.values[ident] = append(l.values[ident], val) 100 | l.state[ident] = l.state[ident] + 1 101 | } 102 | 103 | func (l *LookupTable) Type(id []string, ty irtypes.Type) { 104 | ident := strings.Join(id, "_") 105 | l.types[ident] = ty 106 | } 107 | 108 | func (l *LookupTable) GetType(name string) irtypes.Type { 109 | return l.types[name] 110 | } 111 | -------------------------------------------------------------------------------- /llvm/variables/pointers.go: -------------------------------------------------------------------------------- 1 | package variables 2 | 3 | import "github.com/llir/llvm/ir" 4 | 5 | type Pointers struct { 6 | p map[string]*ir.InstAlloca 7 | } 8 | 9 | func NewPointers() *Pointers { 10 | return &Pointers{ 11 | p: make(map[string]*ir.InstAlloca), 12 | } 13 | } 14 | 15 | func (p *Pointers) get(name string) *ir.InstAlloca { 16 | return p.p[name] 17 | } 18 | 19 | func (p *Pointers) store(name string, point *ir.InstAlloca) { 20 | p.p[name] = point 21 | } 22 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fault/ast" 5 | "fault/execute" 6 | "fault/listener" 7 | "fault/llvm" 8 | "fault/preprocess" 9 | "fault/reachability" 10 | "fault/smt" 11 | resultlog "fault/smt/log" 12 | smtvar "fault/smt/variables" 13 | "fault/swaps" 14 | "fault/types" 15 | "fault/util" 16 | "fault/visualize" 17 | "flag" 18 | "fmt" 19 | "log" 20 | "os" 21 | gopath "path" 22 | "strings" 23 | 24 | _ "github.com/olekukonko/tablewriter" 25 | ) 26 | 27 | func parse(data string, path string, file string, filetype string, reach bool, visu bool) (*ast.Spec, *listener.FaultListener, *types.Checker, string, map[string]string) { 28 | 29 | //Confirm that the filetype and file declaration match 30 | if !validate_filetype(data, filetype) { 31 | log.Fatalf("malformatted file: declaration does not match filetype.") 32 | } 33 | flags := make(map[string]bool) 34 | flags["specType"] = (filetype == "fspec") 35 | flags["testing"] = false 36 | flags["skipRun"] = false 37 | lstnr := listener.Execute(data, path, flags) 38 | 39 | pre := preprocess.Execute(lstnr) 40 | 41 | ty := types.Execute(pre.Processed, pre) 42 | 43 | sw := swaps.NewPrecompiler(ty) 44 | tree := sw.Swap(ty.Checked) 45 | 46 | var visual string 47 | if visu { 48 | vis := visualize.NewVisual(ty.Checked) 49 | vis.Build() 50 | visual = vis.Render() 51 | } 52 | 53 | if reach { 54 | r := reachability.NewTracer() 55 | r.Scan(ty.Checked) 56 | } 57 | return tree, lstnr, ty, visual, sw.Alias 58 | } 59 | 60 | func validate_filetype(data string, filetype string) bool { 61 | if filetype == "fspec" && data[0:4] == "spec" { 62 | return true 63 | } 64 | if filetype == "fsystem" && data[0:6] == "system" { 65 | return true 66 | } 67 | return false 68 | } 69 | 70 | func smt2(ir string, compiler *llvm.Compiler) *smt.Generator { 71 | generator := smt.NewGenerator() 72 | generator.LoadMeta(compiler) 73 | generator.Run(ir) 74 | return generator 75 | } 76 | 77 | func plainSolve(smt string) { 78 | ex := execute.NewModelChecker() 79 | ex.LoadModel(smt, nil, nil, nil, nil) 80 | ok, err := ex.Check() 81 | if err != nil { 82 | log.Fatalf("model checker has failed: %s", err) 83 | } 84 | if !ok { 85 | fmt.Println("Fault could not find a failure case.") 86 | return 87 | } 88 | scenario, err := ex.PlainSolve() 89 | if err != nil { 90 | log.Fatalf("error found fetching solution from solver: %s", err) 91 | } 92 | fmt.Println(scenario) 93 | } 94 | 95 | func probability(smt string, uncertains map[string][]float64, unknowns []string, results map[string][]*smtvar.VarChange, rlog *resultlog.ResultLog) (*execute.ModelChecker, map[string]execute.Scenario) { 96 | ex := execute.NewModelChecker() 97 | ex.LoadModel(smt, uncertains, unknowns, results, rlog) 98 | ok, err := ex.Check() 99 | if err != nil { 100 | log.Fatalf("model checker has failed: %s", err) 101 | } 102 | if !ok { 103 | fmt.Println("Fault could not find a failure case.") 104 | return ex, nil 105 | } 106 | scenario, err := ex.Solve() 107 | if err != nil { 108 | log.Fatalf("error found fetching solution from solver: %s", err) 109 | } 110 | data := ex.Filter(scenario) 111 | return ex, data 112 | } 113 | 114 | func run(filepath string, mode string, input string, output string, reach bool) { 115 | filetype := util.DetectMode(filepath) 116 | if filetype == "" { 117 | log.Fatal("file provided is not a .fspec or .fsystem file") 118 | } 119 | 120 | filepath = util.Filepath(filepath) 121 | uncertains := make(map[string][]float64) 122 | unknowns := []string{} 123 | 124 | data, err := os.ReadFile(filepath) 125 | if err != nil { 126 | log.Fatal(err) 127 | } 128 | d := string(data) 129 | path := gopath.Dir(filepath) 130 | 131 | switch input { 132 | case "fspec": 133 | tree, lstnr, ty, visual, alias := parse(d, path, filepath, filetype, reach, output == "visualize") 134 | if lstnr == nil { 135 | log.Fatal("Fault parser returned nil") 136 | } 137 | 138 | if mode == "ast" { 139 | fmt.Println(lstnr.AST) 140 | return 141 | } 142 | 143 | compiler := llvm.Execute(tree, ty.SpecStructs, lstnr.Uncertains, lstnr.Unknowns, alias, false) 144 | 145 | uncertains = compiler.Uncertains 146 | unknowns = compiler.Unknowns 147 | 148 | if mode == "ir" { 149 | fmt.Println(compiler.GetIR()) 150 | return 151 | } 152 | 153 | if !compiler.IsValid && visual != "" { 154 | fmt.Println(visual) 155 | fmt.Printf("\n\n") 156 | return 157 | } 158 | 159 | if !compiler.IsValid { 160 | fmt.Println("Fault found nothing to run. Missing run block or start block.") 161 | return 162 | } 163 | 164 | generator := smt.Execute(compiler) 165 | if mode == "smt" { 166 | fmt.Println(generator.SMT()) 167 | return 168 | } 169 | 170 | if output == "smt" { 171 | plainSolve(generator.SMT()) 172 | return 173 | } 174 | 175 | mc, data := probability(generator.SMT(), uncertains, unknowns, generator.Results, generator.Log) 176 | if output == "visualize" { 177 | fmt.Println(visual) 178 | fmt.Printf("\n\n") 179 | mc.Mermaid() 180 | return 181 | } 182 | 183 | if data != nil && output == "legacy" { 184 | mc.LoadMeta(generator.Forks) 185 | mc.Format(data) 186 | return 187 | } 188 | 189 | if data != nil && output == "static" { 190 | mc.LoadMeta(generator.Forks) 191 | mc.Static(data) 192 | return 193 | } 194 | 195 | if data != nil { 196 | mc.LoadMeta(generator.Forks) 197 | mc.EventLog(data) 198 | } 199 | case "ll": 200 | compiler := llvm.NewCompiler() 201 | compiler.Uncertains = uncertains 202 | compiler.Unknowns = unknowns 203 | generator := smt2(d, compiler) 204 | if mode == "smt" { 205 | fmt.Println(generator.SMT()) 206 | return 207 | } 208 | 209 | if output == "smt" { 210 | plainSolve(generator.SMT()) 211 | return 212 | } 213 | 214 | mc, data := probability(generator.SMT(), uncertains, unknowns, generator.Results, generator.Log) 215 | if mode == "visualize" { 216 | mc.Mermaid() 217 | return 218 | } 219 | if data != nil && output == "legacy" { 220 | mc.LoadMeta(generator.Forks) 221 | mc.Format(data) 222 | return 223 | } 224 | 225 | if data != nil && output == "static" { 226 | mc.LoadMeta(generator.Forks) 227 | mc.Static(data) 228 | return 229 | } 230 | 231 | if data != nil { 232 | mc.LoadMeta(generator.Forks) 233 | mc.EventLog(data) 234 | } 235 | case "smt2": 236 | if output == "smt" { 237 | plainSolve(d) 238 | return 239 | } 240 | 241 | mc, data := probability(d, uncertains, unknowns, make(map[string][]*smtvar.VarChange), &resultlog.ResultLog{}) 242 | 243 | if mode == "visualize" { 244 | mc.Mermaid() 245 | return 246 | } 247 | if data != nil && output == "legacy" { 248 | mc.Format(data) 249 | return 250 | } 251 | 252 | if data != nil && output == "static" { 253 | mc.Static(data) 254 | return 255 | } 256 | 257 | if data != nil { 258 | fmt.Println("~~~~~~~~~~\n Fault found the following scenario\n~~~~~~~~~~") 259 | mc.EventLog(data) 260 | } 261 | } 262 | } 263 | 264 | func main() { 265 | var mode string 266 | var input string 267 | var output string 268 | var filepath string 269 | var reach bool 270 | modeCommand := flag.String("m", "check", "stop compiler at certain milestones: ast, ir, smt, or check") 271 | inputCommand := flag.String("i", "fspec", "format of the input file (default: fspec)") 272 | fpCommand := flag.String("f", "", "path to file to compile") 273 | reachCommand := flag.Bool("complete", false, "make sure the transitions to all defined states are specified in the model") 274 | outputCommand := flag.String("format", "log", "format of the output: log, static, smt, legacy, or visualize") 275 | 276 | flag.Parse() 277 | 278 | if *fpCommand == "" { 279 | flag.PrintDefaults() 280 | fmt.Println("must provide path of file to compile") 281 | os.Exit(1) 282 | } 283 | filepath = *fpCommand 284 | 285 | if *modeCommand == "" { 286 | mode = "check" 287 | } else { 288 | mode = strings.ToLower(*modeCommand) 289 | switch mode { 290 | case "ast": 291 | case "ir": 292 | case "smt": 293 | case "check": 294 | default: 295 | fmt.Printf("%s is not a valid mode\n", mode) 296 | os.Exit(1) 297 | } 298 | } 299 | 300 | if *outputCommand == "" { 301 | output = "log" 302 | } else { 303 | output = strings.ToLower(*outputCommand) 304 | switch output { 305 | case "static": 306 | case "log": 307 | case "legacy": 308 | case "visualize": 309 | case "smt": 310 | default: 311 | fmt.Printf("%s is not a valid mode\n", output) 312 | os.Exit(1) 313 | } 314 | } 315 | 316 | //Check if solver is set 317 | if mode == "check" && 318 | os.Getenv("SOLVERCMD") == "" || os.Getenv("SOLVERARG") == "" { 319 | fmt.Printf("\n no solver configured, defaulting to SMT output without model checking. Please set SOLVERCMD and SOLVERARG variables.\n\n") 320 | mode = "smt" 321 | } 322 | 323 | if *inputCommand == "" { 324 | input = "fspec" 325 | } else { 326 | input = strings.ToLower(*inputCommand) 327 | switch input { 328 | case "fspec": 329 | case "ll": 330 | case "smt2": 331 | default: 332 | fmt.Printf("%s is not a valid input format\n", input) 333 | os.Exit(1) 334 | } 335 | } 336 | 337 | if *reachCommand { 338 | reach = true 339 | } 340 | 341 | run(filepath, mode, input, output, reach) 342 | } 343 | -------------------------------------------------------------------------------- /preprocess/preprocess_errors_test.go: -------------------------------------------------------------------------------- 1 | package preprocess 2 | 3 | import ( 4 | "fault/ast" 5 | "testing" 6 | ) 7 | 8 | func TestConstantsErr(t *testing.T) { 9 | p := NewProcesser() 10 | p.trail = p.trail.PushSpec("test") 11 | p.Specs["test"] = NewSpecRecord() 12 | p.Specs["test"].SpecName = "test" 13 | p.Specs["test"].AddConstant("foo", &ast.IntegerLiteral{Value: 2}) 14 | 15 | test := &ast.Spec{Statements: []ast.Statement{&ast.ConstantStatement{Name: &ast.Identifier{Spec: "test", Value: "foo"}, 16 | Value: &ast.IntegerLiteral{Value: 2}, 17 | }}} 18 | 19 | _, err := p.walk(test) 20 | if err == nil { 21 | t.Fatal("failed to error on constant redeclare") 22 | } 23 | 24 | if err.Error() != "variable foo is a constant and cannot be modified" { 25 | t.Fatalf("error message on constant redeclare incorrect got=%s", err.Error()) 26 | } 27 | } 28 | 29 | func TestInstanceErr(t *testing.T) { 30 | p := NewProcesser() 31 | p.trail = p.trail.PushSpec("test") 32 | p.Specs["test"] = NewSpecRecord() 33 | p.Specs["test"].SpecName = "test" 34 | p.initialPass = false 35 | 36 | test := &ast.Instance{Value: &ast.Identifier{Spec: "test", Value: "bash"}, Name: "foo"} 37 | 38 | _, err := p.walk(test) 39 | if err == nil { 40 | t.Fatal("failed to error on unknown instance") 41 | } 42 | 43 | if err.Error() != "can't find an instance named bash" { 44 | t.Fatalf("error message on unknown instance incorrect got=%s", err.Error()) 45 | } 46 | } 47 | 48 | func TestStructInstanceErr(t *testing.T) { 49 | p := NewProcesser() 50 | p.trail = p.trail.PushSpec("test") 51 | p.Specs["test"] = NewSpecRecord() 52 | p.Specs["test"].SpecName = "test" 53 | 54 | stockdata := make(map[*ast.Identifier]ast.Expression) 55 | stockdata[&ast.Identifier{Spec: "test", Value: "foo"}] = &ast.StructInstance{Spec: "test", Name: "foo", Parent: []string{"test", "bar"}} 56 | test := &ast.DefStatement{Name: &ast.Identifier{Spec: "test", Value: "zoo"}, Value: &ast.StockLiteral{Pairs: stockdata, Order: []string{"foo"}}} 57 | 58 | p.walk(test) 59 | 60 | _, err := p.walk(test) 61 | 62 | if err == nil { 63 | t.Fatal("failed to error on unknown instance") 64 | } 65 | 66 | if err.Error() != "can't find a struct instance named [test bar]" { 67 | t.Fatalf("error message on unknown instance incorrect got=%s", err.Error()) 68 | } 69 | } 70 | 71 | func TestParamCallErr(t *testing.T) { 72 | p := NewProcesser() 73 | p.trail = p.trail.PushSpec("test") 74 | p.Specs["test"] = NewSpecRecord() 75 | p.Specs["test"].SpecName = "test" 76 | p.initialPass = false 77 | 78 | test := &ast.ParameterCall{Spec: "test", Value: []string{"foo", "bar"}} 79 | 80 | _, err := p.walk(test) 81 | if err == nil { 82 | t.Fatal("failed to error on unknown instance") 83 | } 84 | 85 | if err.Error() != "cannot fetch a variable [test foo bar] of type NIL" { 86 | t.Fatalf("error message on unknown instance incorrect got=%s", err.Error()) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /preprocess/record_errors_test.go: -------------------------------------------------------------------------------- 1 | package preprocess 2 | 3 | import ( 4 | "fault/ast" 5 | "testing" 6 | ) 7 | 8 | func TestFetchVarError(t *testing.T) { 9 | sr := NewSpecRecord() 10 | sr.SpecName = "test" 11 | test := []string{"test", "should", "fail"} 12 | _, err := sr.FetchVar(test, "STOCK") 13 | if err == nil { 14 | t.Fatal("test failed to produce an error") 15 | } 16 | 17 | if err.Error() != "no stock found with name should in spec test" { 18 | t.Fatalf("error message did not match got=%s", err.Error()) 19 | } 20 | 21 | _, err = sr.FetchVar(test, "FLOW") 22 | if err == nil { 23 | t.Fatal("test failed to produce an error") 24 | } 25 | 26 | if err.Error() != "no flow found with name should in spec test" { 27 | t.Fatalf("error message did not match got=%s", err.Error()) 28 | } 29 | 30 | _, err = sr.FetchVar(test, "COMPONENT") 31 | if err == nil { 32 | t.Fatal("test failed to produce an error") 33 | } 34 | 35 | if err.Error() != "no component found with name should in spec test" { 36 | t.Fatalf("error message did not match got=%s", err.Error()) 37 | } 38 | 39 | _, err = sr.FetchVar(test, "CONSTANT") 40 | if err == nil { 41 | t.Fatal("test failed to produce an error") 42 | } 43 | 44 | if err.Error() != "no constant found with name should in spec test" { 45 | t.Fatalf("error message did not match got=%s", err.Error()) 46 | } 47 | 48 | _, err = sr.FetchVar(test, "INVALID") 49 | if err == nil { 50 | t.Fatal("test failed to produce an error") 51 | } 52 | 53 | if err.Error() != "cannot fetch a variable [test should fail] of type INVALID" { 54 | t.Fatalf("error message did not match got=%s", err.Error()) 55 | } 56 | 57 | sr.AddStock("should", make(map[string]ast.Node)) 58 | _, err = sr.FetchVar(test, "STOCK") 59 | if err == nil { 60 | t.Fatal("test failed to produce an error") 61 | } 62 | 63 | if err.Error() != "no property named fail in stock should" { 64 | t.Fatalf("error message did not match got=%s", err.Error()) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /preprocess/record_test.go: -------------------------------------------------------------------------------- 1 | package preprocess 2 | 3 | import ( 4 | "fault/ast" 5 | "testing" 6 | ) 7 | 8 | func TestSpecRecord(t *testing.T) { 9 | sr := NewSpecRecord() 10 | 11 | testNodes := map[string]ast.Node{ 12 | "bar": &ast.IntegerLiteral{Value: 1}} 13 | 14 | sr.AddStock("test", testNodes) 15 | t1, _ := sr.FetchStock("test") 16 | if t1 == nil { 17 | t.Fatal("stock not found in spec record") 18 | } 19 | 20 | i, ok := t1["bar"].(*ast.IntegerLiteral) 21 | if !ok { 22 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t1["bar"]) 23 | } 24 | 25 | if i.Value != 1 { 26 | t.Fatalf("property bar has incorrect value got=%d want=1", i.Value) 27 | } 28 | 29 | sr.AddFlow("test", testNodes) 30 | t2, _ := sr.FetchFlow("test") 31 | if t2 == nil { 32 | t.Fatal("stock not found in spec record") 33 | } 34 | 35 | i2, ok := t2["bar"].(*ast.IntegerLiteral) 36 | if !ok { 37 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t2["bar"]) 38 | } 39 | 40 | if i2.Value != 1 { 41 | t.Fatalf("property bar has incorrect value got=%d want=1", i2.Value) 42 | } 43 | 44 | sr.AddComponent("test", testNodes) 45 | t3, _ := sr.FetchComponent("test") 46 | if t3 == nil { 47 | t.Fatal("stock not found in spec record") 48 | } 49 | 50 | i3, ok := t3["bar"].(*ast.IntegerLiteral) 51 | if !ok { 52 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t3["bar"]) 53 | } 54 | 55 | if i3.Value != 1 { 56 | t.Fatalf("property bar has incorrect value got=%d want=1", i3.Value) 57 | } 58 | 59 | sr.AddConstant("test", &ast.IntegerLiteral{Value: 1}) 60 | t4, _ := sr.FetchConstant("test") 61 | if t4 == nil { 62 | t.Fatal("constant not found in spec record") 63 | } 64 | 65 | i4, ok := t4.(*ast.IntegerLiteral) 66 | if !ok { 67 | t.Fatalf("constant is incorrect type got=%T want=IntegerLiteral", t4) 68 | } 69 | 70 | if i4.Value != 1 { 71 | t.Fatalf("constant has incorrect value got=%d want=1", i4.Value) 72 | } 73 | } 74 | 75 | func TestSpecRecordUpdate(t *testing.T) { 76 | sr := NewSpecRecord() 77 | 78 | testNodes := map[string]ast.Node{ 79 | "bar": &ast.IntegerLiteral{Value: 1}} 80 | 81 | testNodes2 := map[string]ast.Node{ 82 | "foo": &ast.FloatLiteral{Value: .01}, 83 | "bar": &ast.IntegerLiteral{Value: 2}} 84 | 85 | sr.AddStock("test1", testNodes) 86 | t1, _ := sr.FetchStock("test1") 87 | sr.UpdateStock("test1", testNodes2) 88 | t1a, _ := sr.FetchStock("test1") 89 | if len(t1) == len(t1a) { 90 | t.Fatal("stock not updated correctly spec record") 91 | } 92 | 93 | i, ok := t1a["bar"].(*ast.IntegerLiteral) 94 | if !ok { 95 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t1a["bar"]) 96 | } 97 | 98 | if i.Value != 2 { 99 | t.Fatalf("property bar has incorrect value got=%d want=2", i.Value) 100 | } 101 | 102 | sr.AddFlow("test2", testNodes) 103 | t2, _ := sr.FetchFlow("test2") 104 | sr.UpdateFlow("test2", testNodes2) 105 | t2a, _ := sr.FetchFlow("test2") 106 | if len(t2) == len(t2a) { 107 | t.Fatal("flow not updated correctly") 108 | } 109 | 110 | i2, ok := t2a["bar"].(*ast.IntegerLiteral) 111 | if !ok { 112 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t2a["bar"]) 113 | } 114 | 115 | if i2.Value != 2 { 116 | t.Fatalf("property bar has incorrect value got=%d want=2", i2.Value) 117 | } 118 | 119 | sr.AddComponent("test3", testNodes) 120 | t3, _ := sr.FetchComponent("test3") 121 | sr.UpdateComponent("test3", testNodes2) 122 | t3a, _ := sr.FetchComponent("test3") 123 | if len(t3) == len(t3a) { 124 | t.Fatal("component not found in spec record") 125 | } 126 | 127 | i3, ok := t3a["bar"].(*ast.IntegerLiteral) 128 | if !ok { 129 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t3a["bar"]) 130 | } 131 | 132 | if i3.Value != 2 { 133 | t.Fatalf("property bar has incorrect value got=%d want=2", i3.Value) 134 | } 135 | } 136 | 137 | func TestUpdateVar(t *testing.T) { 138 | sr := NewSpecRecord() 139 | 140 | testNodes := map[string]ast.Node{ 141 | "bar": &ast.IntegerLiteral{Value: 1}} 142 | 143 | testNodes3 := map[string]ast.Node{ 144 | "foo": &ast.FloatLiteral{Value: .01}, 145 | "bar": &ast.IntegerLiteral{Value: 3}} 146 | 147 | sr.AddStock("test1", testNodes) 148 | sr.UpdateVar([]string{"foo", "test1", "bar"}, "STOCK", testNodes3["bar"]) 149 | t1b, _ := sr.FetchStock("test1") 150 | 151 | ib, ok := t1b["bar"].(*ast.IntegerLiteral) 152 | if !ok { 153 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t1b["bar"]) 154 | } 155 | 156 | if ib.Value != 3 { 157 | t.Fatalf("property bar has incorrect value got=%d want=3", ib.Value) 158 | } 159 | 160 | sr.AddFlow("test2", testNodes) 161 | sr.UpdateVar([]string{"foo", "test2", "bar"}, "FLOW", testNodes3["bar"]) 162 | t2b, _ := sr.FetchFlow("test2") 163 | 164 | i2b, ok := t2b["bar"].(*ast.IntegerLiteral) 165 | if !ok { 166 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t2b["bar"]) 167 | } 168 | 169 | if i2b.Value != 3 { 170 | t.Fatalf("property bar has incorrect value got=%d want=3", i2b.Value) 171 | } 172 | 173 | sr.AddComponent("test3", testNodes) 174 | sr.UpdateVar([]string{"foo", "test3", "bar"}, "COMPONENT", testNodes3["bar"]) 175 | t3b, _ := sr.FetchComponent("test3") 176 | 177 | i3b, ok := t3b["bar"].(*ast.IntegerLiteral) 178 | if !ok { 179 | t.Fatalf("property bar is incorrect type got=%T want=IntegerLiteral", t3b["bar"]) 180 | } 181 | 182 | if i3b.Value != 3 { 183 | t.Fatalf("property bar has incorrect value got=%d want=3", i3b.Value) 184 | } 185 | 186 | sr.AddGlobal("test4", &ast.IntegerLiteral{Value: 1}) 187 | sr.UpdateVar([]string{"foo", "test4"}, "GLOBAL", &ast.IntegerLiteral{Value: 3}) 188 | t4b, _ := sr.FetchGlobal("test4") 189 | 190 | i4b, ok := t4b.(*ast.IntegerLiteral) 191 | if !ok { 192 | t.Fatalf("constant is incorrect type got=%T want=IntegerLiteral", t4b) 193 | } 194 | 195 | if i4b.Value != 3 { 196 | t.Fatalf("constant has incorrect value got=%d want=3", i4b.Value) 197 | } 198 | } 199 | 200 | func TestSpecRecordInstance(t *testing.T) { 201 | sr := NewSpecRecord() 202 | 203 | testNodes := map[string]ast.Node{ 204 | "bar": &ast.IntegerLiteral{Value: 1}} 205 | 206 | sr.AddStock("test", testNodes) 207 | t1, _ := sr.FetchStock("test") 208 | sr.AddInstance("test2", t1, "STOCK") 209 | t2, _ := sr.FetchStock("test2") 210 | t2["bar"] = &ast.IntegerLiteral{Value: 0} 211 | if t1["bar"] == t2["bar"] { 212 | t.Fatal("instance not added correctly") 213 | } 214 | } 215 | 216 | func TestSpecRecordFetching(t *testing.T) { 217 | sr := NewSpecRecord() 218 | 219 | testNodes := map[string]ast.Node{ 220 | "bar": &ast.IntegerLiteral{Value: 1}} 221 | testNodes1 := map[string]ast.Node{ 222 | "bar1": &ast.IntegerLiteral{Value: 1}} 223 | testNodes2 := map[string]ast.Node{ 224 | "bar2": &ast.IntegerLiteral{Value: 1}} 225 | testNodes3 := map[string]ast.Node{ 226 | "bar3": &ast.IntegerLiteral{Value: 1}} 227 | 228 | testNodes4 := map[string]ast.Node{ 229 | "bar4": &ast.IntegerLiteral{Value: 1}} 230 | 231 | sr.AddStock("test", testNodes) 232 | s2, _ := sr.FetchVar([]string{"here", "test", "bar"}, "STOCK") 233 | 234 | if _, ok := s2.(*ast.IntegerLiteral); !ok { 235 | t.Fatalf("var not an IntegerLiteral got=%T", s2) 236 | } 237 | 238 | sr.AddFlow("test1", testNodes1) 239 | sr.AddStock("test2", testNodes2) 240 | sr.AddStock("test3", testNodes3) 241 | sr.AddComponent("test4", testNodes4) 242 | 243 | sr.AddConstant("foo", &ast.IntegerLiteral{Value: 1}) 244 | sr.AddConstant("foo2", &ast.IntegerLiteral{Value: 1}) 245 | 246 | all := sr.FetchAll() 247 | i := 0 248 | for k := range all { 249 | if k == "bar" || k == "bar1" || k == "bar2" || k == "bar3" || k == "bar4" || k == "foo" || k == "foo1" { 250 | i = i + 1 251 | } 252 | } 253 | if i != 6 { 254 | t.Fatalf("fetch all returned wrong number of vars got=%d", i) 255 | } 256 | } 257 | 258 | func TestSpecRecordTypes(t *testing.T) { 259 | sr := NewSpecRecord() 260 | 261 | ty0, _ := sr.GetStructType([]string{"this", "does", "not", "exist"}) 262 | if ty0 != "NIL" { 263 | t.Fatalf("spec record did not return the correct type for a nil variable got=%s", ty0) 264 | } 265 | 266 | testNodes := map[string]ast.Node{ 267 | "bar": &ast.IntegerLiteral{Value: 1}} 268 | 269 | sr.AddStock("test", testNodes) 270 | sr.Index("STOCK", "test") 271 | ty1, _ := sr.GetStructType([]string{"this", "test"}) 272 | if ty1 != "STOCK" { 273 | t.Fatalf("spec record did not return the correct type for stock got=%s", ty1) 274 | } 275 | 276 | sr.AddFlow("test1", testNodes) 277 | sr.Index("FLOW", "test1") 278 | ty2, _ := sr.GetStructType([]string{"this", "test1"}) 279 | if ty2 != "FLOW" { 280 | t.Fatalf("spec record did not return the correct type for flow got=%s", ty2) 281 | } 282 | 283 | sr.AddComponent("test2", testNodes) 284 | sr.Index("COMPONENT", "test2") 285 | ty3, _ := sr.GetStructType([]string{"this", "test2"}) 286 | if ty3 != "COMPONENT" { 287 | t.Fatalf("spec record did not return the correct type for component got=%s", ty3) 288 | } 289 | 290 | sr.AddConstant("test3", testNodes["bar"]) 291 | ty4, _ := sr.GetStructType([]string{"this", "test3"}) 292 | if ty4 != "CONSTANT" { 293 | t.Fatalf("spec record did not return the correct type for constant got=%s", ty4) 294 | } 295 | 296 | } 297 | 298 | func TestSpecRecordIndex(t *testing.T) { 299 | sr := NewSpecRecord() 300 | 301 | testNodes := map[string]ast.Node{ 302 | "bar": &ast.IntegerLiteral{Value: 1}} 303 | 304 | sr.AddStock("test", testNodes) 305 | sr.Index("STOCK", "test") 306 | 307 | if len(sr.Order) != 1 { 308 | t.Fatalf("SpecRecord has an incorrect index length got=%s", sr.Order) 309 | } 310 | 311 | if sr.Order[0][0] != "STOCK" || sr.Order[0][1] != "test" { 312 | t.Fatalf("SpecRecord has an incorrect index got=%s", sr.Order[0]) 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /reachability/reachability.go: -------------------------------------------------------------------------------- 1 | package reachability 2 | 3 | import ( 4 | "fault/ast" 5 | "fmt" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | type Tracer struct { 11 | graph map[string]bool 12 | undefined []string 13 | last string 14 | } 15 | 16 | func NewTracer() *Tracer { 17 | return &Tracer{graph: make(map[string]bool)} 18 | } 19 | 20 | func (t *Tracer) Scan(spec *ast.Spec) { 21 | t.walk(spec) 22 | ch, missing := t.check() 23 | if !ch { 24 | fmt.Fprintf(os.Stderr, "error: system under specified, states %s are unreachable\n", missing) 25 | os.Exit(1) 26 | } 27 | } 28 | 29 | func (t *Tracer) walk(n ast.Node) { 30 | switch node := n.(type) { 31 | case *ast.Spec: 32 | for _, v := range node.Statements { 33 | t.walk(v) 34 | } 35 | case *ast.DefStatement: 36 | t.walk(node.Value) 37 | case *ast.ComponentLiteral: 38 | nid := node.Id() 39 | for k, v := range node.Pairs { 40 | if f, ok := v.(*ast.FunctionLiteral); ok { 41 | pid := k.Id() 42 | id := fmt.Sprintf("%s_%s", nid[1], pid[1]) 43 | if _, ok := t.graph[id]; !ok { 44 | t.graph[id] = false 45 | } 46 | 47 | if t.seenBefore(id) { 48 | t.graph[id] = true 49 | t.removeUndefined(id) 50 | } 51 | 52 | t.walk(f) 53 | } 54 | } 55 | case *ast.StartStatement: 56 | for _, v := range node.Pairs { 57 | id := strings.Join(v, "_") 58 | if _, ok := t.graph[id]; !ok { 59 | t.undefined = append(t.undefined, id) 60 | } else { 61 | t.graph[id] = true 62 | t.removeUndefined(id) 63 | } 64 | } 65 | case *ast.FunctionLiteral: 66 | t.walk(node.Body) 67 | case *ast.BlockStatement: 68 | for _, v := range node.Statements { 69 | t.walk(v) 70 | } 71 | case *ast.ExpressionStatement: 72 | t.walk(node.Expression) 73 | case *ast.IfExpression: 74 | t.walk(node.Consequence) 75 | 76 | if node.Elif != nil { 77 | t.walk(node.Elif) 78 | } 79 | if node.Alternative != nil { 80 | t.walk(node.Alternative) 81 | } 82 | case *ast.BuiltIn: 83 | for _, v := range node.Parameters { 84 | id := v.(ast.Nameable).Id() 85 | if _, ok := t.graph[id[1]]; !ok { 86 | t.undefined = append(t.undefined, id[1]) 87 | } else { 88 | t.graph[id[1]] = true 89 | t.removeUndefined(id[1]) 90 | } 91 | } 92 | case *ast.InfixExpression: 93 | t.walk(node.Left) 94 | t.walk(node.Right) 95 | case *ast.PrefixExpression: 96 | t.walk(node.Right) 97 | } 98 | } 99 | 100 | func (t *Tracer) seenBefore(id string) bool { 101 | for _, v := range t.undefined { 102 | if v == id { 103 | return true 104 | } 105 | } 106 | return false 107 | } 108 | 109 | func (t *Tracer) removeUndefined(id string) { 110 | var new []string 111 | for _, v := range t.undefined { 112 | if v != id { 113 | new = append(new, v) 114 | } 115 | } 116 | t.undefined = new 117 | } 118 | 119 | func (t *Tracer) check() (bool, []string) { 120 | for k, v := range t.graph { 121 | if !v { 122 | t.undefined = append(t.undefined, k) 123 | } 124 | } 125 | return len(t.undefined) == 0, t.undefined 126 | } 127 | -------------------------------------------------------------------------------- /reachability/reachability_test.go: -------------------------------------------------------------------------------- 1 | package reachability 2 | 3 | import ( 4 | "fault/listener" 5 | "fault/preprocess" 6 | "fault/types" 7 | "testing" 8 | ) 9 | 10 | func TestSeenBefore(t *testing.T) { 11 | tracer := NewTracer() 12 | tracer.undefined = []string{"test"} 13 | 14 | if !tracer.seenBefore("test") { 15 | t.Fatal("seenBefore function not working") 16 | } 17 | } 18 | 19 | func TestRemoveUndefined(t *testing.T) { 20 | tracer := NewTracer() 21 | tracer.undefined = []string{"test", "test2", "test3"} 22 | 23 | tracer.removeUndefined("test2") 24 | 25 | if len(tracer.undefined) != 2 || tracer.undefined[0] != "test" || tracer.undefined[1] != "test3" { 26 | t.Fatalf("removeUndefined function not working got=%s", tracer.undefined) 27 | } 28 | } 29 | 30 | func TestCorrect(t *testing.T) { 31 | test := ` 32 | system test; 33 | 34 | component foo = states{ 35 | x: 8, 36 | initial: func{ 37 | if this.x > 10{ 38 | stay(); 39 | }else{ 40 | advance(this.alarm); 41 | } 42 | }, 43 | alarm: func{ 44 | advance(this.close); 45 | }, 46 | close: func{ 47 | stay(); 48 | }, 49 | }; 50 | 51 | start { 52 | foo: initial, 53 | }; 54 | ` 55 | check, missing := prepTestSys(test) 56 | 57 | if !check || len(missing) > 0 { 58 | t.Fatalf("reachability check failed on valid spec got=%s", missing) 59 | } 60 | } 61 | 62 | func TestIncorrect(t *testing.T) { 63 | test := ` 64 | system test; 65 | 66 | component foo = states{ 67 | initial: func{ 68 | advance(this.alarm); 69 | }, 70 | alarm: func{ 71 | advance(this.close); 72 | }, 73 | close: func{ 74 | stay(); 75 | }, 76 | error: func{ 77 | stay(); 78 | }, 79 | }; 80 | 81 | start { 82 | foo: initial, 83 | }; 84 | ` 85 | check, missing := prepTestSys(test) 86 | 87 | if check { 88 | t.Fatal("reachability check failed to catch missing state error") 89 | } 90 | 91 | if len(missing) == 0 || missing[0] != "foo_error" { 92 | t.Fatalf("reachability check failed to catch missing state got=%s", missing) 93 | } 94 | } 95 | 96 | func TestMultiIncorrect(t *testing.T) { 97 | test := ` 98 | system test; 99 | 100 | component foo = states{ 101 | initial: func{ 102 | advance(this.alarm); 103 | }, 104 | alarm: func{ 105 | advance(bar.error); 106 | }, 107 | }; 108 | 109 | component bar = states{ 110 | error: func{ 111 | advance(this.resolve); 112 | }, 113 | resolve: func { 114 | advance(foo.initial); 115 | }, 116 | }; 117 | 118 | component fizz = states{ 119 | empty: func{ 120 | advance(bar.error); 121 | }, 122 | active: func{ 123 | advance(this.empty); 124 | }, 125 | }; 126 | 127 | start { 128 | foo: initial, 129 | }; 130 | ` 131 | check, missing := prepTestSys(test) 132 | 133 | if check { 134 | t.Fatal("reachability check failed to catch missing state error") 135 | } 136 | 137 | if len(missing) == 0 || missing[0] != "fizz_active" { 138 | t.Fatalf("reachability check failed to catch missing state got=%s", missing) 139 | } 140 | } 141 | 142 | func TestMultiCorrect(t *testing.T) { 143 | test := ` 144 | system test; 145 | 146 | component foo = states{ 147 | initial: func{ 148 | advance(this.alarm); 149 | }, 150 | alarm: func{ 151 | advance(bar.error); 152 | }, 153 | }; 154 | 155 | component bar = states{ 156 | error: func{ 157 | advance(this.resolve); 158 | }, 159 | resolve: func { 160 | advance(foo.initial); 161 | }, 162 | }; 163 | 164 | component fizz = states{ 165 | empty: func{ 166 | advance(bar.error); 167 | }, 168 | active: func{ 169 | advance(this.empty); 170 | }, 171 | }; 172 | 173 | start { 174 | foo: initial, 175 | fizz: active, 176 | }; 177 | ` 178 | check, missing := prepTestSys(test) 179 | 180 | if !check || len(missing) > 0 { 181 | t.Fatalf("reachability check failed on valid spec got=%s", missing) 182 | } 183 | } 184 | 185 | func TestMultiPath(t *testing.T) { 186 | test := ` 187 | system test; 188 | 189 | component foo = states{ 190 | initial: func{ 191 | advance(bar.alarm); 192 | }, 193 | }; 194 | 195 | component bar = states{ 196 | initial: func{ 197 | advance(this.alarm); 198 | }, 199 | alarm: func{ 200 | advance(this.close); 201 | }, 202 | close: func{ 203 | stay(); 204 | }, 205 | }; 206 | 207 | start { 208 | foo: initial, 209 | bar: initial, 210 | }; 211 | ` 212 | check, missing := prepTestSys(test) 213 | 214 | if !check || len(missing) > 0 { 215 | t.Fatalf("reachability check failed on valid spec got=%s", missing) 216 | } 217 | } 218 | 219 | func prepTestSys(test string) (bool, []string) { 220 | flags := make(map[string]bool) 221 | flags["specType"] = false 222 | flags["testing"] = true 223 | flags["skipRun"] = false 224 | 225 | var path string 226 | 227 | l := listener.Execute(test, path, flags) 228 | pre := preprocess.Execute(l) 229 | ty := types.Execute(pre.Processed, pre) 230 | tracer := NewTracer() 231 | tracer.walk(ty.Checked) 232 | return tracer.check() 233 | } 234 | -------------------------------------------------------------------------------- /smt/forks/forks.go: -------------------------------------------------------------------------------- 1 | package forks 2 | 3 | import ( 4 | "fault/smt/rules" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // Key is the base variable name 10 | //type Fork map[string][]*Choice 11 | // map[base_var]map[phi_SSA]map[branch_id] 12 | // base_var 13 | // -> Whats the Phi value 14 | // -> Whats the last defined state in the chain? 15 | // -> What's the full chain? 16 | // -> What's the unique branch id? 17 | // -> What's the choice id? 18 | // -> have we picked a winning branch for this choice? 19 | 20 | // Choice[id] = {branch_ids, winning_branch} 21 | // branch[id] = []full_var_names 22 | // Var[base_var][branch_id] = {[]full_var_names, phi} 23 | 24 | type Fork struct { 25 | Choices map[string][]string //slice of Branchid 26 | Branches map[string][]string //slice of variables in the branch 27 | Vars map[string]*Var 28 | Bases map[string]map[string]bool // Is there an instance of this variable in the branch? 29 | ToKill map[string]bool //Quick lookup around which variables are listed as to be killed 30 | } 31 | 32 | type Var struct { 33 | Base string 34 | Last map[string]bool 35 | SSA string 36 | Previous map[string]string // map[branchId]string If this is a Phi value, keep track of the last value in the branch 37 | Phi map[string]string // map[choiceID] = phi (handles nestled phis) 38 | } 39 | 40 | func InitFork() *Fork { 41 | return &Fork{ 42 | Choices: make(map[string][]string), 43 | Branches: make(map[string][]string), 44 | Vars: make(map[string]*Var), 45 | Bases: make(map[string]map[string]bool), 46 | ToKill: make(map[string]bool), 47 | } 48 | } 49 | 50 | func (f *Fork) MarkMany(ids []string) { 51 | for _, id := range ids { 52 | f.Mark(id) 53 | } 54 | } 55 | 56 | func (f *Fork) Mark(id string) { 57 | f.ToKill[id] = true 58 | } 59 | 60 | func (f *Fork) MarkedForDeath(id string) bool { 61 | return f.ToKill[id] 62 | } 63 | 64 | func (f *Fork) InBranch(branch string, id string) bool { 65 | for _, v := range f.Branches[branch] { 66 | if id == v { 67 | return true 68 | } 69 | } 70 | return false 71 | } 72 | 73 | func (f *Fork) AddToBranch(branch string, id string) { 74 | if !f.InBranch(branch, id) { 75 | f.Branches[branch] = append(f.Branches[branch], id) 76 | } 77 | } 78 | 79 | func (f *Fork) AddVar(branch string, base string, id string, v *Var) { 80 | f.AddToBranch(branch, id) 81 | 82 | if _, ok := f.Vars[id]; !ok { 83 | f.Vars[id] = v 84 | } else { 85 | for k, vlast := range v.Last { 86 | f.Vars[id].Last[k] = vlast 87 | } 88 | for k, vphi := range v.Phi { 89 | f.Vars[id].Phi[k] = vphi 90 | } 91 | } 92 | 93 | if _, ok := f.Bases[branch]; ok { 94 | f.Bases[branch][base] = true 95 | } else { 96 | f.Bases[branch] = make(map[string]bool) 97 | f.Bases[branch][base] = true 98 | } 99 | } 100 | 101 | func (f *Fork) IsPhi(r *rules.Wrap) bool { 102 | if f.Vars[r.Value] == nil { 103 | return false 104 | } 105 | return f.Vars[r.Value].IsPhi(r.Choice()) 106 | } 107 | 108 | func NewVar(base string, last bool, ssa string, choice string, phi string) *Var { 109 | v := &Var{Base: base, Last: make(map[string]bool), SSA: ssa, Phi: make(map[string]string), Previous: make(map[string]string)} 110 | v.Last[choice] = last 111 | v.Phi[choice] = phi 112 | return v 113 | } 114 | 115 | func (f *Fork) Get(id string) *Var { 116 | return f.Vars[id] 117 | } 118 | 119 | func (f *Fork) GetPrevious(id string, branch string, fallback []string) string { 120 | if r, ok := f.Vars[id].Previous[branch]; ok { 121 | return r 122 | } 123 | 124 | //This is a hacky solution, if the branch isn't in the map, fallback to another branch 125 | //really only happens with phis generated to sync the branches so should be fine 126 | for _, b := range fallback { 127 | if r, ok := f.Vars[id].Previous[b]; b != branch && ok { 128 | return r 129 | } 130 | } 131 | panic(fmt.Sprintf("no previous state found for variable %s in branch %s", id, branch)) 132 | } 133 | 134 | func (v *Var) AddPrevious(branchId string, previous string) { 135 | v.Previous[branchId] = previous 136 | } 137 | 138 | func (v *Var) IsPhi(choice string) bool { 139 | return v.SSA == v.Phi[choice] 140 | } 141 | 142 | func (v *Var) FullPhi(choice string) string { 143 | return fmt.Sprintf("%s_%s", v.Base, v.Phi[choice]) 144 | } 145 | 146 | func (v *Var) PhiInt(choice string) int { 147 | i, err := strconv.ParseInt(v.Phi[choice], 10, 32) 148 | if err != nil { 149 | panic(err) 150 | } 151 | return int(i) 152 | } 153 | 154 | func (v *Var) PhiInt16(choice string) int16 { 155 | i, err := strconv.ParseInt(v.Phi[choice], 10, 32) 156 | if err != nil { 157 | panic(err) 158 | } 159 | return int16(i) 160 | } 161 | 162 | type PhiState struct { 163 | levels int 164 | } 165 | 166 | func NewPhiState() *PhiState { 167 | return &PhiState{ 168 | levels: 0, 169 | } 170 | } 171 | 172 | func (p *PhiState) Check() bool { 173 | return p.levels > 0 174 | } 175 | 176 | func (p *PhiState) Level() int { 177 | return p.levels 178 | } 179 | 180 | func (p *PhiState) In() { 181 | p.levels = p.levels + 1 182 | } 183 | 184 | func (p *PhiState) Out() { 185 | if p.levels != 0 { 186 | p.levels = p.levels - 1 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /smt/generator_log_test.go: -------------------------------------------------------------------------------- 1 | package smt 2 | 3 | import ( 4 | "fault/listener" 5 | "fault/llvm" 6 | "fault/preprocess" 7 | "fault/swaps" 8 | "fault/types" 9 | "fault/util" 10 | gopath "path" 11 | "testing" 12 | ) 13 | 14 | func TestChange(t *testing.T) { 15 | test := `spec test1; 16 | 17 | def amount = stock{ 18 | value: 10, 19 | }; 20 | 21 | def test = flow{ 22 | foo: new amount, 23 | bar: func{ 24 | foo.value -> 2; 25 | }, 26 | }; 27 | 28 | for 1 init{t = new test;} run { 29 | t.bar; 30 | }; 31 | ` 32 | expecting := `(set-logic QF_NRA) 33 | (declare-fun test1_t_foo_value_0 () Real) 34 | (declare-fun test1_t_foo_value_1 () Real) 35 | (assert (= test1_t_foo_value_0 10.0)) 36 | (assert (= test1_t_foo_value_1 (- test1_t_foo_value_0 2.0))) 37 | ` 38 | 39 | generator := prepLogTest("", test, true, false) 40 | 41 | err := compareResults("LogChange", generator.SMT(), expecting) 42 | 43 | if err != nil { 44 | t.Fatalf(err.Error()) 45 | } 46 | 47 | if len(generator.Log.Events) != 3 { 48 | t.Fatalf("ResultLog has wrong number of events got=%d, want=2", len(generator.Log.Events)) 49 | } 50 | 51 | if generator.Log.Events[1].Type != "TRIGGER" && generator.Log.Events[1].Variable != "test1_t_bar" { 52 | t.Fatalf("Event has wrong data got=%s, want=0,TRIGGER,@__run,test1_t_bar,,,", generator.Log.Events[1].String()) 53 | } 54 | 55 | if generator.Log.Events[1].String() != "0,TRIGGER,@__run,test1_t_bar,,,\n" { 56 | t.Fatalf("Event has wrong data got=%s, want=0,TRIGGER,@__run,test1_t_bar,,,", generator.Log.Events[1].String()) 57 | } 58 | } 59 | 60 | func TestTransition(t *testing.T) { 61 | test := `system test1; 62 | 63 | component drain = states{ 64 | initial: func{ 65 | advance(this.open); 66 | }, 67 | open: func{ 68 | stay(); 69 | }, 70 | }; 71 | 72 | start { 73 | drain: initial, 74 | }; 75 | 76 | for 1 run {}; 77 | 78 | ` 79 | expecting := `(set-logicQF_NRA) 80 | (declare-fun test1_drain_open_2 () Bool) 81 | (declare-fun test1_drain_initial_3 () Bool) 82 | (declare-fun test1_drain_initial_0 () Bool) 83 | (declare-fun test1_drain_open_0 () Bool) 84 | (declare-fun test1_drain_initial_1 () Bool) 85 | (declare-fun test1_drain_open_1 () Bool) 86 | (declare-fun test1_drain_initial_2 () Bool) 87 | (assert (= test1_drain_initial_0 false)) 88 | (assert (= test1_drain_open_0 false)) 89 | (assert (= test1_drain_initial_1 true)) 90 | (assert (= test1_drain_open_1 true)) 91 | (assert (= test1_drain_initial_2 false)) 92 | (assert (ite (= test1_drain_initial_1 true) (and (= test1_drain_open_2 test1_drain_open_1)(= test1_drain_initial_3 test1_drain_initial_2))(and (= test1_drain_open_2 test1_drain_open_0)(= test1_drain_initial_3 test1_drain_initial_1)))) 93 | ` 94 | 95 | generator := prepLogTest("", test, false, false) 96 | 97 | err := compareResults("LogTransition", generator.SMT(), expecting) 98 | 99 | if err != nil { 100 | t.Fatalf(err.Error()) 101 | } 102 | 103 | if len(generator.Log.Events) != 10 { 104 | t.Fatalf("ResultLog has wrong number of events got=%d, want=1-", len(generator.Log.Events)) 105 | } 106 | 107 | if generator.Log.Events[6].String() != "1,TRANSITION,,__state,test1_drain_initial,test1_drain_open,\n" { 108 | t.Fatalf("Event has wrong data got=%s, want=1,TRANSITION,,__state,test1_drain_initial,test1_drain_open,", generator.Log.Events[6].String()) 109 | } 110 | 111 | if generator.Log.Events[3].String() != "1,TRIGGER,@__run,test1_drain_initial__state,,,\n" { 112 | t.Fatalf("Event has wrong data got=%s, want=1,TRIGGER,@__run,test1_drain_initial__state,,,", generator.Log.Events[3].String()) 113 | } 114 | } 115 | 116 | func prepLogTest(filepath string, test string, specType bool, testRun bool) *Generator { 117 | flags := make(map[string]bool) 118 | flags["specType"] = specType 119 | flags["testing"] = testRun 120 | flags["skipRun"] = false 121 | 122 | var path string 123 | if filepath != "" { 124 | filepath = util.Filepath(filepath) 125 | path = gopath.Dir(filepath) 126 | } 127 | 128 | l := listener.Execute(test, path, flags) 129 | pre := preprocess.Execute(l) 130 | ty := types.Execute(pre.Processed, pre) 131 | sw := swaps.NewPrecompiler(ty) 132 | tree := sw.Swap(ty.Checked) 133 | compiler := llvm.Execute(tree, ty.SpecStructs, l.Uncertains, l.Unknowns, sw.Alias, true) 134 | 135 | //fmt.Println(compiler.GetIR()) 136 | generator := Execute(compiler) 137 | return generator 138 | } 139 | -------------------------------------------------------------------------------- /smt/generator_unit_test.go: -------------------------------------------------------------------------------- 1 | package smt 2 | 3 | import ( 4 | "fault/smt/rules" 5 | "testing" 6 | 7 | "github.com/llir/llvm/ir" 8 | "github.com/llir/llvm/ir/constant" 9 | "github.com/llir/llvm/ir/enum" 10 | irtypes "github.com/llir/llvm/ir/types" 11 | ) 12 | 13 | func TestCreateCondRule(t *testing.T) { 14 | g := NewGenerator() 15 | cond := &rules.Infix{ 16 | X: &rules.Wrap{ 17 | Value: "x", 18 | All: true, 19 | }, 20 | Y: &rules.Wrap{ 21 | Value: "y", 22 | All: true, 23 | }, 24 | Op: ">", 25 | } 26 | inst := g.createCondRule(cond) 27 | 28 | if inst.(*rules.Infix).Y.(*rules.Wrap).Value != "y" { 29 | t.Fatalf("createCondRule returns the wrong value. got=%s", inst.(*rules.Infix).String()) 30 | } 31 | 32 | cond2 := &rules.Infix{ 33 | X: &rules.Wrap{ 34 | Value: "x", 35 | All: true, 36 | }, 37 | Y: &rules.Wrap{ 38 | Value: "y", 39 | All: true, 40 | }, 41 | Op: "true", 42 | } 43 | inst2 := g.createCondRule(cond2) 44 | 45 | if inst2.(*rules.Infix).Y.(*rules.Wrap).Value != "True" { 46 | t.Fatalf("createCondRule returns the wrong value. got=%s", inst2.(*rules.Infix).String()) 47 | } 48 | } 49 | 50 | func TestParseTerms(t *testing.T) { 51 | g := NewGenerator() 52 | 53 | b := ir.NewBlock("test") 54 | alloc := b.NewAlloca(irtypes.Double) 55 | alloc.SetName("test_this_var") 56 | val := constant.NewFloat(irtypes.Double, 2) 57 | store := b.NewStore(val, alloc) 58 | g.variables.SSA["test_this_var"] = 0 59 | 60 | alloc2 := b.NewAlloca(irtypes.Double) 61 | alloc2.SetName("test_this_var2") 62 | val2 := constant.NewFloat(irtypes.Double, 3) 63 | store2 := b.NewStore(val2, alloc2) 64 | g.variables.SSA["test_this_var2"] = 0 65 | 66 | terms := ir.NewBlock("test-true") 67 | terms.NewFCmp(enum.FPredOGT, store.Src, store2.Src) 68 | g.parseTerms([]*ir.Block{terms}) 69 | if len(g.variables.Ref) != 1 { 70 | t.Fatal("parse terms failed to save a rule.") 71 | } 72 | if g.variables.Ref["@__run-%0"].(*rules.Infix).X.(*rules.Wrap).Value != "2.0" { 73 | t.Fatalf("parse terms produced the wrong x value. got=%s", g.variables.Ref["%0"].(*rules.Infix).X.(*rules.Wrap).Value) 74 | } 75 | 76 | if g.variables.Ref["@__run-%0"].(*rules.Infix).Y.(*rules.Wrap).Value != "3.0" { 77 | t.Fatalf("parse terms produced the wrong y value. got=%s", g.variables.Ref["%0"].(*rules.Infix).Y.(*rules.Wrap).Value) 78 | } 79 | } 80 | 81 | func TestFetchIdent(t *testing.T) { 82 | g := NewGenerator() 83 | b := ir.NewBlock("test") 84 | alloc := b.NewAlloca(irtypes.I32) 85 | alloc.SetName("test_this_var") 86 | val := constant.NewInt(irtypes.I32, 0) 87 | store := b.NewStore(val, alloc) 88 | g.variables.Loads["@__run-%1"] = store.Dst 89 | g.variables.SSA["test_this_var"] = 0 90 | g.variables.Ref["@__run-%2"] = &rules.Infix{ 91 | X: &rules.Wrap{ 92 | Value: "x", 93 | All: true, 94 | }, 95 | Y: &rules.Wrap{ 96 | Value: "y", 97 | All: true, 98 | }, 99 | Op: ">", 100 | } 101 | 102 | test1 := g.fetchIdent("%1", &rules.Wrap{ 103 | Value: "x", 104 | All: true, 105 | }) 106 | 107 | test2 := g.fetchIdent("%2", &rules.Wrap{ 108 | Value: "x", 109 | All: true, 110 | }) 111 | 112 | if test1.(*rules.Wrap).Value != "%test_this_var_0" { 113 | t.Fatalf("fetchIdent returned the wrong result. got=%s", test1.String()) 114 | } 115 | 116 | if test2.(*rules.Infix).X.String() != "x" || test2.(*rules.Infix).Y.String() != "y" { 117 | t.Fatalf("fetchIdent returned the wrong result. got=%s", test2.String()) 118 | } 119 | 120 | test3 := g.tempToIdent(g.variables.Ref["@__run-%2"]) 121 | 122 | if test3.(*rules.Infix).X.String() != "x" || test3.(*rules.Infix).Y.String() != "y" { 123 | t.Fatalf("tempToIdent returned the wrong result. got=%s", test3.String()) 124 | } 125 | } 126 | 127 | func TestStoreRule(t *testing.T) { 128 | g := NewGenerator() 129 | b := ir.NewBlock("test") 130 | alloc := b.NewAlloca(irtypes.I32) 131 | alloc.SetName("test_this_var") 132 | val := constant.NewInt(irtypes.I32, 0) 133 | store := b.NewStore(val, alloc) 134 | 135 | alloc2 := b.NewAlloca(irtypes.I32) 136 | alloc2.SetName("test_this_var2") 137 | store2 := b.NewStore(val, alloc2) 138 | 139 | g.variables.Loads["@__run-%1"] = store.Dst 140 | g.variables.SSA["test_this_var"] = 0 141 | g.variables.Ref["@__run-%2"] = &rules.Infix{ 142 | X: &rules.Wrap{ 143 | Value: "x", 144 | All: true, 145 | }, 146 | Y: &rules.Wrap{ 147 | Value: "y", 148 | All: true, 149 | }, 150 | Op: ">", 151 | } 152 | 153 | test1 := g.storeRule(store) 154 | if len(test1) != 1 { 155 | t.Fatalf("storeRule did not store new rule. got=%d", len(test1)) 156 | } 157 | test2 := g.storeRule(store2) 158 | if len(test2) != 1 { 159 | t.Fatalf("storeRule did not store new rule. got=%d", len(test2)) 160 | } 161 | if test2[0].(*rules.Infix).X.String() != "test_this_var2_0" { 162 | t.Fatalf("storeRule malformed rule got=%s", test2[0].(*rules.Infix).X.String()) 163 | } 164 | 165 | if test2[0].(*rules.Infix).Y.String() != "0" { 166 | t.Fatalf("storeRule malformed rule got=%s", test2[0].(*rules.Infix).Y.String()) 167 | } 168 | } 169 | 170 | func TestParallelPermutations(t *testing.T) { 171 | g := NewGenerator() 172 | test1 := []string{"foo", "bar"} 173 | results1 := g.parallelPermutations(test1) 174 | 175 | test2 := []string{"foo", "bar", "fizz", "buzz"} 176 | results2 := g.parallelPermutations(test2) 177 | 178 | test3 := []string{"foo", "bar", "fizz", "buzz", "foosh"} 179 | results3 := g.parallelPermutations(test3) 180 | 181 | if len(results1) != 2 { 182 | t.Fatalf("wrong number of permutations on set 1got=%d", len(results1)) 183 | } 184 | 185 | if len(results2) != 24 { 186 | t.Fatalf("wrong number of permutations on set 2 got=%d", len(results2)) 187 | } 188 | 189 | if len(results3) != 120 { 190 | t.Fatalf("wrong number of permutations on set 3 got=%d", len(results3)) 191 | } 192 | } 193 | 194 | func TestConvertIdent(t *testing.T) { 195 | g := NewGenerator() 196 | b := ir.NewBlock("test") 197 | alloc := b.NewAlloca(irtypes.I32) 198 | alloc.SetName("test_this_var") 199 | val := constant.NewInt(irtypes.I32, 0) 200 | store := b.NewStore(val, alloc) 201 | g.variables.Loads["@__run-%1"] = store.Dst 202 | g.variables.SSA["test_this_var"] = 0 203 | 204 | if g.variables.ConvertIdent("@__run", "%test_this_var") != "test_this_var_0" { 205 | t.Fatalf("ConvertIdent returned the wrong value. got=%s", g.variables.ConvertIdent("@__run", "%test_this_var")) 206 | } 207 | 208 | if g.variables.ConvertIdent("@__run", "%1") != "test_this_var_0" { 209 | t.Fatalf("ConvertIdent returned the wrong value. got=%s", g.variables.ConvertIdent("@__run", "%1")) 210 | } 211 | 212 | } 213 | 214 | func TestNewConstants(t *testing.T) { 215 | g := NewGenerator() 216 | globals := []*ir.Global{ 217 | ir.NewGlobalDef("test1", constant.NewFloat(irtypes.Double, 10)), 218 | ir.NewGlobalDef("test2", constant.NewFloat(irtypes.Double, 20)), 219 | ir.NewGlobalDef("test3", constant.NewFloat(irtypes.Double, 30)), 220 | } 221 | g.variables.SSA["test1"] = 0 222 | g.variables.SSA["test2"] = 2 223 | g.variables.SSA["test3"] = 5 224 | 225 | results := g.newConstants(globals) 226 | 227 | if len(results) != 3 { 228 | t.Fatalf("newConstants returned an incorrect number of results. got=%d", len(results)) 229 | } 230 | 231 | if results[0] != "(assert (= test1_1 10.0))" { 232 | t.Fatalf("newConstants returned an incorrect value at index 0. got=%s", results[0]) 233 | } 234 | 235 | if results[1] != "(assert (= test2_3 20.0))" { 236 | t.Fatalf("newConstants returned an incorrect value at index 1. got=%s", results[1]) 237 | } 238 | 239 | if results[2] != "(assert (= test3_6 30.0))" { 240 | t.Fatalf("newConstants returned an incorrect value at index 2. got=%s", results[2]) 241 | } 242 | } 243 | 244 | func TestCaptureState(t *testing.T) { 245 | test1 := "test_constant" 246 | name, a, c := captureState(test1) 247 | if name != "" || a || !c { 248 | t.Fatal("captureState failed on a constant") 249 | } 250 | 251 | test2 := "test_this_var" 252 | name2, a2, c2 := captureState(test2) 253 | if name2 != "" || !a2 || c2 { 254 | t.Fatalf("captureState failed on a general state variablegot=%s %v %v", name2, a2, c2) 255 | } 256 | 257 | test3 := "test_this_var_2" 258 | name3, a3, c3 := captureState(test3) 259 | if name3 != "2" || a3 || c3 { 260 | t.Fatalf("captureState failed on a specific state variable. got=%s %v %v", name3, a3, c3) 261 | } 262 | 263 | } 264 | -------------------------------------------------------------------------------- /smt/rules/rules_test.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/llir/llvm/ir/constant" 7 | irtypes "github.com/llir/llvm/ir/types" 8 | ) 9 | 10 | func TestTagRules(t *testing.T) { 11 | rules := []Rule{&Wrap{Value: "x"}, &Infix{ 12 | X: &Wrap{ 13 | Value: "x", 14 | All: true, 15 | }, 16 | Y: &Wrap{ 17 | Value: "y", 18 | All: true, 19 | }, 20 | Op: ">", 21 | }, &Ite{ 22 | Cond: &Wrap{ 23 | Value: "x", 24 | All: true, 25 | }, 26 | T: []Rule{&Wrap{ 27 | Value: "x", 28 | All: true, 29 | }}, 30 | F: []Rule{&Wrap{ 31 | Value: "y", 32 | All: true, 33 | }}, 34 | }, &Vwrap{ 35 | Value: constant.NewInt(irtypes.I32, 0), 36 | }} 37 | r := TagRules(rules, "foo", "bar") 38 | if r[0].(*Wrap).tag.branch != "foo" || r[0].(*Wrap).tag.block != "bar" { 39 | t.Fatalf("tag not set correctly for rule %s. got=%s", rules[0], r[0].(*Wrap).tag.String()) 40 | } 41 | 42 | if r[1].(*Infix).tag.branch != "foo" || r[1].(*Infix).tag.block != "bar" { 43 | t.Fatalf("tag not set correctly for rule %s. got=%s", rules[1], r[1].(*Infix).tag.String()) 44 | } 45 | 46 | if r[2].(*Ite).tag.branch != "foo" || r[2].(*Ite).tag.block != "bar" { 47 | t.Fatalf("tag not set correctly for rule %s. got=%s", rules[2], r[2].(*Ite).tag.String()) 48 | } 49 | 50 | if r[3].(*Vwrap).tag.branch != "foo" || r[3].(*Vwrap).tag.block != "bar" { 51 | t.Fatalf("tag not set correctly for rule %s. got=%s", rules[3], r[3].(*Vwrap).tag.String()) 52 | } 53 | } 54 | 55 | func TestAssrtType(t *testing.T) { 56 | a := &Assrt{ 57 | Variable: &Wrap{ 58 | Value: "x", 59 | All: true, 60 | }, 61 | Conjunction: "&&", 62 | Assertion: &Wrap{ 63 | Value: "y", 64 | All: true, 65 | }, 66 | } 67 | 68 | a.Tag("test", "me") 69 | 70 | if a.tag.block != "me" || a.tag.branch != "test" { 71 | t.Fatalf("type tagged incorrectly. got=%s block %s branch", a.tag.block, a.tag.branch) 72 | } 73 | 74 | if a.String() != "x&&y" { 75 | t.Fatalf("String() failed got=%s", a.String()) 76 | } 77 | } 78 | 79 | func TestInfixType(t *testing.T) { 80 | i := &Infix{ 81 | X: &Wrap{ 82 | Value: "x", 83 | All: true, 84 | }, 85 | Y: &Wrap{ 86 | Value: "y", 87 | All: true, 88 | }, 89 | Op: ">", 90 | } 91 | 92 | i.Tag("test", "me") 93 | 94 | if i.tag.block != "me" || i.tag.branch != "test" { 95 | t.Fatalf("type tagged incorrectly. got=%s block %s branch", i.tag.block, i.tag.branch) 96 | } 97 | 98 | if i.String() != "x > y" { 99 | t.Fatalf("String() failed got=%s", i.String()) 100 | } 101 | } 102 | 103 | func TestIfeType(t *testing.T) { 104 | cond := &Infix{ 105 | X: &Wrap{ 106 | Value: "x", 107 | All: true, 108 | }, 109 | Y: &Wrap{ 110 | Value: "y", 111 | All: true, 112 | }, 113 | Op: ">", 114 | } 115 | 116 | i := &Ite{ 117 | Cond: cond, 118 | T: []Rule{&Wrap{ 119 | Value: "x", 120 | All: true, 121 | }}, 122 | F: []Rule{&Wrap{ 123 | Value: "y", 124 | All: true, 125 | }}, 126 | } 127 | 128 | i.Tag("test", "me") 129 | 130 | if i.tag.block != "me" || i.tag.branch != "test" { 131 | t.Fatalf("type tagged incorrectly. got=%s block %s branch", i.tag.block, i.tag.branch) 132 | } 133 | 134 | if i.String() != "if x > y then [x] else [y]" { 135 | t.Fatalf("String() failed got=%s", i.String()) 136 | } 137 | } 138 | 139 | func TestInvariantType(t *testing.T) { 140 | i := &Invariant{ 141 | Left: &Wrap{ 142 | Value: "x", 143 | All: true, 144 | }, 145 | Operator: "&&", 146 | Right: &Wrap{ 147 | Value: "y", 148 | All: true, 149 | }, 150 | } 151 | 152 | i.Tag("test", "me") 153 | 154 | if i.tag.block != "me" || i.tag.branch != "test" { 155 | t.Fatalf("type tagged incorrectly. got=%s block %s branch", i.tag.block, i.tag.branch) 156 | } 157 | 158 | if i.String() != "x&&y" { 159 | t.Fatalf("String() failed got=%s", i.String()) 160 | } 161 | } 162 | 163 | func TestWrapType(t *testing.T) { 164 | w := &Wrap{ 165 | Value: "x", 166 | All: true, 167 | } 168 | 169 | w.Tag("test", "me") 170 | 171 | if w.tag.block != "me" || w.tag.branch != "test" { 172 | t.Fatalf("assrt type tagged incorrectly. got=%s block %s branch", w.tag.block, w.tag.branch) 173 | } 174 | 175 | if w.String() != "x" { 176 | t.Fatalf("assrt String() failed got=%s", w.String()) 177 | } 178 | } 179 | 180 | func TestVWrapType(t *testing.T) { 181 | val := constant.NewInt(irtypes.I32, 0) 182 | w := &Vwrap{ 183 | Value: val, 184 | } 185 | 186 | w.Tag("test", "me") 187 | 188 | if w.tag.block != "me" || w.tag.branch != "test" { 189 | t.Fatalf("assrt type tagged incorrectly. got=%s block %s branch", w.tag.block, w.tag.branch) 190 | } 191 | 192 | if w.String() != "i32 0" { 193 | t.Fatalf("assrt String() failed got=%s", w.String()) 194 | } 195 | } 196 | 197 | func TestWrapGroupType(t *testing.T) { 198 | wg := &WrapGroup{ 199 | Wraps: []*Wrap{{ 200 | Value: "x", 201 | All: true, 202 | }, { 203 | Value: "y", 204 | All: true, 205 | }, { 206 | Value: "z", 207 | All: true, 208 | }}} 209 | 210 | wg.Tag("test", "me") 211 | 212 | if wg.tag.block != "me" || wg.tag.branch != "test" { 213 | t.Fatalf("assrt type tagged incorrectly. got=%s block %s branch", wg.tag.block, wg.tag.branch) 214 | } 215 | 216 | if wg.String() != "xyz" { 217 | t.Fatalf("assrt String() failed got=%s", wg.String()) 218 | } 219 | } 220 | 221 | func TestBranchType(t *testing.T) { 222 | b := &branch{ 223 | branch: "dummy", 224 | block: "func", 225 | } 226 | 227 | if b.String() != "dummy.func" { 228 | t.Fatalf("assrt String() failed got=%s", b.String()) 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /smt/testdata/asserts.fspec: -------------------------------------------------------------------------------- 1 | spec asserts; 2 | 3 | def fsample = flow{ 4 | target: new ssample, 5 | fn: func{ 6 | target.value -> target.value/2; 7 | }, 8 | }; 9 | 10 | def ssample = stock{ 11 | value: 40, 12 | }; 13 | 14 | assert ssample.value == 40; 15 | assume fsample.target > 2; 16 | 17 | for 4 init{test = new fsample;} run { 18 | test.fn; 19 | assert test.target == 10; 20 | } -------------------------------------------------------------------------------- /smt/testdata/bathtub.fspec: -------------------------------------------------------------------------------- 1 | spec bathtub; 2 | 3 | def faucet = flow{ 4 | water: new tub, 5 | in: func{ 6 | water.level <- 10; 7 | }, 8 | }; 9 | 10 | def drain = flow{ 11 | water: new tub, 12 | out: func{ 13 | water.level -> 20; 14 | }, 15 | }; 16 | 17 | def tub = stock{ 18 | level: 5, // out of 100% 19 | }; 20 | 21 | for 4 init{drawn = new faucet; 22 | pipe = new drain;} run { 23 | drawn.in | pipe.out; 24 | } -------------------------------------------------------------------------------- /smt/testdata/bathtub.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun bathtub_drawn_water_level_0 () Real) 3 | (declare-fun bathtub_pipe_water_level_0 () Real) 4 | (declare-fun bathtub_drawn_water_level_1 () Real) 5 | (declare-fun bathtub_pipe_water_level_1 () Real) 6 | (declare-fun bathtub_pipe_water_level_2 () Real) 7 | (declare-fun bathtub_drawn_water_level_2 () Real) 8 | (declare-fun bathtub_drawn_water_level_3 () Real) 9 | (declare-fun bathtub_pipe_water_level_3 () Real) 10 | (declare-fun bathtub_drawn_water_level_4 () Real) 11 | (declare-fun bathtub_pipe_water_level_4 () Real) 12 | (declare-fun bathtub_pipe_water_level_5 () Real) 13 | (declare-fun bathtub_drawn_water_level_5 () Real) 14 | (declare-fun bathtub_pipe_water_level_6 () Real) 15 | (declare-fun bathtub_drawn_water_level_6 () Real) 16 | (declare-fun bathtub_drawn_water_level_7 () Real) 17 | (declare-fun bathtub_pipe_water_level_7 () Real) 18 | (declare-fun bathtub_pipe_water_level_8 () Real) 19 | (declare-fun bathtub_drawn_water_level_8 () Real) 20 | (declare-fun bathtub_drawn_water_level_9 () Real) 21 | (declare-fun bathtub_pipe_water_level_9 () Real) 22 | (declare-fun bathtub_drawn_water_level_10 () Real) 23 | (declare-fun bathtub_pipe_water_level_10 () Real) 24 | (declare-fun bathtub_pipe_water_level_11 () Real) 25 | (declare-fun bathtub_drawn_water_level_11 () Real) 26 | (declare-fun bathtub_drawn_water_level_12 () Real) 27 | (declare-fun bathtub_pipe_water_level_12 () Real) 28 | (assert (= bathtub_drawn_water_level_0 5.0)) 29 | (assert (= bathtub_pipe_water_level_0 5.0)) 30 | (assert (= bathtub_drawn_water_level_1 (+ bathtub_drawn_water_level_0 10.0))) 31 | (assert (= bathtub_pipe_water_level_1 (- bathtub_pipe_water_level_0 20.0))) 32 | (assert (= bathtub_pipe_water_level_2 (- bathtub_pipe_water_level_0 20.0))) 33 | (assert (= bathtub_drawn_water_level_2 (+ bathtub_drawn_water_level_0 10.0))) 34 | (assert (or (= bathtub_drawn_water_level_3 bathtub_drawn_water_level_1) (= bathtub_drawn_water_level_3 bathtub_drawn_water_level_2))) 35 | (assert (or (= bathtub_pipe_water_level_3 bathtub_pipe_water_level_1) (= bathtub_pipe_water_level_3 bathtub_pipe_water_level_2))) 36 | (assert (= bathtub_drawn_water_level_4 (+ bathtub_drawn_water_level_3 10.0))) 37 | (assert (= bathtub_pipe_water_level_4 (- bathtub_pipe_water_level_3 20.0))) 38 | (assert (= bathtub_pipe_water_level_5 (- bathtub_pipe_water_level_3 20.0))) 39 | (assert (= bathtub_drawn_water_level_5 (+ bathtub_drawn_water_level_3 10.0))) 40 | (assert (or (= bathtub_pipe_water_level_6 bathtub_pipe_water_level_4) (= bathtub_pipe_water_level_6 bathtub_pipe_water_level_5))) 41 | (assert (or (= bathtub_drawn_water_level_6 bathtub_drawn_water_level_4) (= bathtub_drawn_water_level_6 bathtub_drawn_water_level_5))) 42 | (assert (= bathtub_drawn_water_level_7 (+ bathtub_drawn_water_level_6 10.0))) 43 | (assert (= bathtub_pipe_water_level_7 (- bathtub_pipe_water_level_6 20.0))) 44 | (assert (= bathtub_pipe_water_level_8 (- bathtub_pipe_water_level_6 20.0))) 45 | (assert (= bathtub_drawn_water_level_8 (+ bathtub_drawn_water_level_6 10.0))) 46 | (assert (or (= bathtub_drawn_water_level_9 bathtub_drawn_water_level_7) (= bathtub_drawn_water_level_9 bathtub_drawn_water_level_8))) 47 | (assert (or (= bathtub_pipe_water_level_9 bathtub_pipe_water_level_7) (= bathtub_pipe_water_level_9 bathtub_pipe_water_level_8))) 48 | (assert (= bathtub_drawn_water_level_10 (+ bathtub_drawn_water_level_9 10.0))) 49 | (assert (= bathtub_pipe_water_level_10 (- bathtub_pipe_water_level_9 20.0))) 50 | (assert (= bathtub_pipe_water_level_11 (- bathtub_pipe_water_level_9 20.0))) 51 | (assert (= bathtub_drawn_water_level_11 (+ bathtub_drawn_water_level_9 10.0))) 52 | (assert (or (= bathtub_drawn_water_level_12 bathtub_drawn_water_level_10) (= bathtub_drawn_water_level_12 bathtub_drawn_water_level_11))) 53 | (assert (or (= bathtub_pipe_water_level_12 bathtub_pipe_water_level_10) (= bathtub_pipe_water_level_12 bathtub_pipe_water_level_11))) -------------------------------------------------------------------------------- /smt/testdata/bathtub2.fspec: -------------------------------------------------------------------------------- 1 | spec bathtub; 2 | 3 | def faucet = flow{ 4 | water: new tub, 5 | in: func{ 6 | water.level <- 10; 7 | }, 8 | out: func{ 9 | water.level -> 20; 10 | }, 11 | }; 12 | 13 | def tub = stock{ 14 | level: 5, // out of 100% 15 | }; 16 | 17 | for 3 init{drawn = new faucet; 18 | } run { 19 | drawn.in | drawn.out; 20 | } -------------------------------------------------------------------------------- /smt/testdata/bathtub2.ll: -------------------------------------------------------------------------------- 1 | define void @__run() { 2 | block-0: 3 | %bathtub_drawn_water_level = alloca double 4 | store double 5.0, double* %bathtub_drawn_water_level 5 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !\3114905d4a4c10fbc4b5c21a78c55b23a !DIBasicType(tag: DW_TAG_string_type) 6 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !\3114905d4a4c10fbc4b5c21a78c55b23a !DIBasicType(tag: DW_TAG_string_type) 7 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !f6310547ab23560514f17d3aceeb856a !DIBasicType(tag: DW_TAG_string_type) 8 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !f6310547ab23560514f17d3aceeb856a !DIBasicType(tag: DW_TAG_string_type) 9 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !\3774fb5b072ce98c5739c1a1d79f3a750 !DIBasicType(tag: DW_TAG_string_type) 10 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !\3774fb5b072ce98c5739c1a1d79f3a750 !DIBasicType(tag: DW_TAG_string_type) 11 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !\30ededaa6a0dcd9894de44fee69ad2679 !DIBasicType(tag: DW_TAG_string_type) 12 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !\30ededaa6a0dcd9894de44fee69ad2679 !DIBasicType(tag: DW_TAG_string_type) 13 | ret void 14 | } 15 | 16 | define void @bathtub_drawn_in(double* %bathtub_drawn_water_level) { 17 | block-1: 18 | %0 = load double, double* %bathtub_drawn_water_level 19 | %1 = fadd double %0, 10.0 20 | store double %1, double* %bathtub_drawn_water_level 21 | ret void 22 | } 23 | 24 | define void @bathtub_drawn_out(double* %bathtub_drawn_water_level) { 25 | block-2: 26 | %0 = load double, double* %bathtub_drawn_water_level 27 | %1 = fsub double %0, 20.0 28 | store double %1, double* %bathtub_drawn_water_level 29 | ret void 30 | } -------------------------------------------------------------------------------- /smt/testdata/bathtub2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun bathtub_drawn_water_level_0 () Real) 3 | (declare-fun bathtub_drawn_water_level_1 () Real) 4 | (declare-fun bathtub_drawn_water_level_2 () Real) 5 | (declare-fun bathtub_drawn_water_level_3 () Real) 6 | (declare-fun bathtub_drawn_water_level_4 () Real) 7 | (declare-fun bathtub_drawn_water_level_5 () Real) 8 | (declare-fun bathtub_drawn_water_level_6 () Real) 9 | (declare-fun bathtub_drawn_water_level_7 () Real) 10 | (declare-fun bathtub_drawn_water_level_8 () Real) 11 | (declare-fun bathtub_drawn_water_level_9 () Real) 12 | (declare-fun bathtub_drawn_water_level_10 () Real) 13 | (declare-fun bathtub_drawn_water_level_11 () Real) 14 | (declare-fun bathtub_drawn_water_level_12 () Real) 15 | (declare-fun bathtub_drawn_water_level_13 () Real) 16 | (declare-fun bathtub_drawn_water_level_14 () Real) 17 | (declare-fun bathtub_drawn_water_level_15 () Real)(assert (= bathtub_drawn_water_level_0 5.0)) 18 | (assert (= bathtub_drawn_water_level_1 (+ bathtub_drawn_water_level_0 10.0))) 19 | (assert (= bathtub_drawn_water_level_2 (- bathtub_drawn_water_level_1 20.0))) 20 | (assert (= bathtub_drawn_water_level_3 (- bathtub_drawn_water_level_0 20.0))) 21 | (assert (= bathtub_drawn_water_level_4 (+ bathtub_drawn_water_level_3 10.0))) 22 | (assert (or (= bathtub_drawn_water_level_5 bathtub_drawn_water_level_2) (= bathtub_drawn_water_level_5 bathtub_drawn_water_level_4))) 23 | (assert (= bathtub_drawn_water_level_6 (+ bathtub_drawn_water_level_5 10.0))) 24 | (assert (= bathtub_drawn_water_level_7 (- bathtub_drawn_water_level_6 20.0))) 25 | (assert (= bathtub_drawn_water_level_8 (- bathtub_drawn_water_level_5 20.0))) 26 | (assert (= bathtub_drawn_water_level_9 (+ bathtub_drawn_water_level_8 10.0))) 27 | (assert (or (= bathtub_drawn_water_level_10 bathtub_drawn_water_level_7) (= bathtub_drawn_water_level_10 bathtub_drawn_water_level_9))) 28 | (assert (= bathtub_drawn_water_level_11 (+ bathtub_drawn_water_level_10 10.0))) 29 | (assert (= bathtub_drawn_water_level_12 (- bathtub_drawn_water_level_11 20.0))) 30 | (assert (= bathtub_drawn_water_level_13 (- bathtub_drawn_water_level_10 20.0))) 31 | (assert (= bathtub_drawn_water_level_14 (+ bathtub_drawn_water_level_13 10.0))) 32 | (assert (or (= bathtub_drawn_water_level_15 bathtub_drawn_water_level_12) (= bathtub_drawn_water_level_15 bathtub_drawn_water_level_14))) -------------------------------------------------------------------------------- /smt/testdata/bathtub2opt.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'testdata/bathtub2.ll' 2 | source_filename = "testdata/bathtub2.ll" 3 | 4 | define void @__run() { 5 | block-0: 6 | %bathtub_drawn_water_level = alloca double, align 8 7 | store double 5.000000e+00, double* %bathtub_drawn_water_level, align 8 8 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !\3114905d4a4c10fbc4b5c21a78c55b23a !0 9 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !\3114905d4a4c10fbc4b5c21a78c55b23a !0 10 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !f6310547ab23560514f17d3aceeb856a !0 11 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !f6310547ab23560514f17d3aceeb856a !0 12 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !\3774fb5b072ce98c5739c1a1d79f3a750 !0 13 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !\3774fb5b072ce98c5739c1a1d79f3a750 !0 14 | call void @bathtub_drawn_in(double* %bathtub_drawn_water_level), !\30ededaa6a0dcd9894de44fee69ad2679 !0 15 | call void @bathtub_drawn_out(double* %bathtub_drawn_water_level), !\30ededaa6a0dcd9894de44fee69ad2679 !0 16 | ret void 17 | } 18 | 19 | define void @bathtub_drawn_in(double* %bathtub_drawn_water_level) { 20 | block-1: 21 | %0 = load double, double* %bathtub_drawn_water_level, align 8 22 | %1 = fadd double %0, 1.000000e+01 23 | store double %1, double* %bathtub_drawn_water_level, align 8 24 | ret void 25 | } 26 | 27 | define void @bathtub_drawn_out(double* %bathtub_drawn_water_level) { 28 | block-2: 29 | %0 = load double, double* %bathtub_drawn_water_level, align 8 30 | %1 = fsub double %0, 2.000000e+01 31 | store double %1, double* %bathtub_drawn_water_level, align 8 32 | ret void 33 | } 34 | 35 | !0 = !DIBasicType(tag: DW_TAG_string_type) -------------------------------------------------------------------------------- /smt/testdata/booleans.fspec: -------------------------------------------------------------------------------- 1 | spec booleans; 2 | 3 | def st = stock{ 4 | value: true, 5 | }; 6 | 7 | def fl = flow{ 8 | vault: new st, 9 | fn: func{ 10 | if vault.value { 11 | vault.value = false; 12 | }else{ 13 | vault.value = true; 14 | } 15 | }, 16 | }; 17 | 18 | for 1 init{l = new fl;} run { 19 | l.fn; 20 | } -------------------------------------------------------------------------------- /smt/testdata/booleans.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun booleans_l_vault_value_0 () Bool) 3 | (declare-fun booleans_l_vault_value_3 () Bool) 4 | (declare-fun booleans_l_vault_value_1 () Bool) 5 | (declare-fun booleans_l_vault_value_2 () Bool) 6 | (assert (= booleans_l_vault_value_0 true)) 7 | (assert (= booleans_l_vault_value_1 false)) 8 | (assert (= booleans_l_vault_value_2 true)) 9 | (assert (ite 10 | (= booleans_l_vault_value_0 true) 11 | (= booleans_l_vault_value_3 booleans_l_vault_value_1) 12 | (= booleans_l_vault_value_3 booleans_l_vault_value_2))) -------------------------------------------------------------------------------- /smt/testdata/conditionals/condwelse.fspec: -------------------------------------------------------------------------------- 1 | spec condwe; 2 | 3 | def s = stock{ 4 | cond: 1, 5 | value: 10, 6 | }; 7 | 8 | def f = flow{ 9 | base: new s, 10 | change: func{ 11 | if base.cond > 0 { 12 | if base.cond < 4{ 13 | base.value <- 20; 14 | }else{ 15 | base.value -> 30; 16 | } 17 | } 18 | }, 19 | }; 20 | 21 | for 1 init{t = new f;} run { 22 | t.change; 23 | }; -------------------------------------------------------------------------------- /smt/testdata/conditionals/condwelse.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun condwe_t_base_value_2 () Real) 3 | (declare-fun condwe_t_base_value_4 () Real) 4 | (declare-fun condwe_t_base_cond_0 () Real) 5 | (declare-fun condwe_t_base_value_0 () Real) 6 | (declare-fun condwe_t_base_value_1 () Real) 7 | (declare-fun condwe_t_base_value_3 () Real) 8 | (assert (= condwe_t_base_cond_0 1.0)) 9 | (assert (= condwe_t_base_value_0 10.0)) 10 | (assert (= condwe_t_base_value_1 (- condwe_t_base_value_0 30.0))) 11 | (assert (ite (> condwe_t_base_cond_0 0.0) (= condwe_t_base_value_2 condwe_t_base_value_1) (= condwe_t_base_value_2 condwe_t_base_value_0))) 12 | (assert (= condwe_t_base_value_3 (+ condwe_t_base_value_2 20.0))) 13 | (assert (ite (and (> condwe_t_base_cond_0 0.0) (< condwe_t_base_cond_0 4.0)) (= condwe_t_base_value_4 condwe_t_base_value_3) (= condwe_t_base_value_4 condwe_t_base_value_2))) -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond.fspec: -------------------------------------------------------------------------------- 1 | spec multicond; 2 | 3 | def s = stock{ 4 | cond: 1, 5 | value: 10, 6 | }; 7 | 8 | def f = flow{ 9 | base: new s, 10 | change: func{ 11 | if base.cond > 0 { 12 | base.value <- 10; 13 | base.cond <- 2; 14 | } 15 | if base.cond > 4{ 16 | base.value <- 20; 17 | base.cond -> 2; 18 | } 19 | }, 20 | }; 21 | 22 | for 1 init{t = new f;} run { 23 | t.change; 24 | }; -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun multicond_t_base_cond_0 () Real) 3 | (declare-fun multicond_t_base_value_0 () Real) 4 | (declare-fun multicond_t_base_value_2 () Real) 5 | (declare-fun multicond_t_base_cond_2 () Real) 6 | (declare-fun multicond_t_base_value_4 () Real) 7 | (declare-fun multicond_t_base_cond_4 () Real) 8 | (declare-fun multicond_t_base_value_1 () Real) 9 | (declare-fun multicond_t_base_cond_1 () Real) 10 | (declare-fun multicond_t_base_value_3 () Real) 11 | (declare-fun multicond_t_base_cond_3 () Real) 12 | (assert (= multicond_t_base_cond_0 1.0)) 13 | (assert (= multicond_t_base_value_0 10.0)) 14 | (assert (= multicond_t_base_value_1 (+ multicond_t_base_value_0 10.0))) 15 | (assert (= multicond_t_base_cond_1 (+ multicond_t_base_cond_0 2.0))) 16 | (assert (ite (> multicond_t_base_cond_0 0.0) (and (= multicond_t_base_value_2 multicond_t_base_value_1) (= multicond_t_base_cond_2 multicond_t_base_cond_1)) (and (= multicond_t_base_value_2 multicond_t_base_value_0) (= multicond_t_base_cond_2 multicond_t_base_cond_0)))) 17 | (assert (= multicond_t_base_value_3 (+ multicond_t_base_value_2 20.0))) 18 | (assert (= multicond_t_base_cond_3 (- multicond_t_base_cond_2 2.0))) 19 | (assert (ite (> multicond_t_base_cond_2 4.0) (and (= multicond_t_base_value_4 multicond_t_base_value_3) (= multicond_t_base_cond_4 multicond_t_base_cond_3)) (and (= multicond_t_base_value_4 multicond_t_base_value_2) (= multicond_t_base_cond_4 multicond_t_base_cond_2)))) -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond2.fspec: -------------------------------------------------------------------------------- 1 | spec multicond; 2 | 3 | def s = stock{ 4 | cond: 1, 5 | value: 10, 6 | }; 7 | 8 | def f = flow{ 9 | base: new s, 10 | change: func{ 11 | if base.cond > 0 { 12 | if base.cond < 4{ 13 | base.value <- 20; 14 | } 15 | } 16 | }, 17 | }; 18 | 19 | for 1 init{t = new f;} run { 20 | t.change; 21 | }; -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun multicond_t_base_value_2 () Real) 3 | (declare-fun multicond_t_base_cond_0 () Real) 4 | (declare-fun multicond_t_base_value_0 () Real) 5 | (declare-fun multicond_t_base_value_1 () Real) 6 | (assert (= multicond_t_base_cond_0 1.0)) 7 | (assert (= multicond_t_base_value_0 10.0)) 8 | (assert (= multicond_t_base_value_1 (+ multicond_t_base_value_0 20.0))) 9 | (assert (ite (and (> multicond_t_base_cond_0 0.0) (< multicond_t_base_cond_0 4.0))(= multicond_t_base_value_2 multicond_t_base_value_1)(= multicond_t_base_value_2 multicond_t_base_value_0))) -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond3.fspec: -------------------------------------------------------------------------------- 1 | spec multicond; 2 | 3 | def s = stock{ 4 | cond: 1, 5 | value: 10, 6 | }; 7 | 8 | def f = flow{ 9 | base: new s, 10 | change: func{ 11 | if base.cond > 0 { 12 | base.value <- 20; 13 | } 14 | base.cond <- 1; 15 | }, 16 | }; 17 | 18 | for 3 init{t = new f;} run { 19 | if t.base.cond < 4 { 20 | t.change; 21 | } 22 | }; -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond3.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun multicond_t_base_value_2 () Real) 3 | (declare-fun multicond_t_base_value_3 () Real) 4 | (declare-fun multicond_t_base_cond_2 () Real) 5 | (declare-fun multicond_t_base_value_5 () Real) 6 | (declare-fun multicond_t_base_value_6 () Real) 7 | (declare-fun multicond_t_base_cond_4 () Real) 8 | (declare-fun multicond_t_base_value_8 () Real) 9 | (declare-fun multicond_t_base_value_9 () Real) 10 | (declare-fun multicond_t_base_cond_6 () Real) 11 | (declare-fun multicond_t_base_cond_0 () Real) 12 | (declare-fun multicond_t_base_value_0 () Real) 13 | (declare-fun multicond_t_base_value_1 () Real) 14 | (declare-fun multicond_t_base_cond_1 () Real) 15 | (declare-fun multicond_t_base_value_4 () Real) 16 | (declare-fun multicond_t_base_cond_3 () Real) 17 | (declare-fun multicond_t_base_value_7 () Real) 18 | (declare-fun multicond_t_base_cond_5 () Real)(assert (= multicond_t_base_cond_0 1.0)) 19 | (assert (= multicond_t_base_value_0 10.0)) 20 | (assert (= multicond_t_base_value_1 (+ multicond_t_base_value_0 20.0))) 21 | (assert (ite (> multicond_t_base_cond_0 0.0) (= multicond_t_base_value_2 multicond_t_base_value_1) (= multicond_t_base_value_2 multicond_t_base_value_0))) 22 | (assert (= multicond_t_base_cond_1 (+ multicond_t_base_cond_0 1.0))) 23 | (assert (ite (< multicond_t_base_cond_0 4.0) (and (= multicond_t_base_value_3 multicond_t_base_value_2) (= multicond_t_base_cond_2 multicond_t_base_cond_1)) (and (= multicond_t_base_value_3 multicond_t_base_value_0) (= multicond_t_base_cond_2 multicond_t_base_cond_0)))) 24 | (assert (= multicond_t_base_value_4 (+ multicond_t_base_value_3 20.0))) 25 | (assert (ite (> multicond_t_base_cond_2 0.0) (= multicond_t_base_value_5 multicond_t_base_value_4) (= multicond_t_base_value_5 multicond_t_base_value_3))) 26 | (assert (= multicond_t_base_cond_3 (+ multicond_t_base_cond_2 1.0))) 27 | (assert (ite (< multicond_t_base_cond_2 4.0) (and (= multicond_t_base_value_6 multicond_t_base_value_5) (= multicond_t_base_cond_4 multicond_t_base_cond_3)) (and (= multicond_t_base_value_6 multicond_t_base_value_3) (= multicond_t_base_cond_4 multicond_t_base_cond_2)))) 28 | (assert (= multicond_t_base_value_7 (+ multicond_t_base_value_6 20.0))) 29 | (assert (ite (> multicond_t_base_cond_4 0.0) (= multicond_t_base_value_8 multicond_t_base_value_7) (= multicond_t_base_value_8 multicond_t_base_value_6))) 30 | (assert (= multicond_t_base_cond_5 (+ multicond_t_base_cond_4 1.0))) 31 | (assert (ite (< multicond_t_base_cond_4 4.0) (and (= multicond_t_base_value_9 multicond_t_base_value_8) (= multicond_t_base_cond_6 multicond_t_base_cond_5)) (and (= multicond_t_base_value_9 multicond_t_base_value_6) (= multicond_t_base_cond_6 multicond_t_base_cond_4)))) -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond4.fspec: -------------------------------------------------------------------------------- 1 | spec multicond; 2 | 3 | def s = stock{ 4 | cond: 1, 5 | value: 10, 6 | }; 7 | 8 | def f = flow{ 9 | base: new s, 10 | change: func{ 11 | if base.cond > 0 { 12 | if base.cond < 4 { 13 | base.value <- 20; 14 | } 15 | base.cond <- 1; 16 | } 17 | }, 18 | }; 19 | 20 | for 2 init{t = new f;} run { 21 | t.change; 22 | }; -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond4.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun multicond_t_base_value_2 () Real) 3 | (declare-fun multicond_t_base_value_3 () Real) 4 | (declare-fun multicond_t_base_cond_2 () Real) 5 | (declare-fun multicond_t_base_value_5 () Real) 6 | (declare-fun multicond_t_base_value_6 () Real) 7 | (declare-fun multicond_t_base_cond_4 () Real) 8 | (declare-fun multicond_t_base_cond_0 () Real) 9 | (declare-fun multicond_t_base_value_0 () Real) 10 | (declare-fun multicond_t_base_value_1 () Real) 11 | (declare-fun multicond_t_base_cond_1 () Real) 12 | (declare-fun multicond_t_base_value_4 () Real) 13 | (declare-fun multicond_t_base_cond_3 () Real) 14 | (assert (= multicond_t_base_cond_0 1.0)) 15 | (assert (= multicond_t_base_value_0 10.0)) 16 | (assert (= multicond_t_base_value_1 (+ multicond_t_base_value_0 20.0))) 17 | (assert (ite (< multicond_t_base_cond_0 4.0) (= multicond_t_base_value_2 multicond_t_base_value_1) (= multicond_t_base_value_2 multicond_t_base_value_0))) 18 | (assert (= multicond_t_base_cond_1 (+ multicond_t_base_cond_0 1.0))) 19 | (assert (ite (> multicond_t_base_cond_0 0.0) (and (= multicond_t_base_value_3 multicond_t_base_value_2) (= multicond_t_base_cond_2 multicond_t_base_cond_1)) (and (= multicond_t_base_value_3 multicond_t_base_value_0) (= multicond_t_base_cond_2 multicond_t_base_cond_0)))) 20 | (assert (= multicond_t_base_value_4 (+ multicond_t_base_value_3 20.0))) 21 | (assert (ite (< multicond_t_base_cond_2 4.0) (= multicond_t_base_value_5 multicond_t_base_value_4) (= multicond_t_base_value_5 multicond_t_base_value_3))) 22 | (assert (= multicond_t_base_cond_3 (+ multicond_t_base_cond_2 1.0))) 23 | (assert (ite (> multicond_t_base_cond_2 0.0) (and (= multicond_t_base_value_6 multicond_t_base_value_5) (= multicond_t_base_cond_4 multicond_t_base_cond_3)) (and (= multicond_t_base_value_6 multicond_t_base_value_3) (= multicond_t_base_cond_4 multicond_t_base_cond_2)))) -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond5.fspec: -------------------------------------------------------------------------------- 1 | spec multicond; 2 | 3 | def s = stock{ 4 | cond: 1, 5 | value: 10, 6 | }; 7 | 8 | def f = flow{ 9 | base: new s, 10 | change: func{ 11 | if base.cond > 0 { 12 | if base.cond < 4 { 13 | base.value <- 20; 14 | if base.value > 100 { 15 | base.value = 0; 16 | } 17 | } 18 | base.cond <- 1; 19 | } 20 | }, 21 | }; 22 | 23 | for 2 init{t = new f;} run { 24 | t.change; 25 | }; -------------------------------------------------------------------------------- /smt/testdata/conditionals/multicond5.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun multicond_t_base_value_3 () Real) 3 | (declare-fun multicond_t_base_value_4 () Real) 4 | (declare-fun multicond_t_base_value_5 () Real) 5 | (declare-fun multicond_t_base_cond_2 () Real) 6 | (declare-fun multicond_t_base_value_8 () Real) 7 | (declare-fun multicond_t_base_value_9 () Real) 8 | (declare-fun multicond_t_base_value_10 () Real) 9 | (declare-fun multicond_t_base_cond_4 () Real) 10 | (declare-fun multicond_t_base_cond_0 () Real) 11 | (declare-fun multicond_t_base_value_0 () Real) 12 | (declare-fun multicond_t_base_value_1 () Real) 13 | (declare-fun multicond_t_base_value_2 () Real) 14 | (declare-fun multicond_t_base_cond_1 () Real) 15 | (declare-fun multicond_t_base_value_6 () Real) 16 | (declare-fun multicond_t_base_value_7 () Real) 17 | (declare-fun multicond_t_base_cond_3 () Real)(assert (= multicond_t_base_cond_0 1.0)) 18 | (assert (= multicond_t_base_value_0 10.0)) 19 | (assert (= multicond_t_base_value_1 (+ multicond_t_base_value_0 20.0))) 20 | (assert (= multicond_t_base_value_2 0.0)) 21 | (assert (ite (> multicond_t_base_value_1 100.0) (= multicond_t_base_value_3 multicond_t_base_value_2) (= multicond_t_base_value_3 multicond_t_base_value_1))) 22 | (assert (ite (< multicond_t_base_cond_0 4.0) (= multicond_t_base_value_4 multicond_t_base_value_3) (= multicond_t_base_value_4 multicond_t_base_value_0))) 23 | (assert (= multicond_t_base_cond_1 (+ multicond_t_base_cond_0 1.0))) 24 | (assert (ite (> multicond_t_base_cond_0 0.0) (and (= multicond_t_base_value_5 multicond_t_base_value_4) (= multicond_t_base_cond_2 multicond_t_base_cond_1)) (and (= multicond_t_base_value_5 multicond_t_base_value_0) (= multicond_t_base_cond_2 multicond_t_base_cond_0)))) 25 | (assert (= multicond_t_base_value_6 (+ multicond_t_base_value_5 20.0))) 26 | (assert (= multicond_t_base_value_7 0.0)) 27 | (assert (ite (> multicond_t_base_value_6 100.0) (= multicond_t_base_value_8 multicond_t_base_value_7) (= multicond_t_base_value_8 multicond_t_base_value_6))) 28 | (assert (ite (< multicond_t_base_cond_2 4.0) (= multicond_t_base_value_9 multicond_t_base_value_8) (= multicond_t_base_value_9 multicond_t_base_value_5))) 29 | (assert (= multicond_t_base_cond_3 (+ multicond_t_base_cond_2 1.0))) 30 | (assert (ite (> multicond_t_base_cond_2 0.0) (and (= multicond_t_base_value_10 multicond_t_base_value_9) (= multicond_t_base_cond_4 multicond_t_base_cond_3)) (and (= multicond_t_base_value_10 multicond_t_base_value_5) (= multicond_t_base_cond_4 multicond_t_base_cond_2)))) -------------------------------------------------------------------------------- /smt/testdata/imports/circle_import.smt2: -------------------------------------------------------------------------------- 1 | (set-logicQF_NRA)(declare-funcircle2_b_0()Bool)(declare-funcircle1_a_0()Bool)(assert(or(orcircle1_a_0circle2_b_0)(orcircle1_a_0circle2_b_0))) -------------------------------------------------------------------------------- /smt/testdata/imports/circle_import1.fspec: -------------------------------------------------------------------------------- 1 | spec circle1; 2 | 3 | import circle2 "circle_import2.fspec"; 4 | 5 | a = "hello"; 6 | 7 | assert a && circle2.b; -------------------------------------------------------------------------------- /smt/testdata/imports/circle_import2.fspec: -------------------------------------------------------------------------------- 1 | spec circle2; 2 | 3 | import circle1 "circle_import1.fspec"; 4 | 5 | b = "world"; 6 | 7 | assert circle1.a && b; -------------------------------------------------------------------------------- /smt/testdata/imports/renamed_import.fspec: -------------------------------------------------------------------------------- 1 | spec renamed; 2 | 3 | import (foo "../simple.fspec"); 4 | 5 | for 1 init{l = new foo.fl;} run { 6 | l.fn; 7 | } -------------------------------------------------------------------------------- /smt/testdata/imports/renamed_import.smt2: -------------------------------------------------------------------------------- 1 | (set-logicQF_NRA) 2 | (declare-funrenamed_l_vault_value_2()Real) 3 | (declare-funrenamed_l_active_0()Bool) 4 | (declare-funrenamed_l_vault_value_0()Real) 5 | (declare-funrenamed_l_vault_value_1()Real) 6 | (assert(=renamed_l_active_0false)) 7 | (assert(=renamed_l_vault_value_030.0)) 8 | (assert(=renamed_l_vault_value_1(+renamed_l_vault_value_0(-renamed_l_vault_value_02.0)))) 9 | (assert(ite(>renamed_l_vault_value_04.0)(=renamed_l_vault_value_2renamed_l_vault_value_1)(=renamed_l_vault_value_2renamed_l_vault_value_0))) -------------------------------------------------------------------------------- /smt/testdata/imports/single_import.fspec: -------------------------------------------------------------------------------- 1 | spec imports; 2 | 3 | import ("../simple.fspec"); 4 | 5 | def fl2 = flow{ 6 | vault: new simple.st, 7 | fn: func{ 8 | vault.value <- 10; 9 | }, 10 | }; 11 | 12 | for 2 init{fl3 = new fl2;} run { 13 | fl3.fn; 14 | } -------------------------------------------------------------------------------- /smt/testdata/imports/single_import.smt2: -------------------------------------------------------------------------------- 1 | (set-logicQF_NRA) 2 | (declare-funimports_fl3_vault_value_1()Real) 3 | (declare-funimports_fl3_vault_value_0()Real) 4 | (declare-funimports_fl3_vault_value_2()Real) 5 | (assert(=imports_fl3_vault_value_1(+imports_fl3_vault_value_010.0))) 6 | (assert(=imports_fl3_vault_value_030.0)) 7 | (assert(=imports_fl3_vault_value_2(+imports_fl3_vault_value_110.0))) -------------------------------------------------------------------------------- /smt/testdata/indexes.fspec: -------------------------------------------------------------------------------- 1 | spec indexes; 2 | 3 | def foo = stock{ 4 | a: 10, 5 | }; 6 | 7 | def bar = flow{ 8 | bash: new foo, 9 | fizz: func{ 10 | bash.a <- bash.a[0] - 2; 11 | }, 12 | }; 13 | 14 | for 2 init{ 15 | gee = new bar; 16 | }run{ 17 | gee.fizz; 18 | }; -------------------------------------------------------------------------------- /smt/testdata/indexes.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun indexes_gee_bash_a_0 () Real) 3 | (declare-fun indexes_gee_bash_a_1 ()Real) 4 | (declare-fun indexes_gee_bash_a_2 ()Real) 5 | (assert(= indexes_gee_bash_a_0 10.0)) 6 | (assert (= indexes_gee_bash_a_1 (+ indexes_gee_bash_a_0 (- indexes_gee_bash_a_0 2.0)))) 7 | (assert (= indexes_gee_bash_a_2 (+ indexes_gee_bash_a_1 (- indexes_gee_bash_a_0 2.0)))) 8 | -------------------------------------------------------------------------------- /smt/testdata/indexes_assert.smt2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fault-lang/Fault/b805587ca3a1422fd17cd6c758f30e7bc5946bb5/smt/testdata/indexes_assert.smt2 -------------------------------------------------------------------------------- /smt/testdata/indexes_branches.fspec: -------------------------------------------------------------------------------- 1 | spec fibonacci; 2 | 3 | def n = stock{ 4 | value: 0, 5 | }; 6 | 7 | def fib = flow{ 8 | num: new n, 9 | increment: func{ 10 | if num.value == 0 { 11 | num.value <- 1; 12 | }else{ 13 | num.value <- num.value[now-1]; 14 | } 15 | }, 16 | }; 17 | 18 | for 6 init{ 19 | f = new fib; 20 | }run{ 21 | f.increment; 22 | } -------------------------------------------------------------------------------- /smt/testdata/indexes_branches.smt2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fault-lang/Fault/b805587ca3a1422fd17cd6c758f30e7bc5946bb5/smt/testdata/indexes_branches.smt2 -------------------------------------------------------------------------------- /smt/testdata/multifile1.fspec: -------------------------------------------------------------------------------- 1 | spec multifile1; 2 | 3 | import ("multifile2.fspec"); 4 | 5 | def traffic = flow{ 6 | pool: new multifile2.pool, 7 | in: func{ 8 | if pool.vm1.memory < 75{ 9 | pool.vm1.memory <- 10; 10 | pool.vm1.cpu <- 5; 11 | }else if pool.vm2.memory < 75 { 12 | pool.vm2.memory <- 10; 13 | pool.vm2.cpu <- 5; 14 | }else{ 15 | pool.vm3.memory <- 10; 16 | pool.vm3.cpu <- 5; 17 | } 18 | }, 19 | release1: func{ 20 | pool.vm1.memory -> 10; 21 | pool.vm1.cpu -> 5; 22 | }, 23 | release2: func{ 24 | pool.vm2.memory -> 10; 25 | pool.vm2.cpu -> 5; 26 | }, 27 | release3: func{ 28 | pool.vm3.memory -> 10; 29 | pool.vm3.cpu -> 5; 30 | }, 31 | }; 32 | 33 | for 5 init{servers = new traffic;} run { 34 | servers.in | servers.release1 | servers.release2 | servers.release3; 35 | }; -------------------------------------------------------------------------------- /smt/testdata/multifile2.fspec: -------------------------------------------------------------------------------- 1 | spec multifile2; 2 | 3 | def vm = stock{ 4 | memory: 50, 5 | cpu: 40, 6 | }; 7 | 8 | def pool = stock{ 9 | vm1: new vm, 10 | vm2: new vm, 11 | vm3: new vm, 12 | }; 13 | 14 | assert vm.memory > 10; 15 | assert vm.cpu > 0; -------------------------------------------------------------------------------- /smt/testdata/simple.fspec: -------------------------------------------------------------------------------- 1 | spec simple; 2 | 3 | def st = stock{ 4 | value: 30, 5 | }; 6 | 7 | def fl = flow{ 8 | active: false, 9 | vault: new st, 10 | fn: func{ 11 | if vault.value > 4 { 12 | vault.value <- vault.value - 2; 13 | } 14 | }, 15 | }; 16 | 17 | for 1 init{l = new fl;} run { 18 | l.fn; 19 | } -------------------------------------------------------------------------------- /smt/testdata/simple.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun simple_l_active_0 () Bool) 3 | (declare-fun simple_l_vault_value_0 () Real) 4 | (declare-fun simple_l_vault_value_2 () Real) 5 | (declare-fun simple_l_vault_value_1 () Real) 6 | (assert 7 | (= simple_l_active_0 false)) 8 | (assert 9 | (= simple_l_vault_value_0 30.0)) 10 | (assert 11 | (= simple_l_vault_value_1 (+ simple_l_vault_value_0 (- simple_l_vault_value_0 2.0)))) 12 | (assert 13 | (ite (> simple_l_vault_value_0 4.0) 14 | (= simple_l_vault_value_2 simple_l_vault_value_1) 15 | (= simple_l_vault_value_2 simple_l_vault_value_0))) 16 | -------------------------------------------------------------------------------- /smt/testdata/statecharts/advanceand.fsystem: -------------------------------------------------------------------------------- 1 | system adand; 2 | 3 | component a = states{ 4 | choice: func{ 5 | advance(this.option1) && advance(this.option2) && advance(this.option3); 6 | }, 7 | option1: func{ 8 | stay(); 9 | }, 10 | option2: func{ 11 | stay(); 12 | }, 13 | option3: func{ 14 | stay(); 15 | }, 16 | }; 17 | 18 | start { 19 | a: choice, 20 | }; -------------------------------------------------------------------------------- /smt/testdata/statecharts/advanceand.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun adand_a_option1_1 () Bool) 3 | (declare-fun adand_a_choice_2 () Bool) 4 | (declare-fun adand_a_option2_1 () Bool) 5 | (declare-fun adand_a_choice_3 () Bool) 6 | (declare-fun adand_a_option3_1 () Bool) 7 | (declare-fun adand_a_choice_4 () Bool) 8 | (declare-fun adand_a_option1_2 () Bool) 9 | (declare-fun adand_a_choice_5 () Bool) 10 | (declare-fun adand_a_option2_2 () Bool) 11 | (declare-fun adand_a_option3_2 () Bool) 12 | (declare-fun adand_a_choice_0 () Bool) 13 | (declare-fun adand_a_option1_0 () Bool) 14 | (declare-fun adand_a_option2_0 () Bool) 15 | (declare-fun adand_a_option3_0 () Bool) 16 | (declare-fun adand_a_choice_1 () Bool)(assert (= adand_a_choice_0 false)) 17 | (assert (= adand_a_option1_0 false)) 18 | (assert (= adand_a_option2_0 false)) 19 | (assert (= adand_a_option3_0 false)) 20 | (assert (= adand_a_choice_1 true)) 21 | (assert (and (= adand_a_option1_1 true)(= adand_a_choice_2 false)(= adand_a_option2_1 true)(= adand_a_choice_3 false)(= adand_a_option3_1 true)(= adand_a_choice_4 false))) 22 | (assert (ite (= adand_a_choice_1 true) (and (= adand_a_option1_2 adand_a_option1_1) (= adand_a_choice_5 adand_a_choice_4) (= adand_a_option2_2 adand_a_option2_1) (= adand_a_option3_2 adand_a_option3_1)) (and (= adand_a_option1_2 adand_a_option1_0) (= adand_a_choice_5 adand_a_choice_1) (= adand_a_option2_2 adand_a_option2_0) (= adand_a_option3_2 adand_a_option3_0)))) -------------------------------------------------------------------------------- /smt/testdata/statecharts/advanceor.fsystem: -------------------------------------------------------------------------------- 1 | system ador; 2 | 3 | component a = states{ 4 | choice: func{ 5 | advance(this.option1) || advance(this.option2); 6 | }, 7 | option1: func{ 8 | stay(); 9 | }, 10 | option2: func{ 11 | stay(); 12 | }, 13 | }; 14 | 15 | start { 16 | a: choice, 17 | }; -------------------------------------------------------------------------------- /smt/testdata/statecharts/advanceor.smt2: -------------------------------------------------------------------------------- 1 | 2 | (set-logic QF_NRA) 3 | (declare-fun ador_a_option1_1 () Bool) 4 | (declare-fun ador_a_choice_2 () Bool) 5 | (declare-fun ador_a_option2_1 () Bool) 6 | (declare-fun ador_a_choice_3 () Bool) 7 | (declare-fun ador_a_option1_2 () Bool) 8 | (declare-fun ador_a_choice_4 () Bool) 9 | (declare-fun ador_a_option2_2 () Bool) 10 | (declare-fun ador_a_option1_3 () Bool) 11 | (declare-fun ador_a_choice_5 () Bool) 12 | (declare-fun ador_a_option2_3 () Bool) 13 | (declare-fun ador_a_choice_0 () Bool) 14 | (declare-fun ador_a_option1_0 () Bool) 15 | (declare-fun ador_a_option2_0 () Bool) 16 | (declare-fun ador_a_choice_1 () Bool)(assert (= ador_a_choice_0 false)) 17 | (assert (= ador_a_option1_0 false)) 18 | (assert (= ador_a_option2_0 false)) 19 | (assert (= ador_a_choice_1 true)) 20 | (assert (or 21 | (and (= ador_a_option1_1 true)(= ador_a_choice_2 false)(= ador_a_option1_2 ador_a_option1_1)(= ador_a_choice_4 ador_a_choice_2)(= ador_a_option2_2 ador_a_option2_0)) 22 | (and (= ador_a_option2_1 true)(= ador_a_choice_3 false)(= ador_a_choice_4 ador_a_choice_3)(= ador_a_option2_2 ador_a_option2_1)(= ador_a_option1_2 ador_a_option1_0)))) 23 | (assert (ite (= ador_a_choice_1 true) 24 | (and (= ador_a_option1_3 ador_a_option1_2) (= ador_a_choice_5 ador_a_choice_4) (= ador_a_option2_3 ador_a_option2_2)) 25 | (and (= ador_a_option1_3 ador_a_option1_0) (= ador_a_choice_5 ador_a_choice_1) (= ador_a_option2_3 ador_a_option2_0)))) 26 | -------------------------------------------------------------------------------- /smt/testdata/statecharts/mixedcalls.fsystem: -------------------------------------------------------------------------------- 1 | system mixed; 2 | 3 | import "../simple.fspec"; 4 | 5 | global fl = new simple.fl; 6 | 7 | component a = states{ 8 | choice: func{ 9 | advance(this.option1) || advance(this.option2) && advance(this.option3); 10 | }, 11 | option1: func{ 12 | advance(this.option1) && advance(this.option2) || advance(this.option3); 13 | }, 14 | option2: func{ 15 | if fl.active { 16 | advance(this.option1) || advance(this.option3); 17 | } 18 | }, 19 | option3: func{ 20 | if !fl.active { 21 | advance(this.option1) || advance(this.option2); 22 | }else{ 23 | advance(this.option1) && advance(this.option2) || advance(this.option3); 24 | } 25 | }, 26 | }; 27 | 28 | start { 29 | a: choice, 30 | }; -------------------------------------------------------------------------------- /smt/testdata/statecharts/mixedcalls.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun mixed_a_option2_1 () Bool) 3 | (declare-fun mixed_a_choice_2 () Bool) 4 | (declare-fun mixed_a_option3_1 () Bool) 5 | (declare-fun mixed_a_choice_3 () Bool) 6 | (declare-fun mixed_a_option1_1 () Bool) 7 | (declare-fun mixed_a_choice_4 () Bool) 8 | (declare-fun mixed_a_option1_2 () Bool) 9 | (declare-fun mixed_a_choice_5 () Bool) 10 | (declare-fun mixed_a_option3_2 () Bool) 11 | (declare-fun mixed_a_option2_2 () Bool) 12 | (declare-fun mixed_a_option2_3 () Bool) 13 | (declare-fun mixed_a_choice_6 () Bool) 14 | (declare-fun mixed_a_option3_3 () Bool) 15 | (declare-fun mixed_a_option1_3 () Bool) 16 | (declare-fun mixed_a_option1_4 () Bool) 17 | (declare-fun mixed_a_option1_5 () Bool) 18 | (declare-fun mixed_a_option2_4 () Bool) 19 | (declare-fun mixed_a_option1_6 () Bool) 20 | (declare-fun mixed_a_option3_4 () Bool) 21 | (declare-fun mixed_a_option1_7 () Bool) 22 | (declare-fun mixed_a_option3_5 () Bool) 23 | (declare-fun mixed_a_option1_8 () Bool) 24 | (declare-fun mixed_a_option2_5 () Bool) 25 | (declare-fun mixed_a_option1_9 () Bool) 26 | (declare-fun mixed_a_option2_6 () Bool) 27 | (declare-fun mixed_a_option3_6 () Bool) 28 | (declare-fun mixed_a_option1_10 () Bool) 29 | (declare-fun mixed_a_option2_7 () Bool) 30 | (declare-fun mixed_a_option3_7 () Bool) 31 | (declare-fun mixed_a_option2_8 () Bool) 32 | (declare-fun mixed_a_option1_11 () Bool) 33 | (declare-fun mixed_a_option2_9 () Bool) 34 | (declare-fun mixed_a_option3_8 () Bool) 35 | (declare-fun mixed_a_option3_9 () Bool) 36 | (declare-fun mixed_a_option2_10 () Bool) 37 | (declare-fun mixed_a_option1_12 () Bool) 38 | (declare-fun mixed_a_option1_13 () Bool) 39 | (declare-fun mixed_a_option3_10 () Bool) 40 | (declare-fun mixed_a_option2_11 () Bool) 41 | (declare-fun mixed_a_option3_11 () Bool) 42 | (declare-fun mixed_a_option3_12 () Bool) 43 | (declare-fun mixed_a_option3_13 () Bool) 44 | (declare-fun mixed_a_option3_14 () Bool) 45 | (declare-fun mixed_a_option1_14 () Bool) 46 | (declare-fun mixed_a_option2_12 () Bool) 47 | (declare-fun mixed_a_option1_15 () Bool) 48 | (declare-fun mixed_a_option3_15 () Bool) 49 | (declare-fun mixed_a_option2_13 () Bool) 50 | (declare-fun mixed_a_option1_16 () Bool) 51 | (declare-fun mixed_a_option3_16 () Bool) 52 | (declare-fun mixed_a_option2_14 () Bool) 53 | (declare-fun mixed_a_option3_17 () Bool) 54 | (declare-fun mixed_a_option1_17 () Bool) 55 | (declare-fun mixed_a_option3_18 () Bool) 56 | (declare-fun mixed_a_option2_15 () Bool) 57 | (declare-fun mixed_a_option2_16 () Bool) 58 | (declare-fun mixed_a_option3_19 () Bool) 59 | (declare-fun mixed_a_option1_18 () Bool) 60 | (declare-fun mixed_fl_active_0 () Bool) 61 | (declare-fun mixed_fl_vault_value_0 () Real) 62 | (declare-fun mixed_a_choice_0 () Bool) 63 | (declare-fun mixed_a_option1_0 () Bool) 64 | (declare-fun mixed_a_option2_0 () Bool) 65 | (declare-fun mixed_a_option3_0 () Bool) 66 | (declare-fun mixed_a_choice_1 () Bool)(assert (= mixed_fl_active_0 false)) 67 | (assert (= mixed_fl_vault_value_0 30.0)) 68 | (assert (= mixed_a_choice_0 false)) 69 | (assert (= mixed_a_option1_0 false)) 70 | (assert (= mixed_a_option2_0 false)) 71 | (assert (= mixed_a_option3_0 false)) 72 | (assert (= mixed_a_choice_1 true)) 73 | (assert (or (and (= mixed_a_option2_1 true)(= mixed_a_choice_2 false)(= mixed_a_option3_1 true)(= mixed_a_choice_3 false)(= mixed_a_option3_2 mixed_a_option3_1)(= mixed_a_choice_5 mixed_a_choice_3)(= mixed_a_option2_2 mixed_a_option2_1)(= mixed_a_option1_2 mixed_a_option1_0))(and (= mixed_a_option1_1 true)(= mixed_a_choice_4 false)(= mixed_a_option1_2 mixed_a_option1_1)(= mixed_a_choice_5 mixed_a_choice_4)(= mixed_a_option2_2 mixed_a_option2_0)(= mixed_a_option3_2 mixed_a_option3_0)))) 74 | (assert (ite (= mixed_a_choice_1 true) (and (= mixed_a_option2_3 mixed_a_option2_2) (= mixed_a_choice_6 mixed_a_choice_5) (= mixed_a_option3_3 mixed_a_option3_2) (= mixed_a_option1_3 mixed_a_option1_2)) (and (= mixed_a_option2_3 mixed_a_option2_0) (= mixed_a_choice_6 mixed_a_choice_1) (= mixed_a_option3_3 mixed_a_option3_0) (= mixed_a_option1_3 mixed_a_option1_0)))) 75 | (assert (or (and (= mixed_a_option1_4 true)(= mixed_a_option1_5 false)(= mixed_a_option2_4 true)(= mixed_a_option1_6 false)(= mixed_a_option1_8 mixed_a_option1_6)(= mixed_a_option2_5 mixed_a_option2_4)(= mixed_a_option3_5 mixed_a_option3_3))(and (= mixed_a_option3_4 true)(= mixed_a_option1_7 false)(= mixed_a_option3_5 mixed_a_option3_4)(= mixed_a_option1_8 mixed_a_option1_7)(= mixed_a_option2_5 mixed_a_option2_3)))) 76 | (assert (ite (= mixed_a_option1_3 true) (and (= mixed_a_option1_9 mixed_a_option1_8) (= mixed_a_option2_6 mixed_a_option2_5) (= mixed_a_option3_6 mixed_a_option3_5)) (and (= mixed_a_option1_9 mixed_a_option1_3) (= mixed_a_option2_6 mixed_a_option2_3) (= mixed_a_option3_6 mixed_a_option3_3)))) 77 | (assert (or (and (= mixed_a_option3_7 true)(= mixed_a_option2_8 false)(= mixed_a_option3_8 mixed_a_option3_7)(= mixed_a_option2_9 mixed_a_option2_8)(= mixed_a_option1_11 mixed_a_option1_9))(and (= mixed_a_option1_10 true)(= mixed_a_option2_7 false)(= mixed_a_option1_11 mixed_a_option1_10)(= mixed_a_option2_9 mixed_a_option2_7)(= mixed_a_option3_8 mixed_a_option3_6)))) 78 | (assert (ite (and (= mixed_a_option2_6 true) (= mixed_fl_active_0 true)) (and (= mixed_a_option3_9 mixed_a_option3_8) (= mixed_a_option2_10 mixed_a_option2_9) (= mixed_a_option1_12 mixed_a_option1_11)) (and (= mixed_a_option3_9 mixed_a_option3_6) (= mixed_a_option2_10 mixed_a_option2_6) (= mixed_a_option1_12 mixed_a_option1_9)))) 79 | (assert (or (and (= mixed_a_option1_13 true)(= mixed_a_option3_10 false)(= mixed_a_option2_11 true)(= mixed_a_option3_11 false)(= mixed_a_option1_14 mixed_a_option1_13)(= mixed_a_option2_12 mixed_a_option2_11)(= mixed_a_option3_14 mixed_a_option3_11))(and (= mixed_a_option3_12 true)(= mixed_a_option3_13 false)(= mixed_a_option3_14 mixed_a_option3_13)(= mixed_a_option2_12 mixed_a_option2_10)(= mixed_a_option1_14 mixed_a_option1_12)))) 80 | (assert (ite (= mixed_a_option3_9 true) (and (= mixed_a_option1_15 mixed_a_option1_14) (= mixed_a_option3_15 mixed_a_option3_14) (= mixed_a_option2_13 mixed_a_option2_12)) (and (= mixed_a_option3_15 mixed_a_option3_9) (= mixed_a_option2_13 mixed_a_option2_10) (= mixed_a_option1_15 mixed_a_option1_12)))) 81 | (assert (or (and (= mixed_a_option2_14 true)(= mixed_a_option3_17 false)(= mixed_a_option3_18 mixed_a_option3_17)(= mixed_a_option2_15 mixed_a_option2_14)(= mixed_a_option1_17 mixed_a_option1_15))(and (= mixed_a_option1_16 true)(= mixed_a_option3_16 false)(= mixed_a_option1_17 mixed_a_option1_16)(= mixed_a_option3_18 mixed_a_option3_16)(= mixed_a_option2_15 mixed_a_option2_13)))) 82 | (assert (ite (and (= mixed_a_option3_15 true) (not mixed_fl_active_0)) (and (= mixed_a_option2_16 mixed_a_option2_15) (= mixed_a_option3_19 mixed_a_option3_18) (= mixed_a_option1_18 mixed_a_option1_17)) (and (= mixed_a_option2_16 mixed_a_option2_13) (= mixed_a_option3_19 mixed_a_option3_15) (= mixed_a_option1_18 mixed_a_option1_15)))) -------------------------------------------------------------------------------- /smt/testdata/statecharts/multioradvance.fsystem: -------------------------------------------------------------------------------- 1 | system ador; 2 | 3 | component a = states{ 4 | choice: func{ 5 | advance(this.option1) || advance(this.option2) || advance(this.option3); 6 | }, 7 | option1: func{ 8 | advance(this.choice) || advance(this.option2) || advance(this.option3) || advance(this.option4); 9 | }, 10 | option2: func{ 11 | stay(); 12 | }, 13 | option3: func{ 14 | advance(this.option2); 15 | }, 16 | option4: func{ 17 | stay(); 18 | }, 19 | }; 20 | 21 | start { 22 | a: choice, 23 | }; -------------------------------------------------------------------------------- /smt/testdata/statecharts/multioradvance.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun ador_a_option1_1 () Bool) 3 | (declare-fun ador_a_choice_2 () Bool) 4 | (declare-fun ador_a_option2_1 () Bool) 5 | (declare-fun ador_a_choice_3 () Bool) 6 | (declare-fun ador_a_option3_1 () Bool) 7 | (declare-fun ador_a_choice_4 () Bool) 8 | (declare-fun ador_a_option1_2 () Bool) 9 | (declare-fun ador_a_choice_5 () Bool) 10 | (declare-fun ador_a_option2_2 () Bool) 11 | (declare-fun ador_a_option3_2 () Bool) 12 | (declare-fun ador_a_option2_3 () Bool) 13 | (declare-fun ador_a_choice_6 () Bool) 14 | (declare-fun ador_a_option1_3 () Bool) 15 | (declare-fun ador_a_option3_3 () Bool) 16 | (declare-fun ador_a_choice_7 () Bool) 17 | (declare-fun ador_a_option1_4 () Bool) 18 | (declare-fun ador_a_option2_4 () Bool) 19 | (declare-fun ador_a_option1_5 () Bool) 20 | (declare-fun ador_a_option3_4 () Bool) 21 | (declare-fun ador_a_option1_6 () Bool) 22 | (declare-fun ador_a_option4_1 () Bool) 23 | (declare-fun ador_a_option1_7 () Bool) 24 | (declare-fun ador_a_option4_2 () Bool) 25 | (declare-fun ador_a_option1_8 () Bool) 26 | (declare-fun ador_a_choice_8 () Bool) 27 | (declare-fun ador_a_option2_5 () Bool) 28 | (declare-fun ador_a_option3_5 () Bool) 29 | (declare-fun ador_a_option4_3 () Bool) 30 | (declare-fun ador_a_choice_9 () Bool) 31 | (declare-fun ador_a_option3_6 () Bool) 32 | (declare-fun ador_a_option2_6 () Bool) 33 | (declare-fun ador_a_option1_9 () Bool) 34 | (declare-fun ador_a_option2_8 () Bool) 35 | (declare-fun ador_a_option3_8 () Bool) 36 | (declare-fun ador_a_choice_0 () Bool) 37 | (declare-fun ador_a_option1_0 () Bool) 38 | (declare-fun ador_a_option2_0 () Bool) 39 | (declare-fun ador_a_option3_0 () Bool) 40 | (declare-fun ador_a_option4_0 () Bool) 41 | (declare-fun ador_a_choice_1 () Bool) 42 | (declare-fun ador_a_option2_7 () Bool) 43 | (declare-fun ador_a_option3_7 () Bool) 44 | (assert (= ador_a_choice_0 false)) 45 | (assert (= ador_a_option1_0 false)) 46 | (assert (= ador_a_option2_0 false)) 47 | (assert (= ador_a_option3_0 false)) 48 | (assert (= ador_a_option4_0 false)) 49 | (assert (= ador_a_choice_1 true)) 50 | (assert (or 51 | (and (= ador_a_option2_1 true)(= ador_a_choice_3 false)(= ador_a_choice_5 ador_a_choice_3)(= ador_a_option2_2 ador_a_option2_1)(= ador_a_option1_2 ador_a_option1_0)(= ador_a_option3_2 ador_a_option3_0)) 52 | (and (= ador_a_option3_1 true)(= ador_a_choice_4 false)(= ador_a_choice_5 ador_a_choice_4)(= ador_a_option3_2 ador_a_option3_1)(= ador_a_option1_2 ador_a_option1_0)(= ador_a_option2_2 ador_a_option2_0)) 53 | (and (= ador_a_option1_1 true)(= ador_a_choice_2 false)(= ador_a_option1_2 ador_a_option1_1)(= ador_a_choice_5 ador_a_choice_2)(= ador_a_option2_2 ador_a_option2_0)(= ador_a_option3_2 ador_a_option3_0)))) 54 | (assert (ite (= ador_a_choice_1 true) (and (= ador_a_option2_3 ador_a_option2_2) (= ador_a_choice_6 ador_a_choice_5) (= ador_a_option1_3 ador_a_option1_2) (= ador_a_option3_3 ador_a_option3_2)) (and (= ador_a_option2_3 ador_a_option2_0) (= ador_a_choice_6 ador_a_choice_1) (= ador_a_option1_3 ador_a_option1_0) (= ador_a_option3_3 ador_a_option3_0)))) 55 | (assert (or 56 | (and (= ador_a_option2_4 true)(= ador_a_option1_5 false)(= ador_a_option1_8 ador_a_option1_5)(= ador_a_option2_5 ador_a_option2_4)(= ador_a_option4_2 ador_a_option4_0)(= ador_a_choice_8 ador_a_choice_6)(= ador_a_option3_5 ador_a_option3_3)) 57 | (and (= ador_a_option3_4 true)(= ador_a_option1_6 false)(= ador_a_option1_8 ador_a_option1_6)(= ador_a_option3_5 ador_a_option3_4)(= ador_a_option4_2 ador_a_option4_0)(= ador_a_choice_8 ador_a_choice_6)(= ador_a_option2_5 ador_a_option2_3)) 58 | (and (= ador_a_option4_1 true)(= ador_a_option1_7 false)(= ador_a_option4_2 ador_a_option4_1)(= ador_a_option1_8 ador_a_option1_7)(= ador_a_choice_8 ador_a_choice_6)(= ador_a_option2_5 ador_a_option2_3)(= ador_a_option3_5 ador_a_option3_3)) 59 | (and (= ador_a_choice_7 true)(= ador_a_option1_4 false)(= ador_a_option1_8 ador_a_option1_4)(= ador_a_choice_8 ador_a_choice_7)(= ador_a_option4_2 ador_a_option4_0)(= ador_a_option2_5 ador_a_option2_3)(= ador_a_option3_5 ador_a_option3_3)))) 60 | (assert (ite (= ador_a_option1_3 true) (and (= ador_a_option4_3 ador_a_option4_2) (= ador_a_choice_9 ador_a_choice_8) (= ador_a_option3_6 ador_a_option3_5) (= ador_a_option2_6 ador_a_option2_5) (= ador_a_option1_9 ador_a_option1_8)) (and (= ador_a_option1_9 ador_a_option1_3) (= ador_a_option4_3 ador_a_option4_0) (= ador_a_choice_9 ador_a_choice_6) (= ador_a_option3_6 ador_a_option3_3) (= ador_a_option2_6 ador_a_option2_3)))) 61 | (assert (= ador_a_option2_7 true)) 62 | (assert (= ador_a_option3_7 false)) 63 | (assert (ite (= ador_a_option3_6 true) (and (= ador_a_option2_8 ador_a_option2_7) (= ador_a_option3_8 ador_a_option3_7)) (and (= ador_a_option2_8 ador_a_option2_6) (= ador_a_option3_8 ador_a_option3_6)))) -------------------------------------------------------------------------------- /smt/testdata/statecharts/statechart.fsystem: -------------------------------------------------------------------------------- 1 | system statechart; 2 | 3 | import "../simple.fspec"; 4 | 5 | global fl = new simple.fl; 6 | 7 | component drain = states{ 8 | initial: func{ 9 | if !fl.active { 10 | advance(this.open); 11 | } 12 | }, 13 | open: func{ 14 | if fl.vault.value < 0 { 15 | advance(this.close); 16 | } 17 | }, 18 | close: func{ 19 | stay(); 20 | }, 21 | }; 22 | 23 | start { 24 | drain: initial, 25 | }; 26 | 27 | for 2 run { 28 | if !drain.close{ 29 | fl.fn; 30 | } 31 | } -------------------------------------------------------------------------------- /smt/testdata/statecharts/statechart.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun statechart_fl_vault_value_2 () Real) 3 | (declare-fun statechart_fl_vault_value_3 () Real) 4 | (declare-fun statechart_drain_open_2 () Bool) 5 | (declare-fun statechart_drain_initial_3 () Bool) 6 | (declare-fun statechart_drain_close_2 () Bool) 7 | (declare-fun statechart_drain_open_4 () Bool) 8 | (declare-fun statechart_fl_vault_value_5 () Real) 9 | (declare-fun statechart_fl_vault_value_6 () Real) 10 | (declare-fun statechart_drain_open_6 () Bool) 11 | (declare-fun statechart_drain_initial_5 () Bool) 12 | (declare-fun statechart_drain_close_4 () Bool) 13 | (declare-fun statechart_drain_open_8 () Bool) 14 | (declare-fun statechart_fl_active_0 () Bool) 15 | (declare-fun statechart_fl_vault_value_0 () Real) 16 | (declare-fun statechart_drain_initial_0 () Bool) 17 | (declare-fun statechart_drain_open_0 () Bool) 18 | (declare-fun statechart_drain_close_0 () Bool) 19 | (declare-fun statechart_drain_initial_1 () Bool) 20 | (declare-fun statechart_fl_vault_value_1 () Real) 21 | (declare-fun statechart_drain_open_1 () Bool) 22 | (declare-fun statechart_drain_initial_2 () Bool) 23 | (declare-fun statechart_drain_close_1 () Bool) 24 | (declare-fun statechart_drain_open_3 () Bool) 25 | (declare-fun statechart_fl_vault_value_4 () Real) 26 | (declare-fun statechart_drain_open_5 () Bool) 27 | (declare-fun statechart_drain_initial_4 () Bool) 28 | (declare-fun statechart_drain_close_3 () Bool) 29 | (declare-fun statechart_drain_open_7 () Bool)(assert (= statechart_fl_active_0 false)) 30 | (assert (= statechart_fl_vault_value_0 30.0)) 31 | (assert (= statechart_drain_initial_0 false)) 32 | (assert (= statechart_drain_open_0 false)) 33 | (assert (= statechart_drain_close_0 false)) 34 | (assert (= statechart_drain_initial_1 true)) 35 | (assert (= statechart_fl_vault_value_1 (+ statechart_fl_vault_value_0 (- statechart_fl_vault_value_0 2.0)))) 36 | (assert (ite (> statechart_fl_vault_value_0 4.0) (= statechart_fl_vault_value_2 statechart_fl_vault_value_1) (= statechart_fl_vault_value_2 statechart_fl_vault_value_0))) 37 | (assert (ite (not statechart_drain_close_0) (= statechart_fl_vault_value_3 statechart_fl_vault_value_2) (= statechart_fl_vault_value_3 statechart_fl_vault_value_0))) 38 | (assert (= statechart_drain_open_1 true)) 39 | (assert (= statechart_drain_initial_2 false)) 40 | (assert (ite (and (= statechart_drain_initial_1 true) (not statechart_fl_active_0)) (and (= statechart_drain_open_2 statechart_drain_open_1) (= statechart_drain_initial_3 statechart_drain_initial_2)) (and (= statechart_drain_initial_3 statechart_drain_initial_1) (= statechart_drain_open_2 statechart_drain_open_0)))) 41 | (assert (= statechart_drain_close_1 true)) 42 | (assert (= statechart_drain_open_3 false)) 43 | (assert (ite (and (= statechart_drain_open_2 true) (< statechart_fl_vault_value_3 0.0)) (and (= statechart_drain_close_2 statechart_drain_close_1) (= statechart_drain_open_4 statechart_drain_open_3)) (and (= statechart_drain_close_2 statechart_drain_close_0) (= statechart_drain_open_4 statechart_drain_open_2)))) 44 | (assert (= statechart_fl_vault_value_4 (+ statechart_fl_vault_value_3 (- statechart_fl_vault_value_3 2.0)))) 45 | (assert (ite (> statechart_fl_vault_value_3 4.0) (= statechart_fl_vault_value_5 statechart_fl_vault_value_4) (= statechart_fl_vault_value_5 statechart_fl_vault_value_3))) 46 | (assert (ite (not statechart_drain_close_2) (= statechart_fl_vault_value_6 statechart_fl_vault_value_5) (= statechart_fl_vault_value_6 statechart_fl_vault_value_3))) 47 | (assert (= statechart_drain_open_5 true)) 48 | (assert (= statechart_drain_initial_4 false)) 49 | (assert (ite (and (= statechart_drain_initial_3 true) (not statechart_fl_active_0)) (and (= statechart_drain_open_6 statechart_drain_open_5) (= statechart_drain_initial_5 statechart_drain_initial_4)) (and (= statechart_drain_initial_5 statechart_drain_initial_3) (= statechart_drain_open_6 statechart_drain_open_4)))) 50 | (assert (= statechart_drain_close_3 true)) 51 | (assert (= statechart_drain_open_7 false)) 52 | (assert (ite (and (= statechart_drain_open_6 true) (< statechart_fl_vault_value_6 0.0)) (and (= statechart_drain_close_4 statechart_drain_close_3) (= statechart_drain_open_8 statechart_drain_open_7)) (and (= statechart_drain_close_4 statechart_drain_close_2) (= statechart_drain_open_8 statechart_drain_open_6)))) -------------------------------------------------------------------------------- /smt/testdata/statecharts/triggerfunc.fsystem: -------------------------------------------------------------------------------- 1 | system trigger; 2 | 3 | import "../simple.fspec"; 4 | 5 | global fl = new simple.fl; 6 | 7 | component x = states{ 8 | foo: func{ 9 | fl.fn; 10 | }, 11 | bar: func{ 12 | if fl.active { 13 | fl.fn; 14 | } 15 | }, 16 | bash: func{ 17 | if fl.active { 18 | advance(this.foo); 19 | }else{ 20 | fl.fn; 21 | } 22 | }, 23 | }; 24 | 25 | start { 26 | x: foo, 27 | }; -------------------------------------------------------------------------------- /smt/testdata/statecharts/triggerfunc.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun trigger_fl_vault_value_2 () Real) 3 | (declare-fun trigger_fl_vault_value_3 () Real) 4 | (declare-fun trigger_fl_vault_value_5 () Real) 5 | (declare-fun trigger_fl_vault_value_6 () Real) 6 | (declare-fun trigger_fl_vault_value_8 () Real) 7 | (declare-fun trigger_fl_vault_value_9 () Real) 8 | (declare-fun trigger_x_foo_3 () Bool) 9 | (declare-fun trigger_x_bash_2 () Bool) 10 | (declare-fun trigger_fl_active_0 () Bool) 11 | (declare-fun trigger_fl_vault_value_0 () Real) 12 | (declare-fun trigger_x_foo_0 () Bool) 13 | (declare-fun trigger_x_bar_0 () Bool) 14 | (declare-fun trigger_x_bash_0 () Bool) 15 | (declare-fun trigger_x_foo_1 () Bool) 16 | (declare-fun trigger_fl_vault_value_1 () Real) 17 | (declare-fun trigger_fl_vault_value_4 () Real) 18 | (declare-fun trigger_fl_vault_value_7 () Real) 19 | (declare-fun trigger_x_foo_2 () Bool) 20 | (declare-fun trigger_x_bash_1 () Bool) 21 | (assert (= trigger_fl_active_0 false)) 22 | (assert (= trigger_fl_vault_value_0 30.0)) 23 | (assert (= trigger_x_foo_0 false)) 24 | (assert (= trigger_x_bar_0 false)) 25 | (assert (= trigger_x_bash_0 false)) 26 | (assert (= trigger_x_foo_1 true)) 27 | (assert (= trigger_fl_vault_value_1 (+ trigger_fl_vault_value_0 (- trigger_fl_vault_value_0 2.0)))) 28 | (assert (ite (> trigger_fl_vault_value_0 4.0) (= trigger_fl_vault_value_2 trigger_fl_vault_value_1) (= trigger_fl_vault_value_2 trigger_fl_vault_value_0))) 29 | (assert (ite (= trigger_x_foo_1 true) (= trigger_fl_vault_value_3 trigger_fl_vault_value_2) (= trigger_fl_vault_value_3 trigger_fl_vault_value_0))) 30 | (assert (= trigger_fl_vault_value_4 (+ trigger_fl_vault_value_3 (- trigger_fl_vault_value_3 2.0)))) 31 | (assert (ite (> trigger_fl_vault_value_3 4.0) (= trigger_fl_vault_value_5 trigger_fl_vault_value_4) (= trigger_fl_vault_value_5 trigger_fl_vault_value_3))) 32 | (assert (ite (and (= trigger_x_bar_0 true) (= trigger_fl_active_0 true)) (= trigger_fl_vault_value_6 trigger_fl_vault_value_5) (= trigger_fl_vault_value_6 trigger_fl_vault_value_3))) 33 | (assert (= trigger_fl_vault_value_7 (+ trigger_fl_vault_value_6 (- trigger_fl_vault_value_6 2.0)))) 34 | (assert (ite (> trigger_fl_vault_value_6 4.0) (= trigger_fl_vault_value_8 trigger_fl_vault_value_7) (= trigger_fl_vault_value_8 trigger_fl_vault_value_6))) 35 | (assert (ite (= trigger_x_bash_0 true) (= trigger_fl_vault_value_9 trigger_fl_vault_value_8) (= trigger_fl_vault_value_9 trigger_fl_vault_value_6))) 36 | (assert (= trigger_x_foo_2 true)) 37 | (assert (= trigger_x_bash_1 false)) 38 | (assert (ite (and (= trigger_x_bash_0 true) (= trigger_fl_active_0 true)) (and (= trigger_x_foo_3 trigger_x_foo_2) (= trigger_x_bash_2 trigger_x_bash_1)) (and (= trigger_x_foo_3 trigger_x_foo_1) (= trigger_x_bash_2 trigger_x_bash_0)))) -------------------------------------------------------------------------------- /smt/testdata/strings.fspec: -------------------------------------------------------------------------------- 1 | spec test; 2 | str1 = "is a fish"; 3 | str2 = "tastes delicious with ginger"; 4 | str3 = "native to North America"; 5 | str4 = !str1 && str2; 6 | 7 | assume (str1 && str3) || str4; 8 | assert str3; -------------------------------------------------------------------------------- /smt/testdata/strings.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun test_str1_0 () Bool) 3 | (declare-fun test_str2_0 () Bool) 4 | (declare-fun test_str3_0 () Bool) 5 | (declare-fun test_str1_neg_0 () Bool) 6 | (declare-fun test_str4_0 () Bool) 7 | (assert (= test_str1_neg_0 (not test_str1_0))) 8 | (assert (= test_str4_0 (and test_str2_0 test_str1_neg_0))) 9 | (assert (not test_str3_0)) 10 | (assert (or (and test_str1_0 test_str3_0) test_str4_0)) -------------------------------------------------------------------------------- /smt/testdata/strings2.fspec: -------------------------------------------------------------------------------- 1 | spec test; 2 | str1 = "is a fish"; 3 | str2 = "tastes delicious with ginger"; 4 | str3 = "native to North America"; 5 | str4 = "walks on four legs"; 6 | str5 = "has a tail"; 7 | str6 = "is blue"; 8 | str7 = (str1 && str2) || (str3 && str4); 9 | str8 = str6 || str5 && str1; 10 | -------------------------------------------------------------------------------- /smt/testdata/strings2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun test_str1_0 () Bool) 3 | (declare-fun test_str2_0 () Bool) 4 | (declare-fun test_str3_0 () Bool) 5 | (declare-fun test_str4_0 () Bool) 6 | (declare-fun test_str5_0 () Bool) 7 | (declare-fun test_str6_0 () Bool) 8 | (declare-fun test_str3_test_str4_0 () Bool) 9 | (declare-fun test_str1_test_str2_0 () Bool) 10 | (declare-fun test_str7_0 () Bool) 11 | (declare-fun test_str5_test_str1_0 () Bool) 12 | (declare-fun test_str8_0 () Bool) 13 | (assert (= test_str3_test_str4_0 (and test_str3_0 test_str4_0))) 14 | (assert (= test_str1_test_str2_0 (and test_str1_0 test_str2_0))) 15 | (assert (= test_str7_0 (or test_str3_test_str4_0 test_str1_test_str2_0))) 16 | (assert (= test_str5_test_str1_0 (and test_str5_0test_str1_0))) 17 | (assert (= test_str8_0 (or test_str5_test_str1_0 test_str6_0))) -------------------------------------------------------------------------------- /smt/testdata/swaps/swaps.fspec: -------------------------------------------------------------------------------- 1 | spec swaps; 2 | 3 | def s1 = stock{ 4 | v: 10, 5 | }; 6 | 7 | def f1 = flow{ 8 | target: new s1, 9 | fn: func{ 10 | target.v <- 2; 11 | }, 12 | }; 13 | 14 | for 2 init { 15 | s2 = new s1; 16 | s2.v = 20; 17 | f2 = new f1; 18 | f2.target = s2; 19 | } run { 20 | f2.fn; 21 | } -------------------------------------------------------------------------------- /smt/testdata/swaps/swaps.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun swaps_s2_v_0 () Real) 3 | (declare-fun swaps_s2_v_1 () Real) 4 | (declare-fun swaps_s2_v_2 () Real) 5 | (assert (= swaps_s2_v_0 20.0)) 6 | (assert (= swaps_s2_v_1 (+ swaps_s2_v_0 2.0))) 7 | (assert (= swaps_s2_v_2 (+ swaps_s2_v_1 2.0))) -------------------------------------------------------------------------------- /smt/testdata/swaps/swaps1.fspec: -------------------------------------------------------------------------------- 1 | spec swaps; 2 | 3 | def s1 = stock{ 4 | v: 10, 5 | }; 6 | 7 | def f1 = flow{ 8 | target: new s1, 9 | fn: func{ 10 | target.v <- 2; 11 | }, 12 | }; 13 | 14 | for 2 init{ f2 = new f1; 15 | s2 = new s1; 16 | f2.target = s2; 17 | s2.v = 20; 18 | } run { 19 | f2.fn; 20 | } -------------------------------------------------------------------------------- /smt/testdata/swaps/swaps1.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun swaps_s2_v_0 () Real) 3 | (declare-fun swaps_s2_v_1 () Real) 4 | (declare-fun swaps_s2_v_2 () Real) 5 | (assert (= swaps_s2_v_0 20.0)) 6 | (assert (= swaps_s2_v_1 (+ swaps_s2_v_0 2.0))) 7 | (assert (= swaps_s2_v_2 (+ swaps_s2_v_1 2.0))) -------------------------------------------------------------------------------- /smt/testdata/swaps/swaps2.fspec: -------------------------------------------------------------------------------- 1 | spec swaps; 2 | 3 | def s1 = stock{ 4 | v: 10, 5 | }; 6 | 7 | def f1 = flow{ 8 | target: new s1, 9 | fn: func{ 10 | target.v <- 2; 11 | }, 12 | }; 13 | 14 | def f2 = flow { 15 | target: new s1, 16 | fn2: func{ 17 | target.v -> 5; 18 | }, 19 | }; 20 | 21 | for 2 init{ 22 | f1a = new f1; 23 | f2a = new f2; 24 | s2 = new s1; 25 | f2a.target = s2; 26 | f1a.target = s2; 27 | } run { 28 | f1a.fn | f2a.fn2; 29 | } -------------------------------------------------------------------------------- /smt/testdata/swaps/swaps2.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun swaps_s2_v_0 () Real) 3 | (declare-fun swaps_s2_v_1 () Real) 4 | (declare-fun swaps_s2_v_2 () Real) 5 | (declare-fun swaps_s2_v_3 () Real) 6 | (declare-fun swaps_s2_v_4 () Real) 7 | (declare-fun swaps_s2_v_5 () Real) 8 | (declare-fun swaps_s2_v_6 () Real) 9 | (declare-fun swaps_s2_v_7 () Real) 10 | (declare-fun swaps_s2_v_8 () Real) 11 | (declare-fun swaps_s2_v_9 () Real) 12 | (declare-fun swaps_s2_v_10 () Real) 13 | (assert (= swaps_s2_v_0 10.0)) 14 | (assert (= swaps_s2_v_1 (+ swaps_s2_v_0 2.0))) 15 | (assert (= swaps_s2_v_2 (- swaps_s2_v_1 5.0))) 16 | (assert (= swaps_s2_v_3 (- swaps_s2_v_0 5.0))) 17 | (assert (= swaps_s2_v_4 (+ swaps_s2_v_3 2.0))) 18 | (assert (or (= swaps_s2_v_5 swaps_s2_v_2) (= swaps_s2_v_5 swaps_s2_v_4))) 19 | (assert (= swaps_s2_v_6 (+ swaps_s2_v_5 2.0))) 20 | (assert (= swaps_s2_v_7 (- swaps_s2_v_6 5.0))) 21 | (assert (= swaps_s2_v_8 (- swaps_s2_v_5 5.0))) 22 | (assert (= swaps_s2_v_9 (+ swaps_s2_v_8 2.0))) 23 | (assert (or (= swaps_s2_v_10 swaps_s2_v_7) (= swaps_s2_v_10 swaps_s2_v_9))) -------------------------------------------------------------------------------- /smt/testdata/unknowns.fspec: -------------------------------------------------------------------------------- 1 | spec unknowns; 2 | 3 | def s = stock{ 4 | a, 5 | b: 2, 6 | c: 0, 7 | }; 8 | 9 | def f = flow{ 10 | data: new s, 11 | fn: func{ 12 | data.c <- data.a + data.b; 13 | }, 14 | }; 15 | 16 | assume s.a > 5; 17 | assert s.a <= 6; 18 | 19 | for 3 init{loop = new f;} run { 20 | loop.fn; 21 | } -------------------------------------------------------------------------------- /smt/testdata/unknowns.smt2: -------------------------------------------------------------------------------- 1 | (set-logic QF_NRA) 2 | (declare-fun unknowns_loop_data_a_0 () Real) 3 | (declare-fun unknowns_loop_data_b_0 () Real) 4 | (declare-fun unknowns_loop_data_c_0 () Real) 5 | (declare-fun unknowns_loop_data_c_1 () Real) 6 | (declare-fun unknowns_loop_data_c_2 () Real) 7 | (declare-fun unknowns_loop_data_c_3 () Real) 8 | (assert (= unknowns_loop_data_b_0 2.0)) 9 | (assert (= unknowns_loop_data_c_0 0.0)) 10 | (assert (= unknowns_loop_data_c_1 (+ unknowns_loop_data_c_0 (+ unknowns_loop_data_a_0 unknowns_loop_data_b_0)))) 11 | (assert (= unknowns_loop_data_c_2 (+ unknowns_loop_data_c_1 (+ unknowns_loop_data_a_0 unknowns_loop_data_b_0)))) 12 | (assert (= unknowns_loop_data_c_3 (+ unknowns_loop_data_c_2 (+ unknowns_loop_data_a_0 unknowns_loop_data_b_0)))) 13 | (assert (> unknowns_loop_data_a_0 6)) 14 | (assert (> unknowns_loop_data_a_0 5)) 15 | 16 | 17 | -------------------------------------------------------------------------------- /swaps/swaps.go: -------------------------------------------------------------------------------- 1 | package swaps 2 | 3 | import ( 4 | "fault/ast" 5 | "fault/types" 6 | "fmt" 7 | 8 | "github.com/barkimedes/go-deepcopy" 9 | ) 10 | 11 | type Precompiler struct { 12 | checker *types.Checker 13 | Alias map[string]string 14 | } 15 | 16 | func NewPrecompiler(check *types.Checker) *Precompiler { 17 | return &Precompiler{ 18 | checker: check, 19 | Alias: make(map[string]string), 20 | } 21 | } 22 | 23 | func (c *Precompiler) Swap(n *ast.Spec) *ast.Spec { 24 | s := c.walk(n) 25 | return s.(*ast.Spec) 26 | } 27 | 28 | func (c *Precompiler) walk(n ast.Node) ast.Node { 29 | var err error 30 | switch node := n.(type) { 31 | case *ast.StructInstance: 32 | node, err = c.swapValues(node) 33 | if err != nil { 34 | panic(err) 35 | } 36 | return node 37 | case *ast.Spec: 38 | var st []ast.Statement 39 | for _, v := range node.Statements { 40 | snode := c.walk(v) 41 | st = append(st, snode.(ast.Statement)) 42 | } 43 | node.Statements = st 44 | return node 45 | case *ast.SpecDeclStatement: 46 | return node 47 | case *ast.SysDeclStatement: 48 | return node 49 | case *ast.ImportStatement: 50 | snode := c.walk(node.Tree) 51 | node.Tree = snode.(*ast.Spec) 52 | return node 53 | case *ast.ConstantStatement: 54 | return node 55 | case *ast.Identifier: 56 | return node 57 | case *ast.DefStatement: 58 | return node 59 | case *ast.StockLiteral: 60 | return node 61 | case *ast.FlowLiteral: 62 | return node 63 | case *ast.ComponentLiteral: 64 | return node 65 | case *ast.AssertionStatement: 66 | return node 67 | case *ast.ForStatement: 68 | var st []ast.Statement 69 | for _, v := range node.Inits.Statements { 70 | snode := c.walk(v) 71 | st = append(st, snode.(ast.Statement)) 72 | } 73 | node.Inits.Statements = st 74 | return node 75 | case *ast.StartStatement: 76 | return node 77 | case *ast.FunctionLiteral: 78 | return node 79 | case *ast.BlockStatement: 80 | if node == nil { 81 | return node 82 | } 83 | for i := 0; i < len(node.Statements); i++ { 84 | if e, ok := node.Statements[i].(*ast.ExpressionStatement); ok { 85 | snode := c.walk(e.Expression) 86 | node.Statements[i].(*ast.ExpressionStatement).Expression = snode.(ast.Expression) 87 | } 88 | } 89 | return node 90 | case *ast.BuiltIn: 91 | return node 92 | case *ast.IntegerLiteral: 93 | return node 94 | case *ast.FloatLiteral: 95 | return node 96 | case *ast.Boolean: 97 | return node 98 | case *ast.StringLiteral: 99 | return node 100 | case *ast.ParameterCall: 101 | return node 102 | case *ast.ExpressionStatement: 103 | snode := c.walk(node.Expression) 104 | node.Expression = snode.(ast.Expression) 105 | return node 106 | case *ast.Natural: 107 | return node 108 | case *ast.Uncertain: 109 | return node 110 | case *ast.Unknown: 111 | return node 112 | case *ast.PrefixExpression: 113 | return node 114 | case *ast.InfixExpression: 115 | return node 116 | case *ast.This: 117 | return node 118 | case *ast.Clock: 119 | return node 120 | case *ast.Nil: 121 | return node 122 | case *ast.ParallelFunctions: 123 | return node 124 | case *ast.InitExpression: 125 | return node 126 | case *ast.IfExpression: 127 | if node == nil { 128 | return node 129 | } 130 | //Not sure to allow this 131 | con := c.walk(node.Consequence) 132 | alt := c.walk(node.Alternative) 133 | elif := c.walk(node.Elif) 134 | node.Consequence = con.(*ast.BlockStatement) 135 | node.Alternative = alt.(*ast.BlockStatement) 136 | node.Elif = elif.(*ast.IfExpression) 137 | return node 138 | case *ast.IndexExpression: 139 | return node 140 | case *ast.InvariantClause: 141 | return node 142 | default: 143 | panic(fmt.Errorf("unimplemented: %s type %T", node, node)) 144 | } 145 | } 146 | 147 | func (c *Precompiler) swapValues(base *ast.StructInstance) (*ast.StructInstance, error) { 148 | for _, s := range base.Swaps { 149 | infix := s.(*ast.InfixExpression) 150 | rawid := infix.Left.(ast.Nameable).RawId() 151 | key := rawid[len(rawid)-1] 152 | val, err := c.checker.Reference(infix.Right) 153 | if err != nil { 154 | return base, err 155 | } 156 | 157 | // Because part of what we're doing here is renaming 158 | // these nodes. We need to do a deep copy to separate 159 | // the swapped nodes from their original reference values 160 | copyVal, err := deepcopy.Anything(val) 161 | if err != nil { 162 | return base, err 163 | } 164 | 165 | val = copyVal.(ast.Node) 166 | 167 | switch v := val.(type) { 168 | case *ast.ParameterCall, *ast.Identifier: 169 | c.Alias[infix.Left.(ast.Nameable).IdString()] = infix.Right.(ast.Nameable).IdString() 170 | case *ast.StructInstance: 171 | for k, v2 := range v.Properties { 172 | aliasKey := fmt.Sprintf("%s_%s", infix.Left.(ast.Nameable).IdString(), k) 173 | c.Alias[aliasKey] = v2.IdString() 174 | } 175 | 176 | v.Name = key 177 | val = v 178 | } 179 | 180 | if len(val.(ast.Nameable).RawId()) == 0 { 181 | val.(ast.Nameable).SetId(rawid) 182 | } 183 | 184 | base.Properties[key].Value = val 185 | base = c.swapDeepNames(base) 186 | 187 | } 188 | return base, nil 189 | } 190 | 191 | func (c *Precompiler) swapDeepNames(val *ast.StructInstance) *ast.StructInstance { 192 | rawid := val.RawId() 193 | err := c.checker.SpecStructs[rawid[0]].Update(rawid, ast.ExtractBranches(val.Properties)) 194 | if err != nil { 195 | panic(fmt.Sprintf("failed to update spec record on swap %s: %s", val.String(), err)) 196 | } 197 | 198 | node, err := c.checker.Preprocesser.Partial(rawid[0], val) 199 | if err != nil { 200 | panic(fmt.Sprintf("failed to update process ids on swap %s: %s", val.String(), err)) 201 | } 202 | return node.(*ast.StructInstance) 203 | } 204 | -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ospath "path/filepath" 7 | "sort" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var OP_NEGATE = map[string]string{ 13 | "==": "!=", 14 | ">=": "<", 15 | ">": "<=", 16 | "<=": ">", 17 | "!=": "==", 18 | "<": ">=", 19 | "&&": "||", 20 | "||": "&&", 21 | "then": "then", 22 | //"=": "!=", 23 | } 24 | 25 | type StringSet struct { 26 | base map[string]bool 27 | vals []string 28 | } 29 | 30 | func NewStrSet() *StringSet { 31 | return &StringSet{ 32 | base: make(map[string]bool), 33 | } 34 | } 35 | 36 | func (s *StringSet) Add(str string) { 37 | if !s.In(str) { 38 | s.base[str] = true 39 | s.vals = append(s.vals, str) 40 | } 41 | } 42 | 43 | func (s *StringSet) Merge(strs []string) { 44 | for _, str := range strs { 45 | s.Add(str) 46 | } 47 | } 48 | 49 | func (s *StringSet) In(str string) bool { 50 | return s.base[str] 51 | } 52 | 53 | func (s *StringSet) Len() int { 54 | return len(s.base) 55 | } 56 | 57 | func (s *StringSet) Values() []string { 58 | return s.vals 59 | } 60 | 61 | func DiffStrSets(s1 *StringSet, s2 *StringSet) *StringSet { 62 | s3 := NewStrSet() 63 | for k := range s1.base { 64 | if !s2.In(k) { 65 | s3.Add(k) 66 | } 67 | } 68 | 69 | for k := range s2.base { 70 | if !s1.In(k) { 71 | s3.Add(k) 72 | } 73 | } 74 | return s3 75 | } 76 | 77 | func Filepath(filepath string) string { 78 | if host, ok := os.LookupEnv("FAULT_HOST"); ok { 79 | if strings.Contains(filepath, "~") { 80 | return home(host, filepath) 81 | } 82 | for strings.Contains(filepath, "..") { 83 | idx := strings.Index(filepath, "..") 84 | if idx == 0 { 85 | host = uplevel(host, true) 86 | filepath = filepath[3:] 87 | continue 88 | } 89 | 90 | left := filepath[:idx] 91 | right := filepath[idx+2:] 92 | path := uplevel(left, false) 93 | 94 | if path == "" { 95 | filepath = right 96 | } else { 97 | filepath = ospath.Join(path, right) 98 | } 99 | 100 | } 101 | 102 | if len(filepath) < len(host) || host != filepath[0:len(host)] { 103 | filepath = ospath.Join(host, filepath) 104 | } 105 | 106 | dup := fmt.Sprintf("%s%s", string(ospath.Separator), string(ospath.Separator)) 107 | if strings.Contains(filepath, dup) { 108 | path := strings.Split(filepath, dup) 109 | filepath = ospath.Join(path...) 110 | } 111 | 112 | } 113 | return filepath 114 | } 115 | 116 | func home(host string, filepath string) string { 117 | path := strings.Split(filepath, "~") 118 | if string(path[1][0]) == string(ospath.Separator) { 119 | filepath = path[1][1:] 120 | } else { 121 | filepath = path[1] 122 | } 123 | return ospath.Join(host, filepath) 124 | } 125 | 126 | func uplevel(path string, host bool) string { 127 | parts := strings.Split(path, string(ospath.Separator)) 128 | parts = trimSlashes(parts, host) 129 | 130 | if len(parts) > 0 { 131 | return ospath.Join(parts[0 : len(parts)-1]...) 132 | } 133 | return "" 134 | } 135 | 136 | func trimSlashes(parts []string, host bool) []string { 137 | if len(parts) == 0 { 138 | return parts 139 | } 140 | 141 | if parts[0] == "" && !host { //Leading slashes 142 | parts = parts[1:] 143 | return trimSlashes(parts, host) 144 | } 145 | 146 | if parts[len(parts)-1] == "" { //Trailing slashes 147 | parts = parts[0 : len(parts)-1] 148 | return trimSlashes(parts, host) 149 | } 150 | 151 | return parts 152 | } 153 | 154 | func FormatIdent(id string) string { 155 | //Removes LLVM IR specific leading characters 156 | if string(id[0]) == "@" { 157 | return id[1:] 158 | } else if string(id[0]) == "%" { 159 | return id[1:] 160 | } 161 | return id 162 | } 163 | 164 | func Cartesian(list1 []string, list2 []string) [][]string { 165 | var product [][]string 166 | for _, a := range list1 { 167 | for _, b := range list2 { 168 | product = append(product, []string{a, b}) 169 | } 170 | } 171 | return product 172 | } 173 | 174 | func CartesianMulti(listOfLists [][]string) [][]string { 175 | start := Cartesian(listOfLists[0], listOfLists[1]) 176 | for i := 2; i < len(listOfLists); i++ { 177 | start = product(start, listOfLists[i]) 178 | } 179 | return start 180 | } 181 | 182 | func MergeStringMaps(m1 map[string]string, m2 map[string]string) map[string]string { 183 | for k, v := range m2 { 184 | m1[k] = v 185 | } 186 | return m1 187 | } 188 | 189 | func MergeStrSlices(sl1 []string, sl2 []string) []string { 190 | var results []string 191 | skip := false 192 | results = append(results, sl1...) 193 | for _, v2 := range sl2 { 194 | for _, v1 := range sl1 { 195 | if v2 == v1 { 196 | skip = true 197 | break 198 | } 199 | } 200 | if !skip { 201 | results = append(results, v2) 202 | } else { 203 | skip = false 204 | } 205 | } 206 | return results 207 | } 208 | 209 | func InStringSlice(sl []string, sub string) bool { 210 | for _, s := range sl { 211 | if s == sub { 212 | return true 213 | } 214 | } 215 | return false 216 | } 217 | 218 | func StableSortKeys(keys []string) []string { 219 | sort.SliceStable(keys, func(i, j int) bool { 220 | return keys[i] < keys[j] 221 | }) 222 | return keys 223 | } 224 | 225 | func CaptureState(id string) (string, bool, bool) { 226 | var a, c bool 227 | raw := strings.Split(id, "_") 228 | if len(raw) > 2 { //Not a constant 229 | c = false 230 | a = true 231 | } else { 232 | c = true 233 | a = false 234 | } 235 | 236 | _, err := strconv.Atoi(raw[len(raw)-1]) 237 | if err != nil { 238 | return "", a, c 239 | } else { 240 | return raw[len(raw)-1], false, false 241 | } 242 | 243 | } 244 | 245 | func Copy(callstack []string) []string { 246 | var ret []string 247 | for _, v := range callstack { 248 | ret = append(ret, v) 249 | } 250 | return ret 251 | } 252 | 253 | func product(list1 [][]string, list2 []string) [][]string { 254 | var results [][]string 255 | for _, l := range list1 { 256 | for _, l1 := range list2 { 257 | p := append(l, l1) 258 | results = append(results, p) 259 | } 260 | } 261 | return results 262 | } 263 | 264 | func MaxInt16(nums []int16) int16 { 265 | var temp int16 266 | for _, i := range nums { 267 | if temp < i { 268 | temp = i 269 | } 270 | } 271 | return temp 272 | } 273 | 274 | func PairCombinations(left []string, right []string) [][]string { 275 | var ret [][]string 276 | for _, l := range left { 277 | for _, r := range right { 278 | ret = append(ret, []string{l, r}) 279 | } 280 | } 281 | return ret 282 | } 283 | 284 | func Combinations(l [][]string, n int) [][][]string { 285 | if len(l) <= n { 286 | return [][][]string{l} 287 | } 288 | 289 | var subset [][][]string 290 | for idx, itm := range l { 291 | if n == 1 { 292 | subset = append(subset, [][]string{itm}) 293 | } else if len(l) > idx+1 { 294 | i := idx //0 295 | for { 296 | pos := i + n // 1 297 | if pos > len(l) { 298 | break 299 | } 300 | items := append([][]string{itm}, l[i+1:pos]...) 301 | subset = append(subset, items) 302 | i++ 303 | } 304 | } 305 | } 306 | return subset 307 | } 308 | 309 | func NotInSet(o [][]string, c [][]string) [][]string { 310 | var s [][]string 311 | for _, r := range c { 312 | sw := true 313 | exit: 314 | for _, in := range o { 315 | if strings.Join(r, "") == strings.Join(in, "") { 316 | sw = false 317 | break exit 318 | } 319 | } 320 | if sw { 321 | s = append(s, r) 322 | } 323 | } 324 | return s 325 | } 326 | 327 | func DetectMode(filename string) string { 328 | switch ospath.Ext(filename) { 329 | case ".fspec": 330 | return "fspec" 331 | case ".fsystem": 332 | return "fsystem" 333 | default: 334 | return "" 335 | } 336 | } 337 | 338 | func GetVarBase(id string) (string, int) { 339 | v := strings.Split(id, "_") 340 | num, err := strconv.Atoi(v[len(v)-1]) 341 | if err != nil { 342 | panic(fmt.Sprintf("improperly formatted variable SSA name %s", id)) 343 | } 344 | return strings.Join(v[0:len(v)-1], "_"), num 345 | } 346 | 347 | func Intersection(s1 []string, s2 []string, init bool) []string { 348 | var s3 []string 349 | for _, s := range s1 { 350 | s3 = append(s3, s) 351 | for _, z := range s2 { 352 | if s == z { 353 | s3 = s3[0 : len(s3)-1] 354 | } 355 | } 356 | } 357 | if init { 358 | s4 := Intersection(s2, s1, false) 359 | s3 = append(s3, s4...) 360 | } 361 | return s3 362 | } 363 | 364 | func FromEnd(str string, offset int) string { 365 | if len(str) < offset { 366 | return str 367 | } 368 | return str[len(str)-offset:] 369 | } 370 | 371 | type ImportTrail []string 372 | 373 | func (i ImportTrail) BaseSpec() string { 374 | if len(i) == 0 { 375 | panic(fmt.Sprintln("import trail is empty")) 376 | } 377 | return i[0] 378 | } 379 | 380 | func (i ImportTrail) CurrentSpec() string { 381 | if len(i) == 0 { 382 | panic(fmt.Sprintln("import trail is empty")) 383 | } 384 | return i[len(i)-1] 385 | } 386 | 387 | func (i ImportTrail) PushSpec(spec string) []string { 388 | i = append(i, spec) 389 | return i 390 | } 391 | 392 | func (i ImportTrail) PopSpec() (string, []string) { 393 | if len(i) == 0 { 394 | panic(fmt.Sprintln("import trail is empty")) 395 | } 396 | spec := i[len(i)-1] 397 | i = i[0 : len(i)-1] 398 | return spec, i 399 | } 400 | -------------------------------------------------------------------------------- /visualize/visualize.go: -------------------------------------------------------------------------------- 1 | package visualize 2 | 3 | import ( 4 | "fault/ast" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | type Visual struct { 10 | tree ast.Node 11 | stateSet map[string]bool 12 | VisualState map[string][]string 13 | systemState []string 14 | } 15 | 16 | func NewVisual(tree ast.Node) *Visual { 17 | return &Visual{ 18 | tree: tree, 19 | stateSet: make(map[string]bool), 20 | VisualState: make(map[string][]string), 21 | systemState: []string{"flowchart TD"}, 22 | } 23 | } 24 | 25 | func (v *Visual) Build() { 26 | err := v.walk(v.tree) 27 | if err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | func (vis *Visual) walk(n ast.Node) error { 33 | var err error 34 | 35 | switch node := n.(type) { 36 | case *ast.Spec: 37 | for _, v := range node.Statements { 38 | err = vis.walk(v) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | return err 44 | case *ast.DefStatement: 45 | if node.TokenLiteral() != "COMPONENT" && 46 | node.TokenLiteral() != "GLOBAL" { 47 | return err 48 | } 49 | 50 | err = vis.walk(node.Value) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | return err 56 | case *ast.ComponentLiteral: 57 | for _, v := range node.Pairs { 58 | err = vis.walk(v) 59 | if err != nil { 60 | return err 61 | } 62 | } 63 | return err 64 | case *ast.ExpressionStatement: 65 | err = vis.walk(node.Expression) 66 | if err != nil { 67 | return err 68 | } 69 | return err 70 | case *ast.ForStatement: 71 | for _, v := range node.Body.Statements { 72 | err = vis.walk(v) 73 | if err != nil { 74 | return err 75 | } 76 | } 77 | return err 78 | case *ast.StartStatement: 79 | return err 80 | case *ast.FunctionLiteral: 81 | err = vis.walk(node.Body) 82 | if err != nil { 83 | return err 84 | } 85 | return err 86 | case *ast.BlockStatement: 87 | for _, v := range node.Statements { 88 | err = vis.walk(v) 89 | if err != nil { 90 | return err 91 | } 92 | } 93 | return err 94 | case *ast.InfixExpression: 95 | err = vis.walk(node.Left) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | err = vis.walk(node.Right) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | return err 106 | 107 | case *ast.IndexExpression: 108 | err = vis.walk(node.Left) 109 | if err != nil { 110 | return err 111 | } 112 | return err 113 | 114 | case *ast.PrefixExpression: 115 | err := vis.walk(node.Right) 116 | if err != nil { 117 | return err 118 | } 119 | return err 120 | 121 | case *ast.IfExpression: 122 | err = vis.walk(node.Consequence) 123 | if err != nil { 124 | return err 125 | } 126 | 127 | if node.Elif != nil { 128 | err = vis.walk(node.Elif) 129 | if err != nil { 130 | return err 131 | } 132 | } 133 | if node.Alternative != nil { 134 | err = vis.walk(node.Alternative) 135 | if err != nil { 136 | return err 137 | } 138 | } 139 | return err 140 | 141 | case *ast.StructInstance: 142 | ty := node.Type() 143 | switch ty { 144 | case "STOCK": 145 | for _, v := range node.Properties { 146 | switch inst := v.Value.(type) { 147 | case *ast.StructInstance: 148 | to, err := vis.getShape(inst) 149 | if err != nil { 150 | return err 151 | } 152 | nid := node.IdString() 153 | stock := fmt.Sprintf("\t%s[%s]-->%s", nid, nid, to) 154 | vis.systemState = append(vis.systemState, stock) 155 | } 156 | } 157 | return err 158 | case "FLOW": 159 | for _, v := range node.Properties { 160 | switch inst := v.Value.(type) { 161 | case *ast.StructInstance: 162 | to, err := vis.getShape(inst) 163 | if err != nil { 164 | return err 165 | } 166 | nid := node.IdString() 167 | flow := fmt.Sprintf("\t%s{{%s}}-->%s", nid, nid, to) 168 | vis.systemState = append(vis.systemState, flow) 169 | } 170 | } 171 | return err 172 | default: 173 | return fmt.Errorf("can't find a struct instance named %s", node.Parent) 174 | } 175 | 176 | case *ast.BuiltIn: 177 | if node.Function == "advance" { 178 | temp := node.RawId() 179 | froms := temp[1:3] 180 | tos := node.Parameters["toState"].(ast.Nameable).Id() 181 | vis.addLine(froms[0], fmt.Sprintf("\t%s --> %s", strings.Join(froms, "_"), tos[1])) 182 | } 183 | 184 | return err 185 | default: 186 | return err 187 | } 188 | } 189 | 190 | func (v *Visual) getShape(inst *ast.StructInstance) (string, error) { 191 | switch inst.Type() { 192 | case "STOCK": 193 | return fmt.Sprintf("%s[%s]", inst.IdString(), inst.IdString()), nil 194 | case "FLOW": 195 | return fmt.Sprintf("%s{{%s}}", inst.IdString(), inst.IdString()), nil 196 | default: 197 | return "", fmt.Errorf("invalid type in getShape") 198 | } 199 | } 200 | 201 | func (v *Visual) addLine(k string, s string) { 202 | if s == "}" { 203 | v.VisualState[k] = append(v.VisualState[k], s) 204 | return 205 | } 206 | 207 | if _, ok := v.stateSet[s]; !ok { 208 | v.VisualState[k] = append(v.VisualState[k], s) 209 | v.stateSet[s] = true 210 | } 211 | } 212 | 213 | func (v *Visual) Render() string { 214 | var s string 215 | if len(v.VisualState) > 0 { 216 | s = "stateDiagram" 217 | for k, v := range v.VisualState { 218 | block := strings.Join(v, "\n") 219 | s = fmt.Sprintf("%s\nstate %s {\n%s\n}", s, k, block) 220 | } 221 | } 222 | 223 | if len(v.systemState) > 1 && s != "" { 224 | sys := strings.Join(v.systemState, "\n") 225 | return strings.Join([]string{s, sys}, "\n\n") 226 | } else if len(v.systemState) > 1 { 227 | return strings.Join(v.systemState, "\n") 228 | } else { 229 | return s 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /visualize/visualize_test.go: -------------------------------------------------------------------------------- 1 | package visualize 2 | 3 | import ( 4 | "fault/ast" 5 | "fault/listener" 6 | "fault/preprocess" 7 | "fault/types" 8 | "strings" 9 | "testing" 10 | "unicode" 11 | ) 12 | 13 | func TestSpec(t *testing.T) { 14 | test := `spec test1; 15 | def foo = stock{ 16 | foosh: 3, 17 | }; 18 | 19 | def zoo = flow{ 20 | con: new foo, 21 | rate: func{ 22 | con.foosh + 2; 23 | }, 24 | }; 25 | 26 | for 2 run { 27 | bar = new zoo; 28 | }; 29 | 30 | ` 31 | flags := make(map[string]bool) 32 | flags["specType"] = true 33 | flags["testing"] = false 34 | flags["skipRun"] = false 35 | vis := prepTest(test, flags) 36 | 37 | got := vis.Render() 38 | 39 | if string(got[0]) == "\n" { 40 | t.Fatal("rough line break detected") 41 | } 42 | 43 | expected := `flowchart TD 44 | test1_bar{{test1_bar}}-->test1_bar_con[test1_bar_con]` 45 | 46 | if stripAndEscape(got) != stripAndEscape(expected) { 47 | t.Fatalf("incorrect visualization generated got=%s want=%s", got, expected) 48 | } 49 | 50 | } 51 | 52 | func TestSys(t *testing.T) { 53 | test := `system test1; 54 | component foo = states{ 55 | idle: func{ 56 | advance(this.step1); 57 | }, 58 | step1: func{ 59 | stay(); 60 | }, 61 | }; 62 | ` 63 | 64 | flags := make(map[string]bool) 65 | flags["specType"] = false 66 | flags["testing"] = true 67 | flags["skipRun"] = false 68 | vis := prepTest(test, flags) 69 | 70 | got := vis.Render() 71 | 72 | expected := `stateDiagram 73 | state foo { 74 | foo_idle --> foo_step1 75 | }` 76 | 77 | if stripAndEscape(got) != stripAndEscape(expected) { 78 | t.Fatalf("incorrect visualization generated got=%s want=%s", got, expected) 79 | } 80 | 81 | } 82 | 83 | func TestCombined(t *testing.T) { 84 | test := `system test1; 85 | import "../smt/testdata/simple.fspec"; 86 | 87 | global f = new simple.fl; 88 | 89 | component foo = states{ 90 | idle: func{ 91 | advance(this.step1); 92 | }, 93 | step1: func{ 94 | stay(); 95 | }, 96 | }; 97 | ` 98 | 99 | flags := make(map[string]bool) 100 | flags["specType"] = false 101 | flags["testing"] = false 102 | flags["skipRun"] = false 103 | vis := prepTest(test, flags) 104 | 105 | got := vis.Render() 106 | 107 | expected := `stateDiagram 108 | state foo { 109 | foo_idle --> foo_step1 110 | } 111 | 112 | flowchart TD 113 | test1_f{{test1_f}}-->test1_f_vault[test1_f_vault] 114 | ` 115 | 116 | if stripAndEscape(got) != stripAndEscape(expected) { 117 | t.Fatalf("incorrect visualization generated got=%s \nwant=%s", got, expected) 118 | } 119 | 120 | } 121 | 122 | func TestError(t *testing.T) { 123 | token := ast.Token{Type: "BAD", Position: []int{0, 0, 0, 0}} 124 | test := &ast.StructInstance{Token: token} 125 | 126 | vis := NewVisual(test) 127 | err := vis.walk(vis.tree) 128 | if err == nil { 129 | t.Fatal("visualizer did not error on bad tree") 130 | } 131 | } 132 | 133 | func stripAndEscape(str string) string { 134 | var output strings.Builder 135 | output.Grow(len(str)) 136 | for _, ch := range str { 137 | if !unicode.IsSpace(ch) { 138 | if ch == '%' { 139 | output.WriteString("%%") 140 | } else { 141 | output.WriteRune(ch) 142 | } 143 | } 144 | } 145 | return output.String() 146 | } 147 | 148 | func prepTest(test string, flags map[string]bool) *Visual { 149 | var path string 150 | l := listener.Execute(test, path, flags) 151 | pre := preprocess.Execute(l) 152 | ty := types.Execute(pre.Processed, pre) 153 | vis := NewVisual(ty.Checked) 154 | vis.Build() 155 | 156 | return vis 157 | } 158 | --------------------------------------------------------------------------------